Visualize.Chart.Frame (Visualize v0.2.25)

Copy Markdown View Source

The realised frame of a chart: its scales, built once, and the static furniture — grid, axes, legend and labels — rendered from them (spec/14 §4, D-60).

new/2 takes a Visualize.Chart and the sources its marks are bound to, realises every :scale node through the existing scale modules — a domain: :auto inferred from the whole column of every mark channel that names the scale, a range: :auto from the plot area by the scale's name — and caches the structs on the frame. scales/1 reads them, the escape hatch for annotations, brush conversion and custom marks; put_scale/3 replaces one. generate/2 is the furniture as one Visualize.IR.Element group with the margins applied and every theme slot resolved through Visualize.Theme.resolve/3; render/2 is the same as a string. Given sources:, both also draw every mark of the design between the grid and the axes through Visualize.Chart.Mark.generate/3 (spec/14 §5.6); without them a frame renders the furniture alone, over its unit domains, which is what the static split of a compiled chart needs (spec/14 §12).

Examples

iex> chart =
...>   Visualize.Chart.from_map!(%{
...>     version: 2,
...>     sources: %{primary: %{fields: [:t, :v]}},
...>     frames: %{main: %{
...>       kind: :cartesian,
...>       scales: %{x: %{kind: :linear}, y: %{kind: :linear, domain: [0, :auto]}},
...>       axes: [%{scale: :x, side: :bottom}, %{scale: :y, side: :left, grid: true}]
...>     }},
...>     marks: [%{type: :line, data: :primary, channels: %{x: :t, y: :v}}]
...>   })
iex> frame = Visualize.Chart.Frame.new(chart, sources: %{primary: %{t: [1, 2, 3], v: [4, 9, 2]}}, size: {300, 200})
iex> Visualize.Chart.Frame.scale(frame, :x).domain
[1, 3]
iex> Visualize.Chart.Frame.scale(frame, :y).domain
[0, 9]
iex> frame.plot
%{width: 240, height: 150}

Summary

Types

A frame kind of spec/14 §4.1.

t()

Functions

The size a frame is realised at when no size: is given (spec/14 §4.2).

As generate/2 with resolve: :css.

The frame as one Visualize.IR.Element group (spec/14 §4.7): the static furniture, and every mark of the design between the grid and the axes when sources are given.

The frame of a chart, every scale realised and the theme resolved (spec/14 §4.7).

The projection of a :geo frame (spec/14 §4.6), nil for any other kind.

The frame with the realised scale replaced: the override of spec/14 §4.3.

As render/2 with [] options.

One realised scale; ArgumentError when the frame declares no such scale.

The realised scales by name: the escape hatch (spec/14 §4.3).

The attributes of a sync group for the frame it reads (spec/14 §2.9, D-116): data-vis-sync, the group, and data-vis-sync-x, d0,d1,x0,x1,width — the x scale's domain (milliseconds since the Unix epoch for a time scale), the chart pixels of its ends and the chart's width — which is all the hooks need to map a shared x through this frame's own scale. %{} for a frame that is no group's.

Types

kind()

@type kind() :: :cartesian | :polar | :geo | :facet

A frame kind of spec/14 §4.1.

t()

@type t() :: %Visualize.Chart.Frame{
  axes: [map()],
  background: term() | nil,
  box: [number()],
  defs: %{required(atom()) => map()},
  design_styles: %{required(atom()) => map()},
  domains: %{required(atom()) => term()},
  facet: %{by: atom(), columns: pos_integer()} | nil,
  forces: map(),
  kind: kind(),
  label_paths: [non_neg_integer()],
  labels: [map()],
  legend: map() | nil,
  margin: %{top: number(), right: number(), bottom: number(), left: number()},
  mark_paths: [non_neg_integer()],
  marks: [map()],
  name: atom() | nil,
  now: term() | nil,
  panels: [term()],
  plot: %{width: number(), height: number()},
  projection: Visualize.Geo.Projection.t() | nil,
  scales: %{required(atom()) => struct()},
  size: %{width: number(), height: number()},
  styles: %{required(atom()) => map()},
  sync: String.t() | nil,
  theme: Visualize.Theme.t(),
  z: integer()
}

Functions

default_size()

@spec default_size() :: {number(), number()}

The size a frame is realised at when no size: is given (spec/14 §4.2).

generate(frame)

@spec generate(t()) :: Visualize.IR.Element.t()

As generate/2 with resolve: :css.

generate(frame, opts)

