| Title: | A Low-Level Graphics Framework with a Rust Backend |
|---|---|
| Description: | A grid-like low-level graphics system for R whose scene graph, unit/layout engine, and rendering run in a Rust backend. |
| Authors: | David Schoch [aut, cre] |
| Maintainer: | David Schoch <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.2.0.9000 |
| Built: | 2026-07-12 18:40:26 UTC |
| Source: | https://github.com/r-vellum/vellum |
Wrap a grob (or list of grobs) as a mask for vl_viewport(mask = ...). The mask
content is rendered to an isolated layer; its coverage then modulates the
visibility of the viewport's contents.
as_mask(grob, type = c("alpha", "luminance"))as_mask(grob, type = c("alpha", "luminance"))
grob |
A grob, or a list of grobs, drawn in the masked viewport's coordinate system. |
type |
|
A vellum_mask object.
as_mask(circle_grob(r = 0.4, gp = vl_gpar(fill = "white", col = NA)))as_mask(circle_grob(r = 0.4, gp = vl_gpar(fill = "white", col = NA)))
as_vellum() converts a grid grob tree — or a ggplot2 plot — into a
vl_scene() by letting an offscreen grid device resolve all coordinates to
absolute units, then emitting vellum grobs. render_grid() does that and
writes the result. This is the interop path (grid-based graphics rendered by
vellum's deterministic backend); a native graphics device is future work.
as_vellum(x, width = 7, height = 7, dpi = 96, bg = "white") render_grid( x, path, width = 7, height = 7, dpi = 96, bg = "white", text = c("native", "outline") )as_vellum(x, width = 7, height = 7, dpi = 96, bg = "white") render_grid( x, path, width = 7, height = 7, dpi = 96, bg = "white", text = c("native", "outline") )
x |
A grid grob/gTree/gtable, or a ggplot object. |
width, height
|
Page size in inches. |
dpi, bg
|
As in |
path |
Output file ( |
text |
Passed to |
as_vellum(): a vellum_scene. render_grid(): path, invisibly.
## Not run: library(ggplot2) p <- ggplot(mtcars, aes(wt, mpg)) + geom_point() render_grid(p, "plot.png", width = 6, height = 4) ## End(Not run)## Not run: library(ggplot2) p <- ggplot(mtcars, aes(wt, mpg)) + geom_point() render_grid(p, "plot.png", width = 6, height = 4) ## End(Not run)
The extensible seam a higher-level package (e.g. a grammar layer) implements to
compile its own plot object into a vl_scene(). render() coerces its input
through this generic, so render(x, path) works for any x that has an
as_vellum_scene() method. An identity method for vellum_scene is provided.
as_vellum_scene(x, ...)as_vellum_scene(x, ...)
x |
An object to coerce: a |
... |
Passed on to methods. |
This is the stable compiler-backend entry point: downstream packages should
target as_vellum_scene() (and the exported grob/viewport/unit constructors)
rather than vellum's internal compile() / .scene_to_backend() helpers.
A vellum_scene.
sc <- vl_scene() identical(as_vellum_scene(sc), sc) # the identity method returns its inputsc <- vl_scene() identical(as_vellum_scene(sc), sc) # the identity method returns its input
For data beyond the point where drawing one marker each is practical
(overplotted, millions of points), the fast and overplotting-honest approach
is not to draw markers faster but to not draw markers at all: bin the points
into a canvas-sized grid in one pass, then colour each cell by its density. This
is what makes datashader fast — aggregation decouples
cost from both point count and overplotting. datashade() returns a single
raster_grob() you draw like any other grob.
datashade( x, y, weight = NULL, width = 600L, height = 400L, xlim = NULL, ylim = NULL, colors = c("#deebf7", "#08306b"), how = c("eq_hist", "log", "cbrt", "linear"), interpolate = FALSE, name = NULL, vp = NULL, id = NULL, role = NULL )datashade( x, y, weight = NULL, width = 600L, height = 400L, xlim = NULL, ylim = NULL, colors = c("#deebf7", "#08306b"), how = c("eq_hist", "log", "cbrt", "linear"), interpolate = FALSE, name = NULL, vp = NULL, id = NULL, role = NULL )
x, y
|
Point coordinates (plain numerics, in data space). |
weight |
Optional per-point weight; cells accumulate the summed weight
instead of a plain count. |
width, height
|
Aggregation grid size in cells (= output raster pixels). |
xlim, ylim
|
Data range to bin over; default the finite range of |
colors |
Two or more colours forming the low-to-high density ramp. |
how |
Density-to-colour mapping: |
interpolate |
Passed to |
name, vp, id, role
|
Passed to |
The points are binned over xlim x ylim into a width x height grid, so to
line the image up with data axes draw it inside a vl_viewport() whose xscale /
yscale match xlim / ylim (it fills the viewport, npc 0..1). For crisp
bins make width/height match the viewport's pixel size and keep
interpolate = FALSE.
A grob (a raster), drawable with draw().
set.seed(1) n <- 1e6 x <- rnorm(n); y <- x * 0.5 + rnorm(n) g <- datashade(x, y, width = 400, height = 300) s <- vl_scene(6, 4.5) |> push(vl_viewport(xscale = range(x), yscale = range(y))) |> draw(g)set.seed(1) n <- 1e6 x <- rnorm(n); y <- x * 0.5 + rnorm(n) g <- datashade(x, y, width = 400, height = 300) s <- vl_scene(6, 4.5) |> push(vl_viewport(xscale = range(x), yscale = range(y))) |> draw(g)
Attach (or replace) an accessible name (title) and long description
(desc, the alt text) on an existing scene. The SVG backend then emits
role="img" + <title>/<desc>, and the PDF backend tags the page as a
Figure with the description as Alt text — meeting WCAG 1.1.1 (text
alternative). Equivalent to passing title/desc to vl_scene().
describe(scene, title = NULL, desc = NULL)describe(scene, title = NULL, desc = NULL)
scene |
A |
title |
An accessible name (short), or |
desc |
A long description / alt text, or |
The scene, with the accessibility fields set (a new value).
vl_scene(2, 2) |> draw(points_grob(c(0.3, 0.7), 0.5, gp = vl_gpar(fill = "red"))) |> describe(title = "Two red dots", desc = "Two red points on a white field.")vl_scene(2, 2) |> draw(points_grob(c(0.3, 0.7), 0.5, gp = vl_gpar(fill = "red"))) |> describe(title = "Two red dots", desc = "Two red points on a white field.")
display() re-renders scene to fill the current graphics device and draws it
there. Interactively this is the RStudio / Positron Plots pane; inside a
knitr / Quarto chunk it becomes the chunk's figure (it draws to the chunk's
device). This is the seam any package built on vellum can call to show output
instead of writing a file: scene is coerced via as_vellum_scene(), so it
also accepts e.g. a grammar's plot spec.
display(scene, ...)display(scene, ...)
scene |
A |
... |
Unused. |
To fill the window (no letterbox margins, like ggplot2) the scene is re-rendered
at the device's size and pixel density, so its relative (npc/native/layout)
content reflows to the window and absolute (mm/in/pt) content keeps its
physical size. It draws through a grid grob that re-rasterizes on every draw, so
resizing the Plots pane re-renders the scene crisply at the new size (round
markers stay round) rather than stretching one bitmap. Use render() to write
the scene at its authored width/height. Auto-printing a scene at the console
(or calling plot() on it) displays it.
Inside a knitr / Quarto chunk the chunk's dpi option wins, so
knitr::opts_chunk$set(dpi = 200) yields a genuine 200-dpi figure even on
knitr's default dev = "png" device (which misreports its pixel density);
outside knitting the scene's authored vl_scene() dpi is honored unless the
live device reports a trustworthy higher density (e.g. a resized Plots pane).
The (coerced) scene, invisibly.
## Not run: vl_scene(4, 3) |> draw(circle_grob(r = 0.3, gp = vl_gpar(fill = "tomato", col = NA))) |> display() ## End(Not run)## Not run: vl_scene(4, 3) |> draw(circle_grob(r = 0.3, gp = vl_gpar(fill = "tomato", col = NA))) |> display() ## End(Not run)
Create a linear or radial gradient to use as a fill in vl_gpar(). A gradient
interpolates between colour stops. Its geometry (x1/y1/... or
cx/cy/r) is given in the coordinate system named by units and is
resolved against the viewport at draw time, so the gradient transforms with
the grob just like its outline.
linear_gradient( colours, stops = NULL, x1 = 0, y1 = 0, x2 = 1, y2 = 0, units = "npc", extend = "pad" ) radial_gradient( colours, stops = NULL, cx = 0.5, cy = 0.5, r = 0.5, units = "npc", extend = "pad" )linear_gradient( colours, stops = NULL, x1 = 0, y1 = 0, x2 = 1, y2 = 0, units = "npc", extend = "pad" ) radial_gradient( colours, stops = NULL, cx = 0.5, cy = 0.5, r = 0.5, units = "npc", extend = "pad" )
colours |
A vector of two or more colours (any R colour spec). With
|
stops |
Optional offsets in |
x1, y1, x2, y2
|
Start and end points of a linear gradient (default a
left-to-right sweep in |
units |
Coordinate system for the geometry: one of |
extend |
How the gradient behaves outside |
cx, cy, r
|
Centre and radius of a radial gradient (default centred,
radius |
A vellum_gradient object, suitable for vl_gpar(fill = ...).
linear_gradient(c("white", "navy")) radial_gradient(c("yellow", "red"), cx = 0.5, cy = 0.5, r = 0.5)linear_gradient(c("white", "navy")) radial_gradient(c("yellow", "red"), cx = 0.5, cy = 0.5, r = 0.5)
Grobs are immutable value objects describing something to draw. Build them
with the constructors below, add them to a scene with draw(), and render
with render(). Coordinates accept a vl_unit() vector or a bare numeric
(interpreted in the default_units, usually "npc").
rect_grob( x = 0.5, y = 0.5, width = 1, height = 1, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) roundrect_grob( x = 0.5, y = 0.5, width = 1, height = 1, r = 0.1, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) lines_grob( x, y, arrow = NULL, start_cap = NULL, end_cap = NULL, offset = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) polygon_grob( x, y, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) bezier_grob( x, y, n = 60, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) spline_grob( x, y, shape = 1, n = 20, open = TRUE, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) circle_grob( x = 0.5, y = 0.5, r = 0.25, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) points_grob( x, y, size = vl_unit(2, "mm"), shape = "circle", sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) hexagon_grob( x = 0.5, y = 0.5, size = vl_unit(2, "mm"), width = NULL, height = NULL, fill = NULL, orientation = c("flat", "pointy"), gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) sector_grob( x = 0.5, y = 0.5, r0 = 0, r1 = 0.5, theta0 = 0, theta1 = 2 * pi, fill = NULL, arrow = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) loop_grob( x = 0.5, y = 0.5, size = vl_unit(4, "mm"), foot = vl_unit(0, "mm"), angle = 0, width = 1, arrow = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) segments_grob( x0, y0, x1, y1, arrow = NULL, start_cap = NULL, end_cap = NULL, offset = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) path_grob( x, y, id = NULL, rule = c("winding", "evenodd"), sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, role = NULL ) raster_grob( image, x = 0.5, y = 0.5, width = 1, height = 1, interpolate = TRUE, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) text_grob( label, x = 0.5, y = 0.5, just = "centre", rot = 0, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL )rect_grob( x = 0.5, y = 0.5, width = 1, height = 1, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) roundrect_grob( x = 0.5, y = 0.5, width = 1, height = 1, r = 0.1, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) lines_grob( x, y, arrow = NULL, start_cap = NULL, end_cap = NULL, offset = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) polygon_grob( x, y, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) bezier_grob( x, y, n = 60, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) spline_grob( x, y, shape = 1, n = 20, open = TRUE, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) circle_grob( x = 0.5, y = 0.5, r = 0.25, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) points_grob( x, y, size = vl_unit(2, "mm"), shape = "circle", sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) hexagon_grob( x = 0.5, y = 0.5, size = vl_unit(2, "mm"), width = NULL, height = NULL, fill = NULL, orientation = c("flat", "pointy"), gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) sector_grob( x = 0.5, y = 0.5, r0 = 0, r1 = 0.5, theta0 = 0, theta1 = 2 * pi, fill = NULL, arrow = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) loop_grob( x = 0.5, y = 0.5, size = vl_unit(4, "mm"), foot = vl_unit(0, "mm"), angle = 0, width = 1, arrow = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) segments_grob( x0, y0, x1, y1, arrow = NULL, start_cap = NULL, end_cap = NULL, offset = NULL, sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL, key = NULL, meta = NULL ) path_grob( x, y, id = NULL, rule = c("winding", "evenodd"), sketch = NULL, gp = vl_gpar(), name = NULL, vp = NULL, role = NULL ) raster_grob( image, x = 0.5, y = 0.5, width = 1, height = 1, interpolate = TRUE, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL ) text_grob( label, x = 0.5, y = 0.5, just = "centre", rot = 0, gp = vl_gpar(), name = NULL, vp = NULL, id = NULL, role = NULL )
x, y
|
Coordinates ( |
width, height
|
Grob size ( |
sketch |
Optional |
gp |
Graphical parameters, from |
name |
Optional name (for |
vp |
Optional |
id |
For most grobs, an optional semantic identifier emitted by the SVG
backend as |
role |
Optional ARIA role, emitted by the SVG backend as |
key |
Optional per-element data key(s) for the batched marks
( |
meta |
Optional free-form per-element metadata for the batched marks: a
list with one entry (record) per element (recycled), e.g. tooltip text or
field values. It never reaches the backend (nothing drawn changes); it rides
on the scene and is returned by |
r |
Radius ( |
arrow |
An |
start_cap, end_cap
|
Optional absolute-length |
offset |
Optional absolute-length |
n |
Number of points to sample the curve at (flattened to a polyline). |
shape |
Marker shape(s): |
open |
If |
size |
Loop extent: an absolute |
fill |
Per-element fill colour(s), recycled to the number of sectors. |
orientation |
Hexagon orientation: |
r0, r1
|
Inner and outer radius of each sector ( |
theta0, theta1
|
Start and end angle of each sector, in radians, with 0 at 3 o'clock and increasing counter-clockwise. |
foot |
Node radius the loop's two feet attach at (an absolute
|
angle |
Outward direction of the loop in radians (which way the teardrop bulges away from the vertex, e.g. away from the layout centroid). |
x0, y0, x1, y1
|
Segment start/end coordinates ( |
rule |
Fill rule: |
image |
A raster image: a |
interpolate |
Smoothly interpolate when scaling (default |
label |
Character string(s) to draw. |
just |
Justification: |
rot |
Rotation in degrees, counter-clockwise. |
sector_grob() draws a batch of annular sectors (pie / donut / rose wedges) in a
single call. gp$fill recycles per sector; gp$col/lwd give a uniform stroke.
Passing r0 == r1 gives an open arc (stroke only). Combined with an
absolute (mm) radius at a "native" centre and an vl_arrow(), the radius is
resolved to a device length at render (like a marker size), so the arc tracks
an mm size at any page size or dpi; the arrowhead sits tangent to the outer arc's
end. (For node-link self-loops, prefer loop_grob() — a teardrop, not a
ring.)
loop_grob() draws self-loops for node-link diagrams as an igraph-style cubic
Bézier teardrop: it leaves the vertex (x, y) (a "native" anchor), bulges
out to size along angle, and returns, with an optional vl_arrow() head tangent
to the curve at the returning foot. size and foot are absolute and resolved to
device px at render, so the loop is a fixed physical size that scales with the
mm node markers — no native-per-mm estimation, exact at any figure size/dpi.
A grob object.
grobwidth(grob) and grobheight(grob) return a vl_unit() equal to the drawn
width/height of grob — handy for sizing a vl_viewport() or grid_layout()
track to its contents (e.g. a margin to an axis label). The extent is measured
eagerly to absolute millimetres at construction, so it is exact for text
and absolute-unit (mm/in/pt) grobs. A grob sized in npc/native has no
viewport-independent extent and is measured against a fixed reference, so for
those prefer npc/native directly.
grobwidth(grob, mult = 1) grobheight(grob, mult = 1)grobwidth(grob, mult = 1) grobheight(grob, mult = 1)
grob |
A grob (or composite subtree) to measure. |
mult |
A multiplier on the measured extent (default 1). |
A unit (in millimetres).
grobwidth(text_grob("A wide axis label", gp = vl_gpar(fontsize = 14))) grobheight(rect_grob(height = vl_unit(8, "mm")))grobwidth(text_grob("A wide axis label", gp = vl_gpar(fontsize = 14))) grobheight(rect_grob(height = vl_unit(8, "mm")))
Find the topmost node drawn under a point — the picking primitive the retained
scene graph enables (base grid offers only grid.locator()). The scene is
compiled into a colour pick-buffer (each grob drawn in a colour encoding its
id, respecting clipping and paint order), so the result is geometry-, clip- and
overlap-exact. Markers and text are matched by their bounding box; lines and
segments by a small pick band.
hit_test(scene, x, y, units = c("npc", "px"))hit_test(scene, x, y, units = c("npc", "px"))
scene |
A |
x, y
|
Query point, in |
units |
|
The hit node's name (character); NA_character_ if the topmost grob
there is unnamed; or NULL if nothing is drawn at the point.
md() builds a styled label from a small markdown/HTML-free subset, for use as
the label of text_grob() (and anywhere a label is measured with
grobwidth()/grobheight()). The base font/size/colour come from gp; markup
spans override per run.
md(text)md(text)
text |
A markup string (or a character vector for per-element labels). |
Supported markup:
**bold**
*italic* or _italic_
^sup^ (superscript) and ~sub~ (subscript)
[text]{#c00} — a coloured span (any R colour: name or hex)
Spans nest (e.g. **a^2^**). md() with no markup is equivalent to the plain
string. Embedded newlines (\n) start a new line (stacked baseline-to-baseline).
md() is vectorised: a length-1 input returns a single vellum_md_label; a
longer vector returns a list of them (one per element), so a vellumplot mark can
carry a per-datum rich label.
A vellum_md_label (length-1 text) or a list of them (length > 1).
lab <- md("R^2^ = **0.91**") labs <- md(c("*a*", "**b**")) # a list of two labelslab <- md("R^2^ = **0.91**") labs <- md(c("*a*", "**b**")) # a list of two labels
node_names() lists the names in a scene. get_node() returns the first node
with a given name. edit_node() returns a new scene with that node's
properties updated (copy-on-modify).
node_names(scene) get_node(scene, name) edit_node(scene, name, ...)node_names(scene) get_node(scene, name) edit_node(scene, name, ...)
scene |
A |
name |
A node name (set via the |
... |
Properties to set, e.g. |
node_names(): character. get_node(): a node. edit_node(): a
vellum_scene.
scene_model() walks a rendered scene and returns one row per drawn element of
the keyable marks (points, circles, rects, hexagons, sectors, segments),
pairing each element's grammar-supplied identity — its data key, free-form
meta, grob id/name, and enclosing panel — with its resolved device-pixel
bounding box. It is the host-agnostic bridge underlying interactivity: a host
renders the SVG (each element tagged with data-key, see scene_svg()) and
uses this table to map an event back to the originating datum.
scene_model(scene)scene_model(scene)
scene |
A |
Elements are returned in paint order. key/meta are NA/NULL for marks
drawn without them, so a plain scene still yields a geometry table.
A list with two data frames:
elements — one row per element: key, mark, id, name, panel, the
device-px bbox x0,y0,x1,y1, its centre/size x,y,w,h, and a meta
list-column.
panels — one row per named panel: name and its elements' bounding box
x0,y0,x1,y1.
scene_raster() renders scene and returns its pixels as an integer array
with dimensions c(channel, x, y) — RGBA channels in 0:255, top-left origin,
y increasing downward. This is the form most convenient for probing or
testing (e.g. scene_raster(s)[1, x, y] is the red value at pixel (x, y)).
scene_raster(scene)scene_raster(scene)
scene |
A |
An grDevices::as.raster() method returns the same image as a raster object
(a character matrix of hex colours), drawable with graphics::plot() or
grid::rasterGrob().
scene_raster(): an integer array of dimension c(4, width, height).
The as.raster() method: a raster (character matrix, c(height, width)).
s <- vl_scene(2, 1, bg = "white") |> draw(circle_grob(r = 0.3, gp = vl_gpar(fill = "red", col = NA))) dim(scene_raster(s)) # c(4, width_px, height_px)s <- vl_scene(2, 1, bg = "white") |> draw(circle_grob(r = 0.3, gp = vl_gpar(fill = "red", col = NA))) dim(scene_raster(s)) # c(4, width_px, height_px)
Like render() with an .svg path, but returns the SVG document as a
character string instead of writing a file. This is the in-memory entry point
for hosting a scene interactively (an htmlwidget embeds the markup directly)
and for tests that assert on emitted attributes such as data-key.
scene_svg(scene, text = c("native", "outline"))scene_svg(scene, text = c("native", "outline"))
scene |
A |
text |
For SVG output, how text is written: |
A length-1 character vector: the SVG document.
Attach to a grob's sketch argument to render it in a hand-drawn, sketchy
style (the Rough.js look): wobbly outlines and hachure
fills. Supported by rect_grob(), polygon_grob(), lines_grob(), and
circle_grob(). Output is deterministic given seed.
sketch( roughness = 1, bowing = 1, fill_style = c("hachure", "solid", "crosshatch", "zigzag", "dots"), fill_weight = NULL, hachure_angle = -41, hachure_gap = NULL, curve_tightness = 0, disable_multi_stroke = FALSE, preserve_vertices = FALSE, seed = 1L )sketch( roughness = 1, bowing = 1, fill_style = c("hachure", "solid", "crosshatch", "zigzag", "dots"), fill_weight = NULL, hachure_angle = -41, hachure_gap = NULL, curve_tightness = 0, disable_multi_stroke = FALSE, preserve_vertices = FALSE, seed = 1L )
roughness |
Wobble amount ( |
bowing |
How much straight edges bow (0 disables bowing). |
fill_style |
One of |
fill_weight |
Stroke width of fill/hachure lines, in |
hachure_angle |
Hachure line angle in degrees. |
hachure_gap |
Gap between hachure lines, in |
curve_tightness |
Curve fit tightness for round shapes (circles, arcs). |
disable_multi_stroke |
If |
preserve_vertices |
If |
seed |
Integer seed for the wobble (same seed => identical output). |
Sketch is a deliberate exception to vellum's crisp, fidelity-first defaults —
see vignette / _docs/DESIGN-ROUGHR.md. Text is never sketched.
A vellum_sketch object for a grob's sketch argument.
rect_grob(gp = vl_gpar(fill = "steelblue", col = "black"), sketch = sketch())rect_grob(gp = vl_gpar(fill = "steelblue", col = "black"), sketch = sketch())
A style bundles vl_gpar() graphical parameters under an optional name so the
same look can be reused across many viewports or grobs. A style is a
gpar — it carries every graphical-parameter field and obeys the same
inheritance rules — with an added name for identification. It can therefore
be passed anywhere a gp is accepted.
style( col = NULL, fill = NULL, lwd = NULL, alpha = NULL, lty = NULL, lineend = NULL, linejoin = NULL, linemitre = NULL, fontfamily = NULL, fontface = NULL, fontsize = NULL, lineheight = NULL, name = NULL )style( col = NULL, fill = NULL, lwd = NULL, alpha = NULL, lty = NULL, lineend = NULL, linejoin = NULL, linemitre = NULL, fontfamily = NULL, fontface = NULL, fontsize = NULL, lineheight = NULL, name = NULL )
col |
Stroke/text colour. |
fill |
Fill colour, or a gradient from |
lwd |
Line width (1 == 1/96 inch). |
alpha |
Opacity multiplier in |
lty |
Line type: a name ( |
lineend |
Line cap: |
linejoin |
Line join: |
linemitre |
Mitre limit (>= 1) for mitre joins; default 10. |
fontfamily |
Font family (text grobs). |
fontface |
One of |
fontsize |
Font size in points. |
lineheight |
Line-height multiple. |
name |
Optional style-class name, for identification only; it is ignored by rendering. |
Attaching a style to a viewport cascades its defaults to the whole subtree
via the ordinary gpar inheritance (more-specific overrides less-specific), so
a child grob's own gp still wins. This is the reusable "style class" layer
that sits below a grammar's themes: a theme can compile into named styles
rather than setting gpar fields ad hoc on every element.
A style object (a subclass of gpar).
accent <- style(col = "firebrick", lwd = 2, name = "accent") # Reuse it on a viewport; children inherit unless they override. vl_viewport(gp = accent)accent <- style(col = "firebrick", lwd = 2, name = "accent") # Reuse it on a viewport; children inherit unless they override. vl_viewport(gp = accent)
Describe arrowheads to draw on the ends of a lines_grob() or
segments_grob() (pass as their arrow = argument).
vl_arrow( angle = 30, length = vl_unit(0.25, "in"), ends = c("last", "first", "both"), type = c("open", "closed") )vl_arrow( angle = 30, length = vl_unit(0.25, "in"), ends = c("last", "first", "both"), type = c("open", "closed") )
angle |
Half-angle of the head at the tip, in degrees (default 30). |
length |
Head length as an absolute |
ends |
Which ends get a head: |
type |
|
A vellum_arrow object.
lines_grob(c(0.1, 0.9), c(0.1, 0.9), arrow = vl_arrow(type = "closed"))lines_grob(c(0.1, 0.9), c(0.1, 0.9), arrow = vl_arrow(type = "closed"))
vellum memoises compiled scenes keyed on an object-identity token so repeat
renders of an unchanged scene (multi-format export, a display() resize back
to a prior size, or animation replaying a fixed set of frames) are cheap. The
cache is transparent — a cached render is byte-identical to an uncached one —
and bounded (options(vellum.cache_size=), default 8), so you rarely need
this; it is provided to reclaim memory or to force a cold render in
benchmarks. Disable caching entirely with options(vellum.cache = FALSE).
vl_clear_render_cache()vl_clear_render_cache()
NULL, invisibly.
vl_clear_render_cache()vl_clear_render_cache()
Builds a set of graphical parameters attached to a grob or viewport. Any field
left NULL is inherited from the enclosing viewport; alpha multiplies down
the viewport tree. A colour value sets it; NA means "no paint".
vl_gpar( col = NULL, fill = NULL, lwd = NULL, alpha = NULL, lty = NULL, lineend = NULL, linejoin = NULL, linemitre = NULL, fontfamily = NULL, fontface = NULL, fontsize = NULL, lineheight = NULL )vl_gpar( col = NULL, fill = NULL, lwd = NULL, alpha = NULL, lty = NULL, lineend = NULL, linejoin = NULL, linemitre = NULL, fontfamily = NULL, fontface = NULL, fontsize = NULL, lineheight = NULL )
col |
Stroke/text colour. |
fill |
Fill colour, or a gradient from |
lwd |
Line width (1 == 1/96 inch). |
alpha |
Opacity multiplier in |
lty |
Line type: a name ( |
lineend |
Line cap: |
linejoin |
Line join: |
linemitre |
Mitre limit (>= 1) for mitre joins; default 10. |
fontfamily |
Font family (text grobs). |
fontface |
One of |
fontsize |
Font size in points. |
lineheight |
Line-height multiple. |
A gpar object.
vl_gpar(col = "steelblue", lwd = 2, lty = "dashed", lineend = "round")vl_gpar(col = "steelblue", lwd = 2, lty = "dashed", lineend = "round")
Create a pattern that fills a shape by tiling a grob. The grob is drawn once
into a tile occupying the unit square (0..1 npc), then repeated across a cell
of size width x height (in units) anchored at (x, y). Like gradients,
the cell geometry is resolved against the viewport at draw time.
vl_pattern( grob, width = 0.1, height = 0.1, x = 0.5, y = 0.5, units = "npc", extend = "repeat" )vl_pattern( grob, width = 0.1, height = 0.1, x = 0.5, y = 0.5, units = "npc", extend = "repeat" )
grob |
A grob, or a list of grobs, drawn into the tile (their |
width, height
|
Size of one tile cell (default |
x, y
|
Cell centre (default centred). |
units |
Coordinate system for the geometry; see |
extend |
Tiling mode: |
The tile is rendered to a raster image (sized from width/height at the
scene's resolution) and embedded: PNG raster, SVG <image> in a <pattern>.
The PDF backend has no image support yet, so a pattern degrades to the tile's
average colour there.
A vellum_pattern object, suitable for vl_gpar(fill = ...).
dots <- circle_grob(r = 0.25, gp = vl_gpar(fill = "white", col = NA)) vl_pattern(dots, width = 0.08, height = 0.08)dots <- circle_grob(r = 0.25, gp = vl_gpar(fill = "white", col = NA)) vl_pattern(dots, width = 0.08, height = 0.08)
vl_scene() creates an empty scene. push() adds a viewport (and descends
into it), draw() adds a grob, pop() ascends. The builder is a linear pipe;
a rendered or edited scene is an immutable value (edit_node() copies on
modify). render() compiles the scene and writes the output.
vl_scene( width = 6, height = 4, dpi = 96, bg = "white", gp = vl_gpar(), xscale = c(0, 1), yscale = c(0, 1), clip = FALSE, title = NULL, desc = NULL ) push(scene, vp) draw(scene, grob) pop(scene, n = 1) render(scene, path, text = c("native", "outline"), debug = FALSE)vl_scene( width = 6, height = 4, dpi = 96, bg = "white", gp = vl_gpar(), xscale = c(0, 1), yscale = c(0, 1), clip = FALSE, title = NULL, desc = NULL ) push(scene, vp) draw(scene, grob) pop(scene, n = 1) render(scene, path, text = c("native", "outline"), debug = FALSE)
width, height
|
Page size ( |
dpi |
Resolution in dots per inch. |
bg |
Background colour (or |
gp |
Page-level graphical parameters ( |
xscale, yscale
|
Native coordinate range of the root viewport, so
|
clip |
Clip drawing to the page rectangle? |
title, desc
|
Accessibility: an accessible name (a short title) and a
longer description (alt text) for the scene. When either is set, the SVG
backend emits |
scene |
A |
vp |
|
grob |
A grob (see grob). |
n |
Number of viewport levels to ascend. |
path |
Output file path; the format is taken from the extension ( |
text |
For SVG output, how text is written: |
debug |
If |
vl_scene(), push(), draw(), pop(): a vellum_scene.
render(): path, invisibly.
s <- vl_scene(width = 4, height = 3) |> push(vl_viewport(xscale = c(0, 10), yscale = c(0, 10))) |> draw(rect_grob(gp = vl_gpar(fill = "grey95", col = "grey50"))) |> draw(lines_grob(x = vl_unit(0:10, "native"), y = vl_unit(0:10, "native"), gp = vl_gpar(col = "steelblue", lwd = 2)))s <- vl_scene(width = 4, height = 3) |> push(vl_viewport(xscale = c(0, 10), yscale = c(0, 10))) |> draw(rect_grob(gp = vl_gpar(fill = "grey95", col = "grey50"))) |> draw(lines_grob(x = vl_unit(0:10, "native"), y = vl_unit(0:10, "native"), gp = vl_gpar(col = "steelblue", lwd = 2)))
vl_strwidth() / vl_strheight() return the rendered width/height of each
string, using the same shaping (textshaping/HarfBuzz + systemfonts)
the renderer uses, so measurements match drawn text. Device-independent (does
not need an open scene). Vectorised over label. (Named vl_* to avoid masking
grDevices::strwidth().)
vl_strwidth( label, family = "", fontface = "plain", fontsize = 12, cex = 1, unit = "in" ) vl_strheight( label, family = "", fontface = "plain", fontsize = 12, cex = 1, unit = "in" )vl_strwidth( label, family = "", fontface = "plain", fontsize = 12, cex = 1, unit = "in" ) vl_strheight( label, family = "", fontface = "plain", fontsize = 12, cex = 1, unit = "in" )
label |
Character vector of strings to measure. |
family |
Font family (e.g. |
fontface |
One of |
fontsize |
Font size in points. |
cex |
Multiplier applied to |
unit |
Output unit: one of |
A numeric vector (one per label) of widths/heights in unit.
vl_strwidth(c("short", "a longer label"), fontsize = 14)vl_strwidth(c("short", "a longer label"), fontsize = 14)
A unit is a vectorised (value, unit) pair used for coordinates and sizes.
Each element carries its own unit, so a single unit vector can mix
coordinate systems and a primitive can use different units on the x and y
axes (e.g. x in "native", y in "npc").
vl_unit(values, units = "npc", data = NULL) is_unit(x)vl_unit(values, units = "npc", data = NULL) is_unit(x)
values |
Numeric vector of magnitudes. |
units |
Character vector of unit names, recycled against |
data |
Optional list supplying context for derived units:
|
x |
An object. |
Supported units:
"npc" — normalised parent coordinates (0 = bottom/left, 1 = top/right)
"native" — the enclosing viewport's xscale/yscale
"mm", "cm", "in", "pt" — absolute lengths
"char", "line" — font-relative (need fontsize/lineheight via data)
"strwidth", "strheight" — size of a string (need label via data)
Font- and string-relative units are resolved to absolute millimetres at
construction (text metrics are available device-independently), so a stored
unit only ever holds one of the core backend units.
Arithmetic: + and - combine two units of the same code, or two
absolute units ("mm"/"cm"/"in"/"pt"), which resolve to "mm"
immediately (e.g. vl_unit(10, "mm") + vl_unit(1, "in") is 35.4mm). A position
unit ("npc"/"native") plus an absolute unit forms a compound unit — a
data/panel anchor plus an exact absolute offset — e.g.
vl_unit(1, "native") + vl_unit(2, "mm") is 1native+2mm: it resolves to the
native position shifted right by exactly 2 mm at render, at any scale or
aspect. Mixing two different position bases (e.g. "npc" and "native")
still errors, as it cannot be reduced to one unit. unit * scalar scales the
base value and the offset together.
A unit vector.
vl_unit(1:3, "native") vl_unit(c(0.5, 1), c("npc", "in"))vl_unit(1:3, "native") vl_unit(c(0.5, 1), c("npc", "in"))
A viewport is a rectangular region that establishes its own coordinate
systems (its xscale/yscale for "native" units), optionally rotated,
clipped, and carrying inheritable graphical parameters. Push one onto a scene
with push(). A viewport may define a row/column grid_layout(); child
viewports are then placed into cells via row/col.
vl_viewport( x = 0.5, y = 0.5, width = 1, height = 1, xscale = c(0, 1), yscale = c(0, 1), angle = 0, clip = FALSE, gp = vl_gpar(), layout = NULL, row = NULL, col = NULL, rowspan = 1, colspan = 1, mask = NULL, alpha = NULL, blend = NULL, name = NULL, cache = FALSE ) grid_layout( widths = vl_unit(1, "null"), heights = vl_unit(1, "null"), respect = FALSE )vl_viewport( x = 0.5, y = 0.5, width = 1, height = 1, xscale = c(0, 1), yscale = c(0, 1), angle = 0, clip = FALSE, gp = vl_gpar(), layout = NULL, row = NULL, col = NULL, rowspan = 1, colspan = 1, mask = NULL, alpha = NULL, blend = NULL, name = NULL, cache = FALSE ) grid_layout( widths = vl_unit(1, "null"), heights = vl_unit(1, "null"), respect = FALSE )
x, y
|
Centre of the viewport ( |
width, height
|
Size ( |
xscale, yscale
|
Length-2 native coordinate ranges. |
angle |
Rotation in degrees, counter-clockwise about the centre. |
clip |
Clip drawing to this viewport: |
gp |
Inheritable graphical parameters, from |
layout |
An optional |
row, col
|
Cell (1-based) of the parent's layout to place into. |
rowspan, colspan
|
Number of cells to span. |
mask |
An optional mask: a grob (or list of grobs), or an |
alpha |
Optional group opacity in |
blend |
Optional blend mode for compositing the viewport's contents (as an
isolated layer) onto the backdrop below it. One of |
name |
Optional name (for |
cache |
Repaint boundary ( |
widths, heights
|
Track sizes as a |
respect |
Logical; if |
A viewport object.
grid_layout(): a layout object.
vl_viewport(xscale = c(0, 10), yscale = c(0, 100))vl_viewport(xscale = c(0, 10), yscale = c(0, 100))
Reports the resolved width and height of a named viewport (or grob) and what
determined them — the layout track it was placed in, or the units of its own
width/height. The layout companion to the visual render(scene, debug = TRUE)
overlay; together they make coordinate debugging first-class rather than an
exercise in archaeology.
why_size(scene, name)why_size(scene, name)
scene |
A |
name |
A node name (set via the |
A vellum_why_size record (a list with name, width_mm,
height_mm, and determined_by), printed legibly.
s <- vl_scene(4, 3) |> push(vl_viewport(name = "panel", width = vl_unit(2, "in"), height = vl_unit(1, "in"))) why_size(s, "panel")s <- vl_scene(4, 3) |> push(vl_viewport(name = "panel", width = vl_unit(2, "in"), height = vl_unit(1, "in"))) why_size(s, "panel")