Documentation
¶
Overview ¶
Package spec writes a chart down as JSON and reads it back.
The document is Vega-Lite-shaped: `data.values`, `mark`, `encoding.x.field`, `scale.type`, `facet` and `resolve` mean what they mean in Vega-Lite, and a person who knows that vocabulary can read a figure spec without a manual. It is not a Vega-Lite subset, and it does not claim to be one — figure has marks Vega-Lite has no name for and Vega-Lite has transforms figure does not run. See docs/adr/0014-json-spec.md for what that choice buys and costs.
What is guaranteed is the round trip through figure:
s, err := spec.Of(chart) // chart -> document c, err := s.Chart() // document -> chart
draws the same marks in the same places. The one thing that cannot survive is a Go function: a custom tick formatter or a custom colour ramp has no JSON, and a scale carrying one says so through scale.Desc.Formatted.
Shape ¶
{
"$schema": "https://github.com/timzifer/figure/spec/v1",
"width": 800, "height": 500,
"title": "Signal",
"data": {"values": [{"t": 0, "y": 1}], "format": {"parse": {"t": "number", "y": "number"}}},
"encoding": {
"x": {"type": "quantitative", "scale": {"type": "linear", "nice": true}},
"y": {"type": "quantitative", "scale": {"type": "linear", "nice": true}}
},
"layer": [{"mark": {"type": "line"}, "encoding": {"x": {"field": "t"}, "y": {"field": "y"}}}]
}
The top-level `encoding` carries the plot's scales and axis titles, which is where a layered Vega-Lite spec puts the encodings its layers share. Each layer's own `encoding` carries the columns it reads. Data is hoisted to the top level when every layer draws from the same source and written per layer when they do not.
Index ¶
Constants ¶
const ( EdgeArc = "arc" EdgeChord = "chord" )
The coord edge policies.
const ( ThetaX = "x" ThetaY = "y" )
The angular axes.
const ( ParseNumber = "number" // ParseInteger is a column of exact integers. It is written when a // [github.com/timzifer/figure/data.Source] hands over a // data.KindInt64 column, and read back into one — so an identifier // survives the round trip with every digit it had, which "number" cannot // promise above 2^53. ParseInteger = "integer" ParseDate = "date" ParseString = "string" )
The column types Format.Parse uses.
const ( Independent = "independent" )
The scale resolutions.
const Schema = "https://github.com/timzifer/figure/spec/v1"
Schema is the value written to `$schema`. It names the dialect and the version of it; nothing fetches it, and a Vega-Lite consumer that checks the field will refuse the document, which is the honest outcome.
Stability ¶
The dialect is a v1 stability surface. Within v1 a field is only ever added, never removed, renamed or given a different meaning, and a reader ignores a field it does not know — so a document written by any v1.x reads in any other, and a document written by a v0.x figure reads too. The version in the string moves only with the major version of the module, because that is the only time the meaning of a field may change.
Reading is not gated on it: refusing a chart because of a version string would make the field a trap rather than a label. A document this package cannot understand fails on the part it cannot understand, naming it.
Before v1 the string moved with the dialect: v0.9 added the distribution marks and the size channel, v0.8 the top-level `coord`, v0.7 the series channel (`detail`), the position adjustments and the data-driven rect, and v0.6 a time scale's `origin`.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Channel ¶
type Channel struct {
Field string `json:"field,omitempty"`
Type string `json:"type,omitempty"`
// Datum is the literal value an annotation is placed at: a number, or a
// timestamp string on a temporal axis. Vega-Lite's `datum` is the same
// shape and there for the same reason.
Datum any `json:"datum,omitempty"`
Title string `json:"title,omitempty"`
Scale *Scale `json:"scale,omitempty"`
// Stack is the position adjustment applied along this channel: "zero",
// "normalize", "center", "wiggle", or "none" for groups drawn from a
// common baseline. Vega-Lite puts it here too, and spells the first three
// the same way; "wiggle" is Vega's name for the streamgraph offset and
// "none" is figure's spelling of Vega-Lite's `null`, which is a JSON null
// rather than an absent field and would read as "not set" here.
Stack string `json:"stack,omitempty"`
}
Channel is one encoding: a column, or a literal value, and the scale behind it.
type Chart ¶
type Chart struct {
Width, Height int
DPR float64
Theme theme.Theme
Title string
XTitle string
YTitle string
// Y2Title and X2Title label the secondary axes, and Y2 and X2 are their
// scales. All four are zero for a chart with one axis in that direction.
Y2Title string
X2Title string
X, Y scale.Scale
Y2, X2 scale.Scale
Coord coord.Coord
Layers []geom.Geom
Facet *facet.Spec
// Tracks are the bands at the plot's edges, in the order they were added.
Tracks []Track
// Legend forces the legend on or off. Nil leaves the default, which shows
// one as soon as a plot has more than one layer.
Legend *bool
}
Chart is the part of a plot that survives being written down: everything Of reads and everything Spec.Chart returns.
It exists so that this package does not import the root one — a spec is built out of the model packages, and the root package assembles a Plot from what comes back.
type Config ¶
type Config struct {
// Theme names a theme registered with [theme.Register].
Theme string `json:"theme,omitempty"`
// Legend forces the legend on or off.
Legend *bool `json:"legend,omitempty"`
// DevicePixelRatio is the backend's pixel ratio.
DevicePixelRatio float64 `json:"devicePixelRatio,omitempty"`
}
Config carries the choices that are figure's rather than the chart's.
type Coord ¶
type Coord struct {
// Type is "cartesian", "polar" or "smith".
Type string `json:"type"`
// Theta is the axis a polar coord sweeps around the circle: "x" or "y".
Theta string `json:"theta,omitempty"`
// Hole is the inner radius as a fraction of the outer one: a donut.
Hole float64 `json:"hole,omitempty"`
// Radius is how much of the panel's shorter half-side the circle fills.
Radius float64 `json:"radius,omitempty"`
// Start is where the angular scale begins, in radians clockwise from
// twelve o'clock, and Sweep how much of the circle it covers. Sweep is a
// pointer because a chart that never asked writes no field, while one that
// asked for a full turn should keep saying so.
Start float64 `json:"start,omitempty"`
Sweep *float64 `json:"sweep,omitempty"`
// Counterclockwise reverses the direction the angular scale runs in.
Counterclockwise bool `json:"counterclockwise,omitempty"`
// Admittance mirrors a Smith chart through its centre — Γ ↦ −Γ — so that
// the pair reads as a conductance and a susceptance. It is the Y chart.
Admittance bool `json:"admittance,omitempty"`
// Edge is how an edge between two marks is drawn: "arc" or "chord".
//
// An absent field is the coord's own default, which is not the same answer
// for both: an arc under a polar coord, because that is what a rose petal's
// side is, and a chord under a Smith one, because that is what a measured
// locus is. So a document that names a type and nothing else draws what the
// constructor of that type draws.
Edge string `json:"edge,omitempty"`
}
Coord is the coordinate system the chart is drawn in.
It is figure's own field, named plainly rather than smuggled through a borrowed one. Vega-Lite has no coordinate stage: it reaches a pie with an `arc` mark, and a document that wrote one would round-trip into a mark figure cannot rebuild — so a pie here is `mark: "bar"` with `coord: {"type": "polar"}`, which is exactly what it is. See docs/adr/0014-json-spec.md for why a difference is spelled out rather than disguised.
The field is absent for a Cartesian chart, which is every chart written before there was a coord to write.
type Data ¶
type Data struct {
Values []map[string]any `json:"values"`
Format *Format `json:"format,omitempty"`
}
Data is a table written inline.
Values is one object per row; a missing or null field is a NaN, which is the same thing the missing-data policy already handles. Format.Parse gives every column's type, so a table reads back as the columns it was — Vega-Lite has the same field for the same reason.
func (*Data) UnmarshalJSON ¶
UnmarshalJSON reads the inline values keeping every digit a number was written with.
It exists for one reason: encoding/json decodes a JSON number into an `any` as a float64, and an identifier past 2^53 comes back a different number. A column declared "integer" is meant to survive the round trip exactly, so the values are decoded with json.Decoder.UseNumber and the readers in this package accept a json.Number wherever they accept a float64.
type Encoding ¶
type Encoding struct {
X *Channel `json:"x,omitempty"`
Y *Channel `json:"y,omitempty"`
X2 *Channel `json:"x2,omitempty"`
Y2 *Channel `json:"y2,omitempty"`
Color *Channel `json:"color,omitempty"`
// Detail is the series column: the channel that splits a layer into groups
// without saying anything about how they look. It is Vega-Lite's own name
// for exactly that, and a layer that also colours by the column carries it
// in both places, because the two say different things — one is what makes
// the series, the other is what paints them.
Detail *Channel `json:"detail,omitempty"`
// Width is figure's: the column a bar takes its width from. Vega-Lite has
// no equivalent channel, so no name is borrowed for it.
Width *Channel `json:"width,omitempty"`
// Key is the field that identifies a row across renders, from
// [github.com/timzifer/figure/geom.KeyBy].
//
// The name is borrowed rather than coined: Vega-Lite spells this channel
// "key" and defines it for exactly this — the field that says which datum
// is which when the data behind a view is updated. A document that names
// none has rows with no identity, which is every document written before
// there was one.
Key *Channel `json:"key,omitempty"`
// From, To, ID, Parent and Value are the relational and hierarchical
// channels: the two ends of an edge, the two ends of a hierarchy's, and the
// magnitude of either. They are figure's own — Vega-Lite has no relational
// layouts and therefore no names to borrow.
//
// The two pairs are spelled apart although both are edge tables, because a
// hierarchy's edge runs from the child to its parent and a flow's from
// source to target: a document that called both "from" and "to" would read
// a treemap as a flow.
From *Channel `json:"from,omitempty"`
To *Channel `json:"to,omitempty"`
ID *Channel `json:"id,omitempty"`
Parent *Channel `json:"parent,omitempty"`
Value *Channel `json:"value,omitempty"`
// Explode is figure's too: the column each mark's break-out is read from,
// which is how one slice leaves a donut and the rest stay in it. The
// constant form is the mark's own `explode` property.
Explode *Channel `json:"explode,omitempty"`
// Text is the column a text layer reads its labels from. Vega-Lite has the
// same channel with the same name, and it is what tells a text mark with
// data apart from a note placed at literal values — the way a field tells
// a rect apart from a region.
Text *Channel `json:"text,omitempty"`
// YSecondary and XSecondary are the chart's secondary axes: the scales a
// layer whose mark names `"yAxis": "y2"` or `"xAxis": "x2"` is drawn
// against, and the titles written down the chart's right-hand side and
// along its top. They are only ever set on the top-level encoding — a
// layer has one channel per direction, and which axis it reads is the
// mark's business rather than the channel's.
//
// They are not spelled `x2` and `y2` because those names are already the
// *layer* channels for the far end of a band, and two things called y2 in
// one document is how a reader ends up with a chart that draws neither.
YSecondary *Channel `json:"ySecondary,omitempty"`
XSecondary *Channel `json:"xSecondary,omitempty"`
// Size is the column a mark takes its size from — the bubble chart's third
// dimension. Vega-Lite has the same channel with the same name; what is
// figure's is that the scale behind it is read as an *area*, which the
// scale's `type: "size"` says.
Size *Channel `json:"size,omitempty"`
// Mid is the column an error bar marks its measurement at, inside the
// interval its positional channels describe. Error and ErrorX are the
// symmetric spelling: a column of half-widths about the value on that
// axis. All three are figure's, so no Vega-Lite name is borrowed —
// Vega-Lite reaches the same picture with an `errorbar` mark and an
// aggregate transform, which figure does not have because a stat runs in
// the layer.
Mid *Channel `json:"mid,omitempty"`
Error *Channel `json:"error,omitempty"`
ErrorX *Channel `json:"errorX,omitempty"`
}
Encoding maps channels onto columns, values and scales.
type Facet ¶
type Facet struct {
Field string `json:"field,omitempty"`
Type string `json:"type,omitempty"`
Row *FacetField `json:"row,omitempty"`
Column *FacetField `json:"column,omitempty"`
}
Facet describes small multiples: a field to wrap on, or a row and a column field to cross.
type FacetField ¶
type FacetField struct {
Field string `json:"field,omitempty"`
Type string `json:"type,omitempty"`
}
FacetField is one axis of a facet grid.
type Format ¶
type Format struct {
// Parse maps a column name to "number", "integer", "date" or "string".
Parse map[string]string `json:"parse,omitempty"`
}
Format carries the column types.
type Layer ¶
type Layer struct {
// Name is the layer's legend label, when it was given one.
Name string `json:"name,omitempty"`
Mark Mark `json:"mark"`
Data *Data `json:"data,omitempty"`
Encoding *Encoding `json:"encoding,omitempty"`
}
Layer is one set of marks.
type Mark ¶
type Mark struct {
Type string `json:"type"`
Color string `json:"color,omitempty"`
Fill string `json:"fill,omitempty"`
Opacity *float64 `json:"opacity,omitempty"`
Size float32 `json:"size,omitempty"`
Shape string `json:"shape,omitempty"`
StrokeWidth float32 `json:"strokeWidth,omitempty"`
StrokeDash []float32 `json:"strokeDash,omitempty"`
Interpolate string `json:"interpolate,omitempty"`
Tension float64 `json:"tension,omitempty"`
Orient string `json:"orient,omitempty"`
Extent float64 `json:"extent,omitempty"`
Outliers *bool `json:"outliers,omitempty"`
Text string `json:"text,omitempty"`
Align string `json:"align,omitempty"`
Baseline string `json:"baseline,omitempty"`
FontSize float64 `json:"fontSize,omitempty"`
Angle float64 `json:"angle,omitempty"`
// Caps is whether an error bar carries a crossbar at each end. It is
// figure's own, it is a pointer because the default is true rather than
// false, and a document that omits it gets the caps — writing `false`
// is the point-range look.
Caps *bool `json:"caps,omitempty"`
// XAxis and YAxis name the scales this layer's values are read against:
// "x2" and "y2" for the chart's secondary axes, and empty for its primary
// ones. They are two fields rather than one because the two directions are
// independent — a layer may read the top axis and the right one — and a
// single field would have to spell a set.
//
// They are figure's own: Vega-Lite reaches a second axis by layering two
// specs with independent resolves, which is a different picture and a
// different set of scales.
XAxis string `json:"xAxis,omitempty"`
YAxis string `json:"yAxis,omitempty"`
// Elide is whether a text layer truncates a label too wide for the box its
// row spans rather than dropping it. It is figure's own: Vega-Lite has no
// equivalent, so no name is borrowed for it.
Elide bool `json:"elide,omitempty"`
// AvoidOverlap enables deterministic collision avoidance for text labels.
AvoidOverlap bool `json:"avoidOverlap,omitempty"`
// Origin is the value bars and areas grow from — Vega-Lite reaches the
// same place through a scale's `zero`, which is a different thing.
Origin float64 `json:"origin,omitempty"`
// BarWidth is the fraction of the slot a bar fills.
BarWidth *float64 `json:"barWidth,omitempty"`
// Padding is the gap a layout leaves between the shapes it places, as a
// fraction of the plot, and Thickness how much of its slot a node fills.
// Both are figure's own, and both are zero when the layer left the
// question to the mark.
Padding float64 `json:"padding,omitempty"`
Thickness float64 `json:"thickness,omitempty"`
// Missing is the NaN policy: "gap", "interpolate" or "error".
Missing string `json:"missing,omitempty"`
// Decimate is the reduction: "auto", "none", "lttb", "minmax" or
// "density".
Decimate string `json:"decimate,omitempty"`
// Budget caps how many marks survive a reduction.
Budget int `json:"budget,omitempty"`
// DensityCells is the cell size of a density raster, in device units.
DensityCells float64 `json:"densityCells,omitempty"`
// Extend reports whether an annotation widens the axis to include itself.
Extend *bool `json:"extend,omitempty"`
// Dodge places the groups of a slot side by side rather than stacking
// them, leaving this fraction of each share blank. It is a pointer because
// zero padding is a dodge with the bars touching, which is a different
// thing from no dodge at all. Vega-Lite reaches the same place with an
// `xOffset` channel and its own scale, which is more machinery than one
// number.
Dodge *float64 `json:"dodge,omitempty"`
// Order is the order the groups are stacked and listed in: "appearance",
// "value" or "inside-out".
Order string `json:"order,omitempty"`
// Explode breaks the mark out of the middle of the coord, as a fraction of
// its outer radius: a slice pulled out of a donut. It is figure's own —
// Vega-Lite has no coordinate stage and therefore no middle to move away
// from — and the per-row form is the `explode` channel.
Explode float64 `json:"explode,omitempty"`
// Closed joins a connected mark's last point back to its first, which is
// what makes a radar a contour. Vega-Lite has no equivalent, so no name is
// borrowed for it.
Closed bool `json:"closed,omitempty"`
// The distribution marks' own properties. Vega-Lite reaches most of this
// through transforms — `bin`, `density`, `loess`, `regression` — which
// figure runs inside the layer, so the numbers that configure them travel
// with the mark. See docs/adr/0014-json-spec.md on spelling a difference out
// rather than disguising it.
//
// Bins is how many bins a histogram divides its column into, and BinStart
// and BinEnd pin the interval it covers.
Bins int `json:"bins,omitempty"`
BinStart *float64 `json:"binStart,omitempty"`
BinEnd *float64 `json:"binEnd,omitempty"`
// Bandwidth is the kernel width a violin or a ridgeline estimates with, in
// the data's own units. Vega-Lite's density transform spells it the same
// way.
Bandwidth float64 `json:"bandwidth,omitempty"`
// Span is the fraction of the rows one local fit of a trend sees, and
// Method how it fits: "loess" or "linear". Vega-Lite's loess transform
// spells the first the same way.
Span float64 `json:"span,omitempty"`
Method string `json:"method,omitempty"`
// Overlap is how far a ridgeline's tallest ridge rises, in slots of its
// categorical axis.
Overlap float64 `json:"overlap,omitempty"`
// Extra is what a mark this package does not define carries as its own
// properties. They are written beside the fields above, at the same level
// of the mark object, and read back into [geom.Desc.Extra] for the builder
// the mark was registered with — see [geom.Register] and [geom.Extra]. A
// key that names one of the fields above is an error, not an override.
Extra map[string]any `json:"-"`
}
Mark is what a layer draws, and how.
Type and the properties above the line are Vega-Lite's, spelled as Vega-Lite spells them. The properties below it are figure's own: they name behaviour Vega-Lite has no equivalent for, so borrowing a Vega-Lite name for them would be the misleading option.
func (Mark) MarshalJSON ¶
MarshalJSON writes the mark object with the mark's own properties folded in.
A property this package has a field for is written from the field; one it does not — the Mark.Extra a third-party mark carries — is written beside them, at the same level, so that a document reads the same whether figure or its author defined the mark. A key that names a field this package owns is refused, because a document with two meanings for one key has no meaning.
func (*Mark) UnmarshalJSON ¶
UnmarshalJSON reads the mark object, keeping every property this package has no field for in Mark.Extra so that a registered mark can read it back.
type Resolve ¶
type Resolve struct {
Scale *ResolveScale `json:"scale,omitempty"`
}
Resolve says whether panels share their scales. It carries Vega-Lite's "independent" and "shared".
type ResolveScale ¶
ResolveScale is the per-axis resolution.
type Scale ¶
type Scale struct {
Type string `json:"type,omitempty"`
Domain []any `json:"domain,omitempty"`
Nice bool `json:"nice,omitempty"`
Zero bool `json:"zero,omitempty"`
// TickValues pins a linear axis's tick positions. Vega-Lite spells this
// `axis.values`; figure has no axis object on a channel, and `values` on a
// scale would read as a domain, so it is named for what it pins.
TickValues []float64 `json:"tickValues,omitempty"`
Base float64 `json:"base,omitempty"`
// Constant is Vega-Lite's name for a symlog's linear threshold.
Constant float64 `json:"constant,omitempty"`
Padding *float64 `json:"padding,omitempty"`
Scheme string `json:"scheme,omitempty"`
Range []string `json:"range,omitempty"`
Reverse bool `json:"reverse,omitempty"`
// Transform is how a colour scale's ramp runs across its domain: "log",
// "symlog", or absent for the linear default. Base and Constant configure
// it, the same two fields a positional log or symlog axis reads.
//
// Vega-Lite has no such field because it spells the transform as the
// scale's `type`, which figure cannot: `type` there already carries
// "sequential", "diverging" or "qualitative". A document that writes
// `"type": "log"` on a colour channel is read as a sequential scale with a
// log transform anyway, the same courtesy `"nominal"` gets — but a scale
// written back out says both words, because a diverging log ramp has no
// single one.
Transform string `json:"transform,omitempty"`
// Fallback is the palette a named colour scale colours a category it was
// not given a colour for from, spelled out because it has no registered
// name — a named one is written to `scheme` instead. Vega-Lite has no
// such field: there a `domain`/`range` pair is exhaustive and a value
// outside it is unmapped, where figure still draws the category.
Fallback []string `json:"fallback,omitempty"`
// Breaks are a threshold colour scale's class boundaries, and Classes the
// class count of a quantize or quantile one.
//
// Vega-Lite writes a threshold scale's boundaries in `domain`, which
// figure cannot: `domain` on a colour scale already carries the two ends
// of the interval the ramp runs over, and a threshold scale has both — the
// boundaries it cuts at and the ends its outermost classes reach to.
Breaks []float64 `json:"breaks,omitempty"`
Classes int `json:"classes,omitempty"`
// SizeRange is the diameters a size scale's domain maps onto, in device
// units, when the chart pinned them rather than leaving them to the theme.
// Vega-Lite writes a size scale's range as a plain `range` of two numbers;
// this is a separate field because `range` here is already the list of
// colours a colour scale carries.
SizeRange []float32 `json:"sizeRange,omitempty"`
// SizeZero is the value a size scale gives its smallest mark, when it is
// not zero. Anchoring anywhere but zero stops the drawing being a
// proportion, so it is written out rather than assumed.
SizeZero *float64 `json:"sizeZero,omitempty"`
// Format is how this axis writes its tick labels, and Locale the language
// it writes them in.
//
// A numeric scale reads Format as a number format —
// [github.com/timzifer/figure/scale.NumberFormat] gives the grammar,
// which is "#" for the number with an optional group comma, decimals,
// style letter, and any literal text around it: "€ #,.2". A time scale
// reads it as a Go reference layout, which is
// [github.com/timzifer/figure/scale.TimeLayout]. One field rather than
// two because the scale's own type already says which of the two an axis
// is, and Vega-Lite spells both of its equivalents `format` as well —
// there on the axis, which figure has no object for.
//
// Locale is a name, resolved against what the reading process registered
// through [github.com/timzifer/figure/scale.RegisterLocale]. A name
// nothing registered draws in English and is written back out unchanged,
// so a document does not lose what it asked for by passing through a
// process that cannot honour it.
Format string `json:"format,omitempty"`
Locale string `json:"locale,omitempty"`
// MinorTicks, Center, Undefined, TimeZone and Origin are figure's.
MinorTicks *bool `json:"minorTicks,omitempty"`
Center *float64 `json:"center,omitempty"`
Undefined string `json:"undefined,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
// Origin is the instant a time scale measures its domain from, written as
// a timestamp. It is figure's own — Vega-Lite has no equivalent because
// it has no float64 domain to run out of precision — and it is spelled out
// rather than dropped because it says what the numbers on that axis mean.
// See [github.com/timzifer/figure/scale.Origin].
Origin string `json:"origin,omitempty"`
}
Scale is a positional or colour scale.
type Spec ¶
type Spec struct {
Schema string `json:"$schema,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Title string `json:"title,omitempty"`
Data *Data `json:"data,omitempty"`
Encoding *Encoding `json:"encoding,omitempty"`
Coord *Coord `json:"coord,omitempty"`
Layer []Layer `json:"layer,omitempty"`
Facet *Facet `json:"facet,omitempty"`
Tracks []TrackDoc `json:"tracks,omitempty"`
Columns int `json:"columns,omitempty"`
Resolve *Resolve `json:"resolve,omitempty"`
Config *Config `json:"config,omitempty"`
}
Spec is a chart as a JSON document.
func Of ¶
Of writes a chart down.
It fails rather than guesses. A layer or a scale that cannot describe itself — a third-party one that implements geom.Describer or scale.Describer nowhere — is an error, because a spec missing a layer is a spec that draws a different chart, and finding that out at read time is worse than finding it out here.
func (Spec) Chart ¶
Chart reads a spec back.
`$schema` is not checked. It records the dialect a document was written in and is worth having in the file, but refusing to read a chart because of a version string would make the field a trap rather than a label; a document this package cannot understand fails on the part it cannot understand, naming it.
type Track ¶
type Track struct {
// Edge is "bottom", "top", "left" or "right".
Edge string
// Size is how thick the band is in device-independent pixels, and Fraction
// its thickness as a share of the canvas. Exactly one is non-zero.
Size, Fraction float32
// Scale is the band's own scale: the one it does not share with the plot.
Scale scale.Scale
// Layers are the band's marks.
Layers []geom.Geom
// Axis reports whether the band writes its tick labels, and Grid whether
// it draws grid lines.
Axis, Grid bool
}
Track is a band at an edge of the plot area, written down.
It carries one scale of its own and its own layers, and shares the chart's other scale — which is why only one is written here: a track that named a different scale for the axis it runs along would not be a track.
type TrackDoc ¶
type TrackDoc struct {
Edge string `json:"edge,omitempty"`
Size float32 `json:"size,omitempty"`
Fraction float32 `json:"fraction,omitempty"`
Scale *Channel `json:"scale,omitempty"`
Layer []Layer `json:"layer,omitempty"`
NoAxis bool `json:"noAxis,omitempty"`
Grid bool `json:"grid,omitempty"`
}
TrackDoc is one track in a document.
A track's marks are layers like any other, and its own scale is an axis channel like any other; what is particular to a track is which edge it is on and how much room it takes.