Breaking: renamed the grid-colliding exports to a vl_ prefix so
attaching vellum no longer masks grid: gpar() → vl_gpar(), unit() →
vl_unit(), viewport() → vl_viewport(), arrow() → vl_arrow(), and
pattern() → vl_pattern(). The old names are removed (no aliases).
Multi-line and per-datum rich text. text_grob() labels may now contain
embedded newlines (\n), stacked baseline-to-baseline; md() gained the same
and is now vectorised — md(x) returns a single label for a length-1 x or
a list of labels for a vector, so a label grob can carry one distinct rich label
per position. Single-line, single-label output is byte-for-byte unchanged. (Rust
is untouched — shaping stays R-side.)
Accessibility (a11y). vl_scene(title=, desc=) and the new describe() setter
attach an accessible name and long description (alt text) to a scene. When set:
<svg role="img" aria-labelledby=…> and emits
<title>/<desc> (WCAG 1.1.1);Figure in the
structure tree carrying the description as Alt text.
Purely additive: a scene with no title/desc renders byte-for-byte as before. (Strict
PDF/UA-1 validation is a planned follow-up; the tag tree + Alt ship now.)datashade(weight=) now recycles a scalar and errors on a wrong-length vector,
instead of silently discarding a mismatched weight and reverting to a plain count.
native + mm / npc + mm units. A position unit combined with an
absolute unit now forms a compound unit — a data/panel anchor plus an exact
absolute offset — instead of erroring. unit(1, "native") + unit(2, "mm")
resolves to the native position shifted by exactly 2 mm at render, at any scale
or aspect (the offset is applied device-side after the base resolves). This is
the deferred "B1" route; it unlocks device-exact label nudges, halos, and
drop-shadow offsets in the grammar layer. Mixing two different position bases
(e.g. npc and native) still errors. Unit arithmetic scales the base and the
offset together. Additive change: a scene using no compound units renders
byte-for-byte as before.First release. vellum is a low-level graphics framework for R in the spirit of
grid, with a Rust backend: you describe a scene through a small declarative R
API, and the scene graph, unit/layout engine, and rendering all run in Rust.
vl_scene() and a pipeline of push(),
draw(), and pop() over an immutable tree.render() draws the same scene to PNG, SVG, or PDF, picking the backend
from the file extension — raster via tiny-skia,
PDF via krilla, SVG hand-rolled. Output
is byte-stable and snapshot-testable.display() draws a scene into the active graphics device; scene_raster() /
scene_svg() return the rendered scene in memory.unit(), grobwidth, ...) and nested viewport()s with their
own scales, rotation, and arbitrary-path clipping, plus a row/column layout
solver with "null" (flexible) tracks.viewport(alpha =)), reusable style()s, and hand-drawn sketch()
rendering.md().datashade() aggregates millions of points into a density raster in a single
pass — cost scales with output pixels, not point count — with no overplotting
and small output files.node_names() / get_node() / edit_node(),
hit_test() to pick the topmost grob under a point, and scene_model() to
serialize a per-element model (data keys, bounding boxes) — the foundation the
vellumplot grammar and the vellumwidget widget layer build on.as_vellum() / render_grid() render an existing grid grob tree —
including ggplot2 and lattice — through the vellum backend.