Documentation
¶
Overview ¶
Package refract turns one declarative chart specification into any output you need — SVG, PDF and a browser canvas today, raster through one more module, GPU and a native window through later ones — from the same model, with the same geometry.
The core module is pure Go and depends on nothing but the standard library. Both vector emitters are built in and need no rendering engine and no font stack, so a server that wants a chart as SVG or a report generator that wants one as PDF links nothing native and nothing young. The browser backend is built in for the same reason: a canvas 2D context is reached through syscall/js. Raster output lives in a separate module, github.com/timzifer/refract/backend/gg, which is still CGO-free.
Shape of the API ¶
Build a plot, give it scales, add layers, render it to a target:
src := refract.Float64Columns(map[string][]float64{"t": times, "y": values})
p := refract.New(
refract.Theme(theme.Dark),
refract.Size(800, 500),
refract.Title("Signal"),
)
p.X(scale.Time())
p.Y(scale.Linear(scale.Nice()))
p.Add(geom.Line(src, geom.X("t"), geom.Y("y"), geom.Color(palette.Blue)))
err := p.Render(refract.SVG("signal.svg"))
Scales cover linear, time, log, symlog and ordinal/categorical axes; geoms cover lines, scatters, bars, areas, steps, boxplots and rects. A mark's colour can come from the data through scale.Sequential or scale.Diverging and geom.ColorBy, which contributes a colourbar beside the plot, and its size through scale.Size and geom.SizeBy, which contributes a key of sample marks — the bubble chart.
Distributions ¶
geom.Histogram, geom.Violin, geom.Ridgeline, geom.Hexbin, geom.Beeswarm, geom.ECDF and geom.Trend summarise a column rather than plotting it. Each is a pure function in package stat with a determinism test, and each trains its axis on the summary: a histogram's Y axis holds counts that are nowhere in the data.
p.X(scale.Ordinal())
p.Add(geom.Violin(src, geom.X("service"), geom.Y("latency"),
geom.GroupBy("region")))
Annotations ¶
geom.HLine, geom.VLine, geom.HBand, geom.VBand, geom.Segment, geom.Region and geom.Note add the marks that are not data — a threshold, a shaded window, a label pointing at what happened. They take values rather than a data source.
Many panels ¶
Plot.Facet splits one plot into small multiples, one panel per value of a column; NewGrid puts several different plots on one canvas. Both lay their panels out with the same solver, so the axes line up either way.
p.Facet(facet.Wrap("region", facet.Columns(3)))
Interaction ¶
Plot.On registers a handler for hover, click, zoom or pan, and Plot.Live draws the chart into a surface that can be redrawn, pointed at, panned and zoomed. Each redraw repaints only what changed, and a frame identical to the last is not painted at all. In a browser, [Live.Bind] wires a DOM element to all of it; see package backend/canvas.
p.On(refract.Hover, func(ev refract.Event) {
if ev.Found {
tooltip(ev.Series(), ev.Hit.X, ev.Hit.Y)
}
})
live, err := p.Live(canvas.Element(el))
Live data ¶
data.Stream is a table a producer appends to from one goroutine while the renderer draws a frozen snapshot on another.
A chart as JSON ¶
A Plot marshals to a Vega-Lite-shaped document and reads back as the same chart — see Plot.Spec, ParseJSON and package spec.
Status ¶
Pre-alpha. Every release below v1.0.0 may contain breaking changes without a deprecation cycle. See CONCEPT.md for the design and the roadmap.
Index ¶
- Constants
- Variables
- func NewTable() *data.Table
- func WheelFactor(delta float64) float64
- type Backend
- type Event
- type EventKind
- type Grid
- type GridOption
- func GridAxisTitles(x, y string) GridOption
- func GridDPR(r float64) GridOption
- func GridDescription(title, detail string) GridOption
- func GridLegend(show bool) GridOption
- func GridMath(ts mathtext.Typesetter) GridOption
- func GridParallel(on bool) GridOption
- func GridSize(w, h int) GridOption
- func GridTheme(t themepkg.Theme) GridOption
- func GridTitle(s string) GridOption
- type Hit
- type Input
- func (i *Input) ClickSlop(px float64) *Input
- func (i *Input) DoubleClick() error
- func (i *Input) Down(x, y float64) error
- func (i *Input) Dragging() bool
- func (i *Input) Leave() error
- func (i *Input) Live() *Live
- func (i *Input) Move(x, y float64) error
- func (i *Input) Rescale(dpr float64) error
- func (i *Input) Resize(w, h int) error
- func (i *Input) Up(x, y float64) error
- func (i *Input) Wheel(x, y, delta float64) error
- type Live
- func (l *Live) Autoscale() error
- func (l *Live) Click(x, y float64) Event
- func (l *Live) Close() error
- func (l *Live) DPR() float64
- func (l *Live) Draw() error
- func (l *Live) Index() *interact.Index
- func (l *Live) Input() *Input
- func (l *Live) Leave() Event
- func (l *Live) Move(x, y float64) Event
- func (l *Live) PanBy(dx, dy float64) error
- func (l *Live) Rebuild() error
- func (l *Live) Rescale(dpr float64) error
- func (l *Live) Resize(w, h int) error
- func (l *Live) Size() (w, h int)
- func (l *Live) TrackRows(on bool) *Live
- func (l *Live) Wheel(x, y, factor float64) error
- func (l *Live) ZoomTo(r ir.Rect) error
- type Option
- func Coord(c coordpkg.Coord) Option
- func DPR(r float64) Option
- func Description(title, detail string) Option
- func Legend(show bool) Option
- func Math(ts mathtext.Typesetter) Option
- func Parallel(on bool) Option
- func Responsive(on bool) Option
- func ResponsiveFrom(w, h int) Option
- func Size(w, h int) Option
- func Theme(t themepkg.Theme) Option
- func Title(s string) Option
- func XTitle(s string) Option
- func YTitle(s string) Option
- type Plot
- func (p *Plot) Add(gs ...geom.Geom) *Plot
- func (p *Plot) DataTable(w io.Writer) error
- func (p *Plot) Describe() a11y.Summary
- func (p *Plot) Description() ir.Description
- func (p *Plot) Facet(s *facet.Spec) *Plot
- func (p *Plot) Live(t Target) (*Live, error)
- func (p *Plot) MarshalJSON() ([]byte, error)
- func (p *Plot) On(kind EventKind, h func(Event)) *Plot
- func (p *Plot) Render(t Target) (err error)
- func (p *Plot) Size() (w, h int)
- func (p *Plot) Spec() (spec.Spec, error)
- func (p *Plot) UnmarshalJSON(b []byte) error
- func (p *Plot) X(s scale.Scale) *Plot
- func (p *Plot) Y(s scale.Scale) *Plot
- type Source
- type Target
Constants ¶
const ( Hover = interact.Hover Leave = interact.Leave Click = interact.Click Zoom = interact.Zoom Pan = interact.Pan )
The event kinds. See interact.EventKind.
const DefaultClickSlop = 3
DefaultClickSlop is how far the pointer may travel between press and release and still count as a click rather than a drag, in device-independent pixels.
It is not zero because a pointer never is: a hand on a mouse moves a pixel or two during a click, and a finger on a trackpad more. Three pixels is the figure the desktop toolkits settled on, and it is well below the distance a deliberate pan covers.
Variables ¶
var ErrEmptyGrid = errors.New("refract: grid has no plots")
ErrEmptyGrid reports a render of a grid with no plots in it.
var ErrNoLayers = errors.New("refract: plot has no layers and no scales")
ErrNoLayers reports a render of a plot with nothing in it. Rendering empty axes is a legitimate thing to want, so this is only returned when there is also no scale configured — that combination is always a mistake.
Functions ¶
func NewTable ¶
NewTable returns an empty table that can mix numeric and time columns. See data.NewTable.
func WheelFactor ¶ added in v0.6.0
WheelFactor turns a raw scroll delta into a zoom factor.
The exponential keeps a fast scroll from inverting: any delta maps into (0, ∞) and never through zero, so holding the wheel down zooms smoothly rather than jumping. The rate is chosen so that one notch on a mouse — a hundred units in the browser's pixel mode — is about ten percent, which is the step a reader expects from a map.
Types ¶
type Event ¶ added in v0.5.0
Event is one thing that happened to a chart. See interact.Event.
type EventKind ¶ added in v0.5.0
EventKind is what happened. See interact.EventKind.
type Grid ¶ added in v0.3.0
type Grid struct {
// contains filtered or unexported fields
}
Grid renders several plots together in one image, with their axes aligned.
It is the other half of the multi-panel story: Plot.Facet splits one plot by a column, and a Grid puts different plots side by side. Both go through the same solver, so the panels line up either way.
g := refract.NewGrid(2, refract.GridSize(900, 600), refract.GridTitle("Fleet"))
g.Add(latency, throughput, errors, saturation)
err := g.Render(refract.SVG("fleet.svg"))
A member plot contributes its layers, its scales and its title, which becomes the label above its panel. The canvas is the grid's: its size, theme, chart title and axis titles are the ones used, and a member plot's own size, theme and axis titles are not. That is the price of one image — two panels cannot disagree about the colour of the paper they are printed on.
func NewGrid ¶ added in v0.3.0
func NewGrid(cols int, opts ...GridOption) *Grid
NewGrid creates a grid that flows plots into rows of cols panels.
type GridOption ¶ added in v0.3.0
type GridOption func(*Grid)
GridOption configures a Grid at construction.
func GridAxisTitles ¶ added in v0.3.0
func GridAxisTitles(x, y string) GridOption
GridAxisTitles labels the shared axes, once for the grid. Panels keep their own scales; these name what those scales measure.
func GridDPR ¶ added in v0.3.0
func GridDPR(r float64) GridOption
GridDPR sets the device pixel ratio. See DPR.
func GridDescription ¶ added in v0.6.0
func GridDescription(title, detail string) GridOption
GridDescription attaches an accessible description to the grid. See Description.
There is no `Grid.Describe`: a grid is several charts, and the honest description of one is written by whoever knows why they are on the same page. The grid's title is carried into the output either way, as a chart's is.
func GridLegend ¶ added in v0.3.0
func GridLegend(show bool) GridOption
GridLegend forces the legend on or off. By default it appears when any panel would have shown one.
func GridMath ¶ added in v0.6.0
func GridMath(ts mathtext.Typesetter) GridOption
GridMath typesets the notation in the grid's labels — its title, its axis titles, and the title of every panel in it. See Math and package mathtext.
A member plot's own typesetter is not used, for the same reason its theme is not: the canvas is the grid's, and two panels cannot disagree about how a label is set.
func GridParallel ¶ added in v0.4.0
func GridParallel(on bool) GridOption
GridParallel controls whether the panels are built concurrently. See Parallel; a grid is the shape that benefits most, because its panels are different charts over different data.
func GridSize ¶ added in v0.3.0
func GridSize(w, h int) GridOption
GridSize sets the output size in device-independent pixels. The default is 900x600, which is a grid's worth rather than a single chart's.
func GridTheme ¶ added in v0.3.0
func GridTheme(t themepkg.Theme) GridOption
GridTheme sets the visual tokens for the whole grid.
func GridTitle ¶ added in v0.3.0
func GridTitle(s string) GridOption
GridTitle sets the title above the grid.
type Hit ¶ added in v0.5.0
Hit is the mark under a pointer. See interact.Hit.
type Input ¶ added in v0.6.0
type Input struct {
// contains filtered or unexported fields
}
Input turns a surface's raw pointer input into chart interaction.
Live takes deliberate instructions — hover here, zoom about there by this much, pan by that. A surface reports something rawer: a button went down, the pointer moved, the wheel turned by ninety-six of whatever units this platform counts in. The translation between the two is a small state machine — is this move a hover or a drag, was that release a click or the end of a pan — and it is the same state machine on every surface.
So it lives here, once, rather than being written again in every backend. [Live.Bind] is this driving a DOM element; a native window drives it from its own event loop; a test drives it directly. A backend consumes IR and must not know what a panel or a scale is, which is why none of this is in one.
in := live.Input() // from the surface's event loop: in.Down(x, y) in.Move(x, y) // pans, because a button is down in.Up(x, y) // clicks, because the pointer barely moved in.Wheel(x, y, deltaY)
An Input is not safe for concurrent use, and neither is the Live behind it.
func (*Input) ClickSlop ¶ added in v0.6.0
ClickSlop sets how far the pointer may move between press and release and still count as a click. It returns i so the call can be chained.
func (*Input) DoubleClick ¶ added in v0.6.0
DoubleClick resets the view, releasing every zoom and pan, and redraws. It is the one control a reader looks for first.
func (*Input) Down ¶ added in v0.6.0
Down reports a button pressed at a device position. It starts a drag, which becomes a pan once the pointer has moved past the click slop.
func (*Input) Dragging ¶ added in v0.6.0
Dragging reports whether a drag is in progress — a button is held and the pointer has moved past the click slop. A surface uses it to decide what cursor to show.
func (*Input) Leave ¶ added in v0.6.0
Leave reports the pointer leaving the surface. It cancels any drag in progress and fires Leave, so that a tooltip opened on a hover closes.
func (*Input) Move ¶ added in v0.6.0
Move reports the pointer at a device position.
With no button held it hovers, which fires Hover and reports the mark under the pointer. With a button held it pans, which drags the data under the pointer and redraws — so the value the reader grabbed stays under their finger, which is the whole reason a drag pans in the direction it does.
func (*Input) Rescale ¶ added in v0.6.0
Rescale reports the surface's new device pixel ratio and redraws. See Live.Rescale.
func (*Input) Resize ¶ added in v0.6.0
Resize reports the surface's new size and redraws. See Live.Resize.
func (*Input) Up ¶ added in v0.6.0
Up reports the button released at a device position.
A release that never moved past the click slop is a click, and fires Click; one that did is the end of a pan and fires nothing, because every step of it has already fired Pan. That is what keeps a dragged chart from also selecting whatever the pointer happened to land on.
A release with no press behind it fires nothing either. A surface has more ways to lose a press than to report one — a double click consumed by Input.DoubleClick, a press that started outside the chart, a window that took the pointer away — and inventing a click for each of them would put a tooltip on screen every time a reader reset the view.
func (*Input) Wheel ¶ added in v0.6.0
Wheel zooms about a device position by a raw scroll delta, and redraws.
The delta is in the browser's pixel convention, which is the one every platform can be converted into: positive scrolls the content away from the reader and zooms out, and one notch of a mouse wheel is about a hundred. WheelFactor is the curve it goes through.
type Live ¶ added in v0.5.0
type Live struct {
// contains filtered or unexported fields
}
Live is a chart drawn into a surface that can be redrawn, pointed at, panned and zoomed.
It is the interactive half of a plot: Plot.Render draws once into a file, Live draws over and over into something that stays open — a browser canvas, a window, a test.
live, err := p.Live(canvas.Element(el)) defer live.Close() live.Draw() // from the surface's event loop live.Move(x, y) live.Wheel(x, y, dy)
What a redraw costs ¶
Each Draw records the frame, compares it with the last one and repaints only where the two differ — see ir.Damage. A backend that cannot repaint part of a frame gets the whole one, and a frame that is identical to the last is not painted at all.
The chart is built once ¶
Live resolves the plot into panels when it is created, so that a zoom lands on scales that are still there next frame. Adding a layer, changing the facet or replacing a scale afterwards needs Live.Rebuild, which starts again from the plot as it now stands — and, like any fresh start, forgets where the view was zoomed to.
A Live is not safe for concurrent use.
func (*Live) Autoscale ¶ added in v0.5.0
Autoscale releases every zoom and pan, so the axes come from the data again, and redraws. It is the "reset view" every interactive chart needs.
func (*Live) Close ¶ added in v0.5.0
Close finalises the target. The last frame drawn is what it holds.
func (*Live) Draw ¶ added in v0.5.0
Draw renders the current state of the plot.
It returns nil having painted nothing when the frame is identical to the last one, which is the common case for a pointer moving over a chart that is not being zoomed.
func (*Live) Index ¶ added in v0.5.0
Index returns the hit index of the last frame, for a caller drawing its own tooltip or crosshair.
func (*Live) Input ¶ added in v0.6.0
Input returns a driver for this chart's surface input.
Each call returns a fresh driver with no button held. A surface wants one, made once and kept for as long as the Live is open.
func (*Live) Move ¶ added in v0.5.0
Move reports the pointer at a device position and fires Hover.
The event carries the mark under the pointer, if there is one within interact.DefaultTolerance, and the panel the pointer is in, or -1 for a point in the margins.
The one move that fires something else is the move that leaves the last panel: that fires Leave instead, once, so that a tooltip opened on a hover has a matching event to close on. Moving around in the margins after that goes on firing Hover with no hit.
func (*Live) PanBy ¶ added in v0.5.0
PanBy moves the view by a device-space delta and redraws. It is what a drag does: the data follows the pointer, so dragging right shows earlier data.
func (*Live) Rebuild ¶ added in v0.5.0
Rebuild resolves the plot again, picking up layers, scales or a facet added since the Live was created. It forgets any zoom on a facet's free axes, which belong to panels that no longer exist.
func (*Live) Rescale ¶ added in v0.6.0
Rescale tells the chart its surface's device pixel ratio has changed, and redraws it.
It is what a window dragged onto a display with a different one calls. The chart is not laid out differently — a device pixel ratio is not a size, and coordinates stay in device-independent units either way — but the surface behind it wants more pixels, and a backend that can provide them is told to. A backend that cannot is left alone and the frame is redrawn as it was.
Rescaling to the ratio it already has is not an error and draws nothing.
func (*Live) Resize ¶ added in v0.6.0
Resize tells the chart its surface has changed size, and redraws it.
It is what a window's resize event and a reflowed canvas element call. The scales keep whatever they were zoomed or panned to — a reader who has dragged a view into place has not asked to leave it — and the chart is laid out again at the new size, so the margins, the tick count and the legend follow. A Responsive plot also rescales its type and stroke weights here.
The backend is told too, if it can be: a surface that implements ir.Resizer is resized in place rather than reopened, which is what keeps the frame on screen and the zoom in the scales. One that cannot is redrawn at the new logical size into the surface it has, which is the best available answer and is what a document target would do.
Resizing to the size it already has is not an error and draws nothing.
func (*Live) Size ¶ added in v0.6.0
Size reports the surface's current size in device-independent pixels.
func (*Live) TrackRows ¶ added in v0.5.0
TrackRows turns row identity on or off and returns l, so the call can be chained onto Plot.Live.
live, err := p.Live(canvas.Element(el))
live.TrackRows(true)
// ...
p.On(refract.Hover, func(ev refract.Event) {
if ev.Found && ev.Hit.Row >= 0 {
highlightTableRow(ev.Hit.Row)
}
})
It is off by default because it is not free. With it on, every layer that can report its rows records where each one landed, and the hit index keeps a position and a row number per mark on top of the marks it already keeps — memory proportional to the marks on screen, which after decimation is thousands rather than millions, but not nothing. Without it, [Hit.Row] is -1 and a hit still reports the data values under the pointer.
It takes effect on the next Live.Draw.
Not every mark has a row to report. A boxplot's box aggregates many rows, a density raster is not a mark at all, an interpolated point across a gap was never measured, and a third-party geom that does not report its rows has none to report; all of those leave [Hit.Row] at -1 rather than guessing a nearby one.
func (*Live) Wheel ¶ added in v0.5.0
Wheel zooms about a device position by factor, and redraws.
factor below 1 zooms in and above 1 zooms out: it multiplies the width of the view, so 0.8 shows four fifths of what was there. A wheel notch is usually turned into 0.9 or 1.1 by the surface.
Both axes zoom, about the pointer, so that the value under the cursor stays under the cursor. A scale that cannot be zoomed — an ordinal axis, where half a category is not a view of anything — is left alone, and a wheel over a chart with two such axes does nothing at all.
type Option ¶
type Option func(*Plot)
Option configures a Plot at construction.
func Coord ¶ added in v0.8.0
Coord sets the coordinate system: what the interval a scale maps into means.
The default is [coord.Cartesian], the identity, where the interval is a distance along an edge of the plot. [coord.Polar] wraps one axis around a circle and reads the other as a radius, which is all a pie, a donut, a radar, a rose or a gauge is — the marks are the ones that were already there:
p := refract.New(refract.Coord(coord.Donut(0.45)))
p.X(scale.Linear())
p.Y(scale.Linear())
p.Add(geom.Bar(src, geom.X("one"), geom.Y("share"), geom.GroupBy("browser")))
[coord.Pie] and [coord.Donut] are that recipe named; neither scale is niced, because a pie's ring closes on the stacked total. A slice can also name its own inner and outer radius with geom.X and geom.X2 and be broken out of the ring with geom.ExplodeBy, neither of which is a new mark.
A coord belongs to the chart rather than to a panel, so the panels of a facet all share it.
func DPR ¶
DPR sets the device pixel ratio. Backends that rasterize multiply the pixel buffer by it; coordinates stay in device-independent units either way. The default is 1.
func Description ¶ added in v0.6.0
Description attaches an accessible description to the chart.
title is the short label — an SVG's <title>, a PDF's document title, a canvas element's aria-label. detail is the long reading, which an SVG puts in a <desc> and a screen reader reads after the title. Either may be empty.
A chart with a Title already has a short label and needs no option to get one: the title is written into the output as a matter of course, because a picture with no accessible name is the one thing every accessibility guideline agrees about. This option is for saying something *other* than the title, and for the paragraph a title cannot hold.
Plot.Describe writes both from the data instead, which is what to reach for when the chart is built by a program rather than by a person.
func Legend ¶
Legend forces the legend on or off. By default a legend appears once a plot has more than one layer: one series does not need to be told apart from anything.
func Math ¶ added in v0.6.0
func Math(ts mathtext.Typesetter) Option
Math typesets the notation in the chart's labels.
It applies to every label the chart draws — the title, the axis titles, the tick labels, the legend, a facet's strip, a geom's own note — because a typesetter is installed by wrapping the backend rather than by being consulted at each place text is written.
p := refract.New(
refract.Math(mathtext.TeX()),
refract.YTitle(`flux density $F_\nu$ ($\mathrm{W\,m^{-2}\,Hz^{-1}}$)`),
)
Passing nil turns it off, which is the default: a chart with no typesetter draws its labels exactly as they were written, and pays nothing for the notation it does not have. See package mathtext.
func Parallel ¶ added in v0.4.0
Parallel controls whether a multi-panel chart builds its panels concurrently. It is on by default and produces identical output either way: each panel is recorded on its own goroutine and the recordings are replayed in panel order.
Turn it off to keep a render on one goroutine — inside a benchmark that is measuring something else, or in a process that has already committed its cores elsewhere. It has no effect on a chart with a single panel, which has nothing to overlap.
func Responsive ¶ added in v0.6.0
Responsive scales the theme with the size the chart is drawn at.
A plot is designed at one size — the one Size gave it, or the default 800x500 — and a responsive one keeps its proportions when it is drawn at another: half the width and half the height means half-size type, half-width strokes, half the margins. Without it a chart shrunk to a third of its design size keeps 12pt labels, and they eat the plot area.
It matters for a window, which the reader resizes, and for a browser canvas in a fluid layout. It does nothing at all to a chart rendered once at the size it was built with — the factor is exactly 1 — so turning it on cannot change an existing still.
The factor is the smaller of the two ratios, so that a chart stretched wide scales to what still fits its height, and it is clamped to the range a chart stays legible over. Colours do not scale; see [theme.Scaled] for what does.
func ResponsiveFrom ¶ added in v0.6.0
ResponsiveFrom is Responsive with the design size given explicitly rather than taken from Size.
It is what a still rendered at another size needs: a thumbnail of a chart designed at 800x500 is `Size(200, 125)` with `ResponsiveFrom(800, 500)`, and it comes out as the chart at a quarter of the size rather than as the chart with four times the type in it. A Live surface needs neither, because the size it was built with is already the design.
type Plot ¶
type Plot struct {
// contains filtered or unexported fields
}
Plot is a chart specification: size, theme, scales and layers.
A Plot is not safe for concurrent modification. Rendering the same Plot twice is supported and produces the same result, provided the underlying data has not changed.
func ParseJSON ¶ added in v0.5.0
ParseJSON builds a plot from a spec document.
p, err := refract.ParseJSON(b)
if err == nil {
err = p.Render(refract.SVG("chart.svg"))
}
It is the whole web workflow in two calls: a browser or a config file hands over a chart, and the same model that a Go program builds by hand draws it.
func (*Plot) DataTable ¶ added in v0.6.0
DataTable writes the chart's data to w as an HTML table — the fallback for a reader who cannot see the picture, and the honest answer to what is in it. See a11y.WriteTable.
func (*Plot) Describe ¶ added in v0.6.0
Describe reads the chart's own data and attaches a description of it, then returns what it wrote. See a11y.Describe for the shape of the summary and a11y.Chart for what it is derived from.
p.Describe()
p.Render(refract.SVG("chart.svg")) // carries <title> and <desc>
It is a method that does work rather than an option that sets a flag, because the work is a pass over every plotted column: a chart that nobody asked to describe should not pay for one on every render, and a chart that did should pay for it once rather than per frame. Call it again after the data changes.
A description written by Description is replaced by this, and calling Describe on a plot that has been given one is how a caller says the data has moved on.
func (*Plot) Description ¶ added in v0.6.0
func (p *Plot) Description() ir.Description
Description reports the description the chart currently carries: the one Description or Plot.Describe set, or the chart's title alone.
func (*Plot) Facet ¶ added in v0.3.0
Facet splits the plot into small multiples, one panel per value of a column. See facet.Wrap and facet.Grid.
p.Facet(facet.Wrap("region", facet.Columns(3)))
Passing nil turns faceting back off.
func (*Plot) Live ¶ added in v0.5.0
Live opens t and returns a chart drawn into it.
The target stays open until Live.Close, which is what makes this different from Plot.Render: a Live draws frame after frame into one surface.
It wants a surface rather than a document. The SVG and PDF emitters build a document and write it whole, so drawing many frames into one collects every frame in the same file rather than replacing what was there — use Plot.Render for those, and a Live for a canvas, a window, or anything else that is repainted. A single Draw into a document target is exactly a Render, and is a reasonable way to export what an interactive chart currently shows.
func (*Plot) MarshalJSON ¶ added in v0.5.0
MarshalJSON writes the plot as a spec document, so that a Plot can be handed straight to encoding/json.
Called directly it returns the indented form spec.Spec.Marshal produces: a chart is a thing people read and edit, and the compact form of one over a hundred rows is a single very long line. Called through json.Marshal it comes back compact, because that is what json.Marshal does to anything a Marshaler returns.
func (*Plot) On ¶ added in v0.5.0
On registers a handler for an event kind.
p.On(refract.Hover, func(ev refract.Event) {
if ev.Found {
tooltip(ev.Series(), ev.Hit.X, ev.Hit.Y)
}
})
p.On(refract.Zoom, func(ev refract.Event) { log.Println(ev.Rect) })
Handlers fire in registration order, from Live's input methods, on the goroutine that called one. Registering a handler does not by itself make a chart interactive — Plot.Live is what draws one into a surface that can report input.
func (*Plot) Render ¶
Render draws the plot into t.
It opens the target, lowers the chart into the backend it returns, flushes, and closes the target — so a file target has a complete file on disk when Render returns nil.
func (*Plot) Size ¶ added in v0.6.0
Size reports the size the plot is drawn at, in device-independent pixels. It is what Size set, or the default, and it is what a surface opening a window for this plot wants to know.
func (*Plot) Spec ¶ added in v0.5.0
Spec writes the plot down as a document that can be marshalled to JSON and read back with FromSpec.
It fails on a layer or a scale that cannot describe itself rather than writing a document that draws a different chart — see github.com/timzifer/refract/spec for what survives the trip and what cannot.
func (*Plot) UnmarshalJSON ¶ added in v0.5.0
UnmarshalJSON replaces the plot's contents with the document in b.
func (*Plot) X ¶
X sets the horizontal scale. The default is scale.Linear with nicing.
type Source ¶
Source is a columnar data source. See package data.
func Float64Columns ¶
Float64Columns builds a Source over numeric columns, borrowing the slices. See data.Float64Columns.
type Target ¶
Target is a render destination. See package ir.
func PDF ¶ added in v0.3.0
PDF returns a target writing a PDF document to the named file.
Like SVG, this is a zero-dependency path: the emitter is in backend/pdf and uses nothing but the standard library. The page is one PDF point per device-independent pixel, so a chart sized 800x500 is an 800x500pt page.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package a11y makes a chart readable by something other than an eye.
|
Package a11y makes a chart readable by something other than an eye. |
|
arrow
module
|
|
|
backend
|
|
|
canvas
Package canvas draws a chart into a browser canvas.
|
Package canvas draws a chart into a browser canvas. |
|
pdf
Package pdf renders a chart to PDF using nothing but the standard library.
|
Package pdf renders a chart to PDF using nothing but the standard library. |
|
svg
Package svg is refract's built-in, zero-dependency SVG backend.
|
Package svg is refract's built-in, zero-dependency SVG backend. |
|
gg
module
|
|
|
gg/gpu
module
|
|
|
window
module
|
|
|
Package coord maps scaled positions into device space.
|
Package coord maps scaled positions into device space. |
|
Package data is refract's data layer: columnar, batch-oriented access to a table of values.
|
Package data is refract's data layer: columnar, batch-oriented access to a table of values. |
|
examples
|
|
|
accessible
command
Command accessible renders a chart that can be read without being seen.
|
Command accessible renders a chart that can be read without being seen. |
|
bigdata
command
Command bigdata renders two charts that could not be drawn mark for mark.
|
Command bigdata renders two charts that could not be drawn mark for mark. |
|
categories
command
Command categories renders the categorical chart from the README.
|
Command categories renders the categorical chart from the README. |
|
dashboard
command
Command dashboard renders the v0.3 additions: small multiples, annotations, a colourbar, a grid of subplots, and PDF output.
|
Command dashboard renders the v0.3 additions: small multiples, annotations, a colourbar, a grid of subplots, and PDF output. |
|
distributions
command
Command distributions renders the v0.9 charts from the README.
|
Command distributions renders the v0.9 charts from the README. |
|
groups
command
Command groups renders the grouped charts from the README.
|
Command groups renders the grouped charts from the README. |
|
polar
command
Command polar renders the charts a coordinate system unlocks.
|
Command polar renders the charts a coordinate system unlocks. |
|
signal
command
Command signal renders the chart from CONCEPT.md §13.
|
Command signal renders the chart from CONCEPT.md §13. |
|
stream
command
Command stream draws a live chart over a growing series.
|
Command stream draws a live chart over a growing series. |
|
web
command
Command web draws an interactive chart in a browser.
|
Command web draws an interactive chart in a browser. |
|
Package facet splits one chart into small multiples.
|
Package facet splits one chart into small multiples. |
|
Package geom holds the visual marks a chart is made of.
|
Package geom holds the visual marks a chart is made of. |
|
Package interact turns a rendered chart into something a pointer can ask questions of.
|
Package interact turns a rendered chart into something a pointer can ask questions of. |
|
internal
|
|
|
fontmetrics
Package fontmetrics answers "how wide is this string" using nothing but the standard library.
|
Package fontmetrics answers "how wide is this string" using nothing but the standard library. |
|
irtest
Package irtest provides a recording ir.Backend for tests.
|
Package irtest provides a recording ir.Backend for tests. |
|
markers
Package markers builds the outline of a scatter marker.
|
Package markers builds the outline of a scatter marker. |
|
svgdiff
Package svgdiff compares two SVG documents as drawings rather than as bytes.
|
Package svgdiff compares two SVG documents as drawings rather than as bytes. |
|
Package ir defines refract's intermediate representation: a small, backend-agnostic scene description, plus the Backend interface every renderer implements.
|
Package ir defines refract's intermediate representation: a small, backend-agnostic scene description, plus the Backend interface every renderer implements. |
|
Package layout decides where the plot area, titles and guides go.
|
Package layout decides where the plot area, titles and guides go. |
|
Package mathtext typesets mathematical notation for chart labels.
|
Package mathtext typesets mathematical notation for chart labels. |
|
Package palette provides colours and colour sequences for charts.
|
Package palette provides colours and colour sequences for charts. |
|
Package render lowers a resolved chart into IR.
|
Package render lowers a resolved chart into IR. |
|
Package scale maps data values onto visual positions and generates the ticks that label them.
|
Package scale maps data values onto visual positions and generates the ticks that label them. |
|
Package spec writes a chart down as JSON and reads it back.
|
Package spec writes a chart down as JSON and reads it back. |
|
Package stat aggregates data before it is drawn.
|
Package stat aggregates data before it is drawn. |
|
Package theme holds the visual tokens a chart is drawn with: colours, fonts, sizes and spacings.
|
Package theme holds the visual tokens a chart is drawn with: colours, fonts, sizes and spacings. |




















