Documentation
¶
Overview ¶
Package layout decides where the plot area, titles and guides go.
Compute lays out a single Cartesian panel: axes on the left and bottom, an optional title above, and a column of guides — a legend, colourbars — to the right. Panels lays out a grid of them with their axes aligned, which is what subplots and faceting are made of.
Everything is sized by measuring the real text with the real backend, so an axis whose labels are wide gets a wide margin and nothing is ever clipped by a guessed constant.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Chart ¶
type Chart struct {
// Canvas is the full drawing surface.
Canvas ir.Rect
// Theme supplies sizes, paddings and fonts.
Theme theme.Theme
// Title is the chart title, or "" for none.
Title string
// XTitle and YTitle are axis titles, or "" for none.
XTitle, YTitle string
// XLabels and YLabels are the tick label texts. Only their measured
// extents matter here; their positions are the scales' business.
XLabels, YLabels []string
// Guides are the keys beside the plot, in the order they will be stacked:
// a legend, colourbars, size keys. An empty slice reserves no column.
Guides []Guide
}
Chart is everything layout needs to know about what will be drawn.
type Grid ¶
type Grid struct {
// Canvas is the full drawing surface.
Canvas ir.Rect
// Theme supplies sizes, paddings and fonts.
Theme theme.Theme
// Title is the chart title above the whole grid, or "" for none.
Title string
// XTitle and YTitle label the shared axes, once for the grid.
XTitle, YTitle string
// Y2Title labels the secondary vertical axis, down the grid's right-hand
// side, and X2Title the secondary horizontal one, along its top. Both are
// "" for a grid with one axis in that direction, which is what leaves the
// arithmetic below exactly what it was.
Y2Title string
X2Title string
// Rows and Cols are the shape of the grid.
Rows, Cols int
// Panels are the panels, in any order. A cell with no panel is a hole.
Panels []Panel
// ColWidths fixes the width of a column in device units, or leaves it to
// the solver when the entry is zero or absent. It is RowHeights turned a
// quarter turn: what a left or right track — a band beside the panel, on
// the panel's own Y — is made of.
ColWidths []float32
// RowHeights fixes the height of a row in device units, or leaves it to
// the solver when the entry is zero or absent. It is what a track — a band
// at a panel's edge, on the panel's own X — is made of: a row whose height
// is given rather than derived.
//
// The flexible rows share what the fixed ones leave, equally, so the
// panels that are panels stay the same size as each other. This is the
// narrow widening of ADR 0010 its own "revisit if" clause asks for, not a
// general size-per-panel solver: a row is fixed or it is not, and nothing
// here can make two flexible rows differ.
RowHeights []float32
// Guides are the keys beside the grid as a whole, in stacking order.
Guides []Guide
}
Grid is a set of Cartesian panels laid out together with their axes aligned.
Alignment is the whole point and it is what makes this a constraint problem rather than a loop. Every panel in a column gets the same horizontal extent and every panel in a row the same vertical one, so a value at the same position means the same thing wherever the reader's eye lands. That is not achievable panel by panel: the width of the widest Y tick label in a column decides where every panel in that column starts, and the panels' common size then falls out of what is left.
type GridResult ¶
type GridResult struct {
// Areas are the panel rectangles, parallel to Grid.Panels.
Areas []ir.Rect
// Strips are the label bands above each panel, parallel to Grid.Panels
// and empty for a panel with no strip.
Strips []ir.Rect
// RightStrips are the label bands beside each panel, parallel to
// Grid.Panels and empty for a panel with none.
RightStrips []ir.Rect
// Region is the rectangle the panels and their gutters occupy together.
// The chart title is centred on it and the guides sit beside it.
Region ir.Rect
// Title, XTitle and YTitle are the baseline anchors for the grid's own
// titles, zero when there is none. YTitle is drawn rotated a quarter turn
// anticlockwise.
Title, XTitle, YTitle ir.Point
// Y2Title is the anchor for the secondary vertical axis's title, down the
// right of the grid. It is drawn rotated a quarter turn *clockwise*, so
// that it reads from the outside of the chart the way the left one does.
Y2Title ir.Point
// X2Title is the anchor for the secondary horizontal axis's title, along
// the top of the grid. It is drawn upright, as the bottom one is.
X2Title ir.Point
// Guides are the guide boxes, one per entry in Grid.Guides and in the same
// order, as in [Result].
Guides []ir.Rect
// TickLabelPad is copied from the theme so the renderer does not re-derive
// it.
TickLabelPad float32
}
GridResult is where everything goes, in device space.
func Panels ¶
func Panels(g Grid, m Measurer) GridResult
Panels lays out a grid.
The order of decisions matters and is the reason this is not four independent calculations: the guides' height depends on how tall the panel region is, the panel region's width depends on how wide the guides are, and both depend on the tick labels — which are known before any of it, because a scale can name its ticks from its domain alone.
type Guide ¶
type Guide struct {
// Kind decides how the guide is sized.
Kind GuideKind
// Title names the quantity the guide encodes, or "" for none. A legend has
// none: its entries name themselves.
Title string
// Labels are the texts written in the guide — one per legend entry, one per
// colourbar tick, one per size sample.
Labels []string
// Sizes are the mark diameters a [GuideSize] key shows, parallel to Labels
// and empty for every other kind. A size key's rows are as tall as their
// marks, which is the one thing about it that is not text.
Sizes []float32
}
Guide is what layout needs to know about one entry of the guide column.
Only extents matter here. Where a colourbar's ticks fall along its bar is the colour scale's business and needs the bar's rectangle to answer; what a legend swatch looks like is the theme's. Layout measures the text and reserves the room.
type GuideKind ¶
type GuideKind uint8
GuideKind is what one guide in the column beside the plot shows.
The kinds are here rather than in three fields because they are one column with one stacking rule, and because a fourth kind must not mean a fourth field on every struct between here and github.com/timzifer/figure/render. That was the state before the size key: a legend was a []string, a colourbar a []Colorbar, and adding a third would have meant a third of each and a third branch in the placement.
type Labels ¶
type Labels struct {
// contains filtered or unexported fields
}
Labels places a panel's participating labels in drawing order. Its retained buffer is reused between frames; it stores geometry, never strings or rows. Reset makes it independent of whatever panel last borrowed it from the pool.
type Measurer ¶
type Measurer interface {
Measure(run ir.TextRun) ir.TextMetrics
}
Measurer is the text-measurement capability layout needs. Every backend provides it; taking the narrow interface rather than the whole backend keeps layout unable to draw anything by accident.
type Panel ¶
type Panel struct {
// Row and Col place the panel.
Row, Col int
// Strip is the label written in a band above the panel, or "" for none.
// It is what a facet is named by.
Strip string
// RightStrip is a label written in a band down the panel's right side,
// reading top to bottom. A two-way facet names its rows this way.
RightStrip string
// XLabels and YLabels are the tick labels this panel writes. A panel that
// shares an axis with the panel beside it leaves them empty and takes the
// space anyway, so that the panels stay the same size.
XLabels, YLabels []string
// Y2Labels are the tick labels of the panel's secondary vertical axis,
// written down its right-hand side, and X2Labels those of its secondary
// horizontal one, written along its top. Each sizes a gutter of its own —
// per column and per row, exactly as YLabels and XLabels do on the other
// two sides. A panel with none takes none, so a grid without a second
// axis is laid out as it always was.
Y2Labels []string
X2Labels []string
}
Panel is one Cartesian area within a grid.
type Result ¶
type Result struct {
// Plot is the data area. Scales map into it.
Plot ir.Rect
// Title is the baseline anchor for the chart title, horizontally centred
// on the plot area. Zero if there is no title.
Title ir.Point
// XTitle is the baseline anchor for the X axis title, centred on the plot
// area. Zero if there is none.
XTitle ir.Point
// YTitle is the anchor for the Y axis title, centred on the plot area and
// meant to be drawn rotated a quarter turn anticlockwise. Zero if there is
// none.
YTitle ir.Point
// Guides are the rectangles reserved for the guide column, one per entry in
// Chart.Guides and in the same order. Each covers the whole guide: a
// colourbar's bar, its tick labels and its title.
Guides []ir.Rect
// TickLabelPad is the distance from the axis to the near edge of a tick
// label, copied from the theme so the renderer does not re-derive it.
TickLabelPad float32
}
Result is where everything goes, in device space.