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").
Usage
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,
key = NULL,
meta = 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,
key = NULL,
meta = NULL
)
polygon_grob(
x,
y,
sketch = NULL,
gp = vl_gpar(),
name = NULL,
vp = NULL,
id = NULL,
role = NULL,
key = NULL,
meta = 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,
col = NULL,
lwd = NULL
)
path_grob(
x,
y,
id = NULL,
rule = c("winding", "evenodd"),
sketch = NULL,
gp = vl_gpar(),
name = NULL,
vp = NULL,
role = NULL,
key = NULL,
meta = 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,
width = NULL,
height = NULL,
align = "left",
fit = FALSE,
key = NULL,
meta = NULL
)Arguments
- x, y
Coordinates (
vl_unit()or numeric).- width, height
Grob size (
vl_unit()or numeric), recycled likex/y. For most grobs the drawn rectangle size. Forhexagon_grob(), the optional per-hexagon full corner-to-corner extent along x/y: when both are given they overridesize(resolved per-axis, so a hexagon can be non-regular and tile a non-square lattice — e.g."native"units tile in data space regardless of device aspect); a regular flat hexagon isheight == width * sqrt(3) / 2; leave bothNULLto use circumradiussize; must be given together. Forloop_grob(),widthis instead a dimensionless lateral petal scale in(0, 1](recycled per loop):1(default) is the full teardrop, smaller narrows the petal's waist without shortening it (the igraph "narrowing" factor).For
text_grob()these mean something different:widthis an absolute wrapping measure (mm/cm/in/pt), and giving it breaks the label into lines that fit, making the drawn block a box of exactly that width — sojustanchors the box rather than the longest line.heightis used only byfit. Relative units are rejected there on purpose: wrapping happens when the grob is built, and a viewport has no size innpc/nativeuntil render time.- sketch
Optional
sketch()spec for a hand-drawn look;NULL= crisp.- gp
Graphical parameters, from
vl_gpar().- name
Optional name (for
edit_node()).- vp
Optional
vl_viewport()to draw this grob inside.- id
For most grobs, an optional semantic identifier emitted by the SVG backend as
data-vellum-id(for interactivity, accessibility, and testing; ignored by raster/PDF). Forpath_grobonly,idinstead groups points (one value per point) into closed sub-paths: all points sharing anidform one sub-path (so a hole is a separateid), in first-appearance order (à la grid);NULLmakes a single sub-path.- role
Optional ARIA role, emitted by the SVG backend as
role=for accessibility (ignored by the raster and PDF backends).- key
Optional per-element data key(s) for the batched marks (
points_grob,circle_grob,rect_grob,segments_grob,hexagon_grob,sector_grob), recycled to the element count likefill. Emitted by the SVG backend asdata-keyon each element and surfaced byscene_model()— the join key a host uses to map an interaction back to a datum.NULL(default) emits nothing (a static render is unchanged). Ignored by raster/PDF.- 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
scene_model().NULL(default) = none.- r
Radius (
vl_unit()or numeric).- arrow
An
vl_arrow()spec to draw heads on the line/segment ends, orNULLfor none.- start_cap, end_cap
Optional absolute-length
vl_unit()s (mm/cm/in/pt; a bare numeric is taken asmm) that shorten the drawn line inward from its start/end by that physical amount, resolved at render in device space — so the gap is exact at any size, dpi, and aspect ratio, with no reliance on the native scale. Forsegments_grob()the caps are per-element (scalar or length-n, recycled like the coordinates); forlines_grob()a single (scalar) cap trims each end of the whole polyline.NULL(default) leaves the endpoint untouched. When anvl_arrow()is also present its head is placed at the capped end, so the tip lands on the boundary (e.g. a node marker) rather than under it. This is what lets a directed edge stop at a node's radius. See the acceptance notes in the package for the degenerate cases (a cap>=the segment length draws nothing; a zero-length segment is skipped).- offset
Optional absolute-length
vl_unit()(mm/cm/in/pt; a bare numeric ismm) that shifts the line perpendicular to its own direction by that physical amount, resolved at render in device space. The sign picks the side (+left of the direction of travel,−right). Forsegments_grob()it is per-element (scalar or length-n) — passing a vector spreads parallel/reciprocal edges by a fixed physical spacing that tracks mm node sizes at any figure size; forlines_grob()a single (scalar) offset rigidly translates the whole polyline along the perpendicular of its overall direction. Applied beforestart_cap/end_capand the arrowhead (offset, then cap, then head).NULL/0(default) leaves the geometry untouched.- n
Number of points to sample the curve at (flattened to a polyline).
- shape
Marker shape(s):
"circle"(default),"square","triangle","diamond","plus","cross","triangle_down", or"star", recycled per point. Filled shapes (all but"plus"/"cross") paintgp$filland outline withgp$col, so an open marker isfill = NAwith acol, a filled one setsfill;"plus"/"cross"are stroke-only line glyphs.- open
If
FALSE, the spline is closed (wraps end to start).- size
Loop extent: an absolute
vl_unit()(mm/cm/in/pt; a bare numeric ismm), resolved to a device length at render so the loop tracks a node's mm size at any page size/dpi. Nested loops on one vertex pass growingsize(samex/y/angle) for concentric teardrops. Recycled per loop.- fill
Per-element fill colour(s), recycled to the number of sectors.
NULLfalls back togp$fill.- orientation
Hexagon orientation:
"flat"(default, flat top/bottom edge) or"pointy"(vertex at top).sizeis the circumradius (centre to vertex).- r0, r1
Inner and outer radius of each sector (
vl_unit()or numeric; numeric is treated as"native").r0 = 0gives a pie slice;r0 == r1gives an arc outline (stroke only, no fill).- 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
vl_unit();0= both feet at the vertex, like igraph). A positivefootputs the feet on the node's boundary so the loop visibly leaves and re-enters the node edge, and a directed loop's head lands on the boundary rather than under the marker. Recycled per loop.- 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 (
vl_unit()or numeric).- col, lwd
segments_grob()only — per-segment stroke colour and width, recycled to the segment count, mirroring the per-elementfillthathexagon_grob()andsector_grob()carry.NULL(default) uses the sharedgp, and the batch is drawn in one combined stroke exactly as before.Reach for these instead of building one grob per segment: varying width or colour that way costs an R-side grob each, which is the dominant expense on scenes made of many small marks. Note that no backend can stroke segments differently in a single call, so each segment is stroked on its own here — the saving is in R, not in the output size.
These vary width per element, so the width steps between segments and the joins show. For width varying smoothly along one line, with no joins to give it away, see
lwd_profileinstroke_to_path().- rule
Fill rule:
"winding"(non-zero, default) or"evenodd".- image
A raster image. Either a
grDevices::as.raster()-compatible object — a matrix/array of colours or greyscale values, or araster— or a path to a PNG file, decoded in the Rust backend so no R image package is needed. A numeric RGB/RGBA array in[0, 1](whatpng::readPNG()returns) takes a fast path that skips the per-pixel colour-string round-tripas.raster()would otherwise do; the pixels are identical either way.- interpolate
Smoothly interpolate when scaling (default
TRUE)?FALSEkeeps hard pixel edges.- label
Character string(s) to draw.
- just
Justification:
c(hjust, vjust)as names ("left","centre","right","bottom","top") or numbers in[0, 1].- rot
Rotation in degrees, counter-clockwise.
- align
Alignment of lines within the box:
"left"(default),"centre"/"center","right", or"justify"(flush both edges, last line of each paragraph left as-is).- fit
Auto-fit sizing.
FALSE(default) keepsgp$fontsize.TRUEshrinks the font — never grows it — until the wrapped block fitswidth×height, down to a floor of 4 pt; a number sets that floor instead. Requireswidth.
Details
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.
