--- title: "Rendering grid and ggplot2 through vellum" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Rendering grid and ggplot2 through vellum} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r} #| include: false knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4, dpi = 96, dev = "png" ) library(vellum) ``` You do not have to rewrite existing graphics to draw them with vellum's backend. `as_vellum()` converts a grid grob tree, or a ggplot2 plot, into a `vl_scene()`, and `render_grid()` does that and writes the result to a file. This is the interop path: grid-based graphics rendered by vellum's deterministic PNG / SVG / PDF backend. ## How it works grid graphics are lazy: a grob's real geometry is only known once a device and viewport resolve its units. `as_vellum()` leans on that machinery rather than reimplementing it. It spins up an offscreen grid device, lets grid resolve every coordinate to an absolute position, and then emits the corresponding vellum grobs. The output is a normal `vellum_scene`, so everything else in vellum (multi-backend `render()`, `scene_raster()`, `display()`) applies. ## A grid grob tree Any grob or gTree works. Here is a small hand-built one. ```{r} #| label: grid-grob library(grid) g <- gTree(children = gList( rectGrob(gp = grid::gpar(fill = "grey97", col = "grey70")), circleGrob(r = 0.3, gp = grid::gpar(fill = "#3a7bd5", col = NA)), textGrob("grid -> vellum", y = 0.12, gp = grid::gpar(fontface = "bold")) )) as_vellum(g, width = 5, height = 3) ``` `as_vellum()` returns a scene, which auto-prints (displays) here. Assign it and you can render it to any backend: ```{r} #| label: render-grid #| eval: false render_grid(g, "grid.png", width = 5, height = 3) render_grid(g, "grid.pdf", width = 5, height = 3) ``` ## A ggplot2 plot Passing a ggplot object works the same way: ggplot builds a gtable of grobs, and `as_vellum()` renders that. ```{r} #| label: ggplot #| eval: !expr requireNamespace("ggplot2", quietly = TRUE) library(ggplot2) p <- ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) + geom_point(size = 2) + labs(title = "mtcars", colour = "cyl") + theme_minimal() as_vellum(p, width = 6, height = 4) ``` To write it out, use `render_grid()` with the format in the file extension: ```{r} #| label: ggplot-render #| eval: false render_grid(p, "mtcars.png", width = 6, height = 4) render_grid(p, "mtcars.svg", width = 6, height = 4) ``` lattice output works through the same door: draw the lattice object to capture its grob tree, or pass a captured grob to `as_vellum()`. ## When to use interop versus the native API Reach for `as_vellum()` / `render_grid()` when you already have grid, ggplot2, or lattice output and want vellum's deterministic, multi-backend rendering (for example byte-stable PNGs for snapshot tests, or one plot emitted to PNG, SVG, and PDF from the same source). Build with the native `vl_scene()` API instead when you want what the retained scene graph offers: named and editable nodes, hit-testing, and a per-element `scene_model()` (see `vignette("retained-mode")`). Interop reproduces the *pixels* of a grid scene, but a grammar built directly on vellum is what carries the per-element identity that interactivity needs. A native vellum graphics device (so `plot()` and friends target vellum directly) is future work; until then, `as_vellum()` is the bridge. ```