A design compiled for rendering per tick (spec/14 §12, D-66).
Visualize.Chart.compile/2 is the only constructor. The frame's drawing order is a list
of regions — the grid, each mark, each axis, the legend, the labels, or a facet's
panels — and each is static, drawn once here in :css mode and byte for byte the same
on every tick, or dynamic, drawn per tick by its closure over the frame realised for the
tick's sources: a mark is always dynamic, a facet's panels are, and anything else is
dynamic exactly when it reads a scale whose domain moves — inferred (:auto) or
windowed by a viewport. regions/1 and dynamic_regions/1 report the split and why.
The render target of each mark is decided once, at compilation, from the rows it draws
over the binding (targets/1): :svg at or below the ceiling, the binary path above
it. render/2 is the hybrid chart map of Visualize.Backend.Hybrid with dynamic_svg
added for the SVG layer's moving part and backdrop for the layer beneath the canvas;
static/1 is the static SVG string and backdrop/1 the static backdrop; tick/3 is
the per-tick payload, and for a design with a viewport it slides the canvas layer
through Visualize.Incremental, whose state the struct carries. svg/2 is the
one-string form, the frame's own group, equal to Visualize.Chart.Frame.render/2 when
every mark targets :svg.
A :force step is warm here (spec/14 §12.5, D-132): the struct carries each graph's
last layout, every node's position and velocity by id, and tick/3 and step/3 move
the graph on from it — reheated to the step's alpha for at most its ticks
iterations — where Visualize.Chart.render/2 lays it out cold every time. The marks of
one graph share that one run, and carry/2 carries the layout across a recompilation.
Examples
iex> design = %{
...> version: 2,
...> sources: %{primary: %{fields: [:t, :v]}},
...> frames: %{main: %{
...> kind: :cartesian,
...> scales: %{x: %{kind: :linear, domain: [0, 10]}, y: %{kind: :linear}},
...> axes: [%{scale: :x, side: :bottom}, %{scale: :y, side: :left, grid: true}]
...> }},
...> marks: [%{type: :line, data: :primary, channels: %{x: :t, y: :v}}]
...> }
iex> {:ok, %{main: compiled}} = Visualize.Chart.compile(design, sources: %{primary: %{t: [1, 2], v: [3, 9]}})
iex> Visualize.Chart.Compiled.dynamic_regions(compiled)
[{:grid, {:inferred, [:y]}}, {{:mark, 0}, :data}, {{:axis, 1}, {:inferred, [:y]}}]
iex> Visualize.Chart.Compiled.targets(compiled)
[{:svg, 2}]
Summary
Types
The warm force layouts of spec/14 §12.5 (D-132): per graph, each node's position and velocity by id.
The frame's drawing order: regions, and a centred group of regions in a polar frame.
The per-tick payload of spec/14 §12.4.
Why a region is dynamic (spec/14 §12.2).
A region of spec/14 §12.2: the grid, a mark, an axis, the legend, the labels, a facet's panels.
A render target of spec/14 §12.3.
Functions
The static backdrop (spec/14 §12.3, §12.4, #476, D-120): a static :tiles mark's
images as one SVG root of the shape of static/1, rendered once at compilation, or
"" when there are none. A page stacks it beneath the canvas, so a dense mark drawn
there sits over the basemap; the attribution is in static/1, over the canvas.
A freshly compiled chart carrying the easing of the one it replaces (spec/14 §12.5, #434).
The dynamic regions alone, {region, reason} (spec/14 §12.2).
Every region in the frame's drawing order, {region, :static | {:dynamic, reason}}.
The hybrid chart map over the sources by slot name (spec/14 §12.4): width, height,
margin, static, dynamic, dynamic_style, canvas_format, dynamic_svg and
backdrop, the layer beneath the canvas (a basemap's images, #476).
The static SVG string, rendered once at compilation (spec/14 §12.4).
One frame of the one-string form with the state after it (spec/14 §12.5, #360): the
frame's group of svg/2 as a Visualize.IR.Element, its eased scales at their shown
domains for this tick, and the compiled chart carrying the easing on. :now and :at
as tick/3 takes them. A caller that wants a page-ready document wraps it in
Visualize.IR.Element.root/3.
The one-string form (spec/14 §12.4): the frame's group with the static and the dynamic
regions in the frame's own order, as SVG; equal to Visualize.Chart.Frame.render/2
when every mark targets :svg.
{target, points} per mark in design order (spec/14 §12.3).
As tick/3 with [] options.
The per-tick payload and the compiled chart holding the tick's state (spec/14 §12.4).
Types
@type forces() :: %{ required(term()) => %{ required(term()) => %{x: number(), y: number(), vx: number(), vy: number()} } }
The warm force layouts of spec/14 §12.5 (D-132): per graph, each node's position and velocity by id.
The frame's drawing order: regions, and a centred group of regions in a polar frame.
@type payload() :: %{ svg: String.t(), backdrop: String.t(), canvas: String.t(), canvas_format: :binary_base64 | :json, incremental: map() | nil, bytes: non_neg_integer() }
The per-tick payload of spec/14 §12.4.
@type reason() :: :data | :facet | {:inferred, [atom()]}
Why a region is dynamic (spec/14 §12.2).
@type region() :: :grid | {:grid, :outer} | {:mark, non_neg_integer()} | {:axis, non_neg_integer()} | :legend | :labels | :panels
A region of spec/14 §12.2: the grid, a mark, an axis, the legend, the labels, a facet's panels.
@type t() :: %Visualize.Chart.Compiled{ backdrop: String.t(), box: %{x: number(), y: number(), width: number(), height: number()}, ceiling: pos_integer(), chart: Visualize.Chart.t(), defs: [Visualize.IR.Element.t()], eased: %{required(atom()) => map()}, forces: forces(), layout: layout(), margin: %{top: number(), right: number(), bottom: number(), left: number()}, motions: %{required(non_neg_integer()) => map()}, moving: %{required(non_neg_integer()) => map()}, plot: %{width: number(), height: number()}, regions: [map()], size: term(), static: String.t(), targets: [{target(), non_neg_integer()}], theme: Visualize.Theme.t(), transitions: %{required(atom()) => map()}, viewport: map() | nil }
@type target() :: :svg | :canvas | :binary
A render target of spec/14 §12.3.
Functions
The static backdrop (spec/14 §12.3, §12.4, #476, D-120): a static :tiles mark's
images as one SVG root of the shape of static/1, rendered once at compilation, or
"" when there are none. A page stacks it beneath the canvas, so a dense mark drawn
there sits over the basemap; the attribution is in static/1, over the canvas.
A freshly compiled chart carrying the easing of the one it replaces (spec/14 §12.5, #434).
A compiled chart's plot is part of its state, so a chart drawn at a new size is a new
compilation — and a scale that was mid-transition when the size changed should go on
from where it stood rather than jump back to its target. The eased scale states and the
mark motions of previous are kept for the scales and marks fresh still has, and so is
its warm force layout for each graph fresh still has (D-132): a caller that compiles
again because a tick's marks differ keeps the graph where it stood. Anything else is
dropped, since a state for a scale the design no longer eases, or for a graph it no
longer draws, would never be read again.
iex> design = %{version: 2, sources: %{s: %{fields: [:t, :v]}},
...> frames: %{main: %{kind: :cartesian, scales: %{
...> x: %{kind: :linear, domain: [0, 10]},
...> y: %{kind: :linear, transition: %{duration: 300}}}}},
...> marks: [%{type: :line, data: :s, channels: %{x: :t, y: :v}}]}
iex> rows = %{s: [%{t: 1, v: 3}, %{t: 2, v: 9}]}
iex> {:ok, %{main: first}} = Visualize.Chart.compile(design, sources: rows)
iex> {_group, first} = Visualize.Chart.Compiled.step(first, rows, at: 0)
iex> {:ok, %{main: wider}} = Visualize.Chart.compile(design, sources: rows, size: {900, 400})
iex> Visualize.Chart.Compiled.carry(first, wider).eased == first.eased
true
The dynamic regions alone, {region, reason} (spec/14 §12.2).
Every region in the frame's drawing order, {region, :static | {:dynamic, reason}}.
The hybrid chart map over the sources by slot name (spec/14 §12.4): width, height,
margin, static, dynamic, dynamic_style, canvas_format, dynamic_svg and
backdrop, the layer beneath the canvas (a basemap's images, #476).
Raises ArgumentError as Visualize.Chart.Frame.new/2 does for a value a scale
cannot take.
The static SVG string, rendered once at compilation (spec/14 §12.4).
@spec step(t(), map(), keyword()) :: {Visualize.IR.Element.t(), t()}
One frame of the one-string form with the state after it (spec/14 §12.5, #360): the
frame's group of svg/2 as a Visualize.IR.Element, its eased scales at their shown
domains for this tick, and the compiled chart carrying the easing on. :now and :at
as tick/3 takes them. A caller that wants a page-ready document wraps it in
Visualize.IR.Element.root/3.
The one-string form (spec/14 §12.4): the frame's group with the static and the dynamic
regions in the frame's own order, as SVG; equal to Visualize.Chart.Frame.render/2
when every mark targets :svg.
@spec targets(t()) :: [{target(), non_neg_integer()}]
{target, points} per mark in design order (spec/14 §12.3).
As tick/3 with [] options.
The per-tick payload and the compiled chart holding the tick's state (spec/14 §12.4).
svg is the SVG layer's dynamic regions as one SVG root of the shape of static/1
("" when there are none); backdrop the dynamic regions' backdrop — a fitted
basemap's images, for the page to stack beneath the canvas (#476) — in the same shape
("" when there are none); canvas the canvas layer through
Visualize.Backend.Hybrid.render_dynamic/1 ("" when there is none, or when the
layer streams incrementally); incremental the canvas_incremental payload of
Visualize.Incremental for a design with a viewport and a binary canvas layer
(spec/14 §12.5), else nil; bytes the byte size of what the tick transmits.
Options
:now- the window's upper end for a viewport design; by default the newest value the marks bind to the viewport scale:at- the clock in milliseconds a scale's transition eases by (spec/14 §12.5); by defaultSystem.monotonic_time(:millisecond):frame- the tick's own frame node (spec/14 §12.5, #424): when it differs from the one the chart was compiled with, the frame is re-realised at it and the static regions re-rendered, which is what a design animating through its frame needs