@spec generate(t(), keyword()) :: Visualize.IR.Element.t()

The frame as one Visualize.IR.Element group (spec/14 §4.7): the static furniture, and every mark of the design between the grid and the axes when sources are given.

Options

  • :resolve - how the theme's slots render: :css (default) for references a stylesheet can override, :literal for the plain values a canvas needs
  • :sources - a map from source name to anything Visualize.Data.Table.rows/1 reads, as new/2 takes; when given, the marks are drawn from them (spec/14 §5.6)
  • :paths - true for every design node's element to carry data-node with the node's path as Visualize.Chart.Validator.format_path/1 spells it — frame, marks[0], frames.main.axes[0], frames.main.legend, labels[0] — for a caller that must name what it drew, as the builder's graph does (spec/14 §4.7, D-105); false (default) leaves the markup as it is

Examples

iex> chart = Visualize.Chart.from_map!(%{version: 2, sources: %{s: %{fields: [:x, :y]}},
...>   frames: %{main: %{kind: :cartesian, scales: %{x: %{kind: :linear}, y: %{kind: :linear}}}},
...>   marks: [%{type: :line, data: :s, channels: %{x: :x, y: :y}}]})
iex> rows = [%{x: 0, y: 1}, %{x: 1, y: 2}]
iex> group = Visualize.Chart.Frame.new(chart, sources: %{s: rows}) |> Visualize.Chart.Frame.generate(sources: %{s: rows}, paths: true)
iex> {group.attrs["data-node"], Enum.map(group.children, & &1.attrs["data-node"])}
{"frames.main", ["marks[0]"]}

new(chart, opts \\ [])

@spec new(Visualize.Chart.t(), keyword()) :: t()

The frame of a chart, every scale realised and the theme resolved (spec/14 §4.7).

Options

  • :sources - a map from source name to anything Visualize.Data.Table.rows/1 reads; the columns :auto domains are inferred from (default %{})
  • :theme - a %Visualize.Theme{}; taken for any theme name the design gives, and required for one that is neither built-in nor inline
  • :size - the {width, height} the frame is realised at (default {600, 400}, spec/14 §4.2); a design records none
  • :now - the instant a :window step measures its durations back from (spec/14 §5.4.1); without it each such step takes the newest reading of its own column
  • :warm - the force layouts a compiled chart carries between ticks (spec/14 §12.5, D-132); every :force step is cold without it (default %{})

Each distinct :force step of the frame's marks is laid out once, before any domain is inferred, and held in forces: every pipeline that reaches the step reads that layout (spec/14 §5.4.3).

Raises ArgumentError for an unresolved variable, a theme name it cannot resolve, a bound value a scale cannot take, or a legend in a margin that cannot hold it (spec/14 §4.5).

projection(frame)

@spec projection(t()) :: Visualize.Geo.Projection.t() | nil

The projection of a :geo frame (spec/14 §4.6), nil for any other kind.

put_scale(frame, name, scale)

@spec put_scale(t(), atom(), struct()) :: t()

The frame with the realised scale replaced: the override of spec/14 §4.3.

render(frame)

@spec render(t()) :: String.t()

As render/2 with [] options.

render(frame, opts)

@spec render(t(), keyword()) :: String.t()

generate/2 rendered through Visualize.Render.to_string/2.

Options

scale(frame, name)

@spec scale(t(), atom()) :: struct()

One realised scale; ArgumentError when the frame declares no such scale.

scales(frame)

@spec scales(t()) :: %{required(atom()) => struct()}

The realised scales by name: the escape hatch (spec/14 §4.3).

sync_attrs(frame)

@spec sync_attrs(t()) :: %{required(String.t()) => String.t()}

The attributes of a sync group for the frame it reads (spec/14 §2.9, D-116): data-vis-sync, the group, and data-vis-sync-x, d0,d1,x0,x1,width — the x scale's domain (milliseconds since the Unix epoch for a time scale), the chart pixels of its ends and the chart's width — which is all the hooks need to map a shared x through this frame's own scale. %{} for a frame that is no group's.

iex> chart = Visualize.Chart.from_map!(%{version: 2,
...>   interaction: %{sync: "dash"},
...>   frames: %{main: %{kind: :cartesian, scales: %{x: %{kind: :linear, domain: [0, 100]}}}}})
iex> Visualize.Chart.Frame.new(chart, size: {300, 200}) |> Visualize.Chart.Frame.sync_attrs()
%{"data-vis-sync" => "dash", "data-vis-sync-x" => "0,100,40,280,300"}