This guide walks through the basic features of unigd and
compares them with the plot rendering methods in base R.
Plot rendering in base R
Rendering a plot in base R is done by (1) starting a graphics device, (2) calling some plot functions and (3) closing the device:
temp <- airquality$Temp
png(file="my_plot.png", width=600, height=400) # (1) Start the 'png' device
hist(temp, col="darkblue") # (2) Plot a histogram
dev.off() # (3) Close the deviceThis has some unfortunate constraints:
- Rendering information must be specified before the plot is created: file format, filepath, and dimensions.
- There is no way to render the same plot in multiple formats or dimensions without re-running the plotting code.
- No easy way to access the rendered data without writing to a file first.
-
dev.off()must be called every time, even if the plotting code errors.
unigd solves these issues with a different graphics
device architecture.
Plot rendering with unigd
The same render using unigd:
library(unigd)
temp <- airquality$Temp
ugd() # (1) Start the 'ugd' device
hist(temp, col="darkblue") # (2) Plot a histogram
ugd_save(file="my_plot.png", width=600, height=400) # Render 600x400 PNG
dev.off() # (3) Close the deviceRendering is an explicit step after plotting. This means you can render the same plot to multiple formats and dimensions:
# ...
hist(temp, col="darkblue")
ugd_save(file="my_plot.png", width=600, height=400) # 600x400 PNG
ugd_save(file="my_plot.pdf", width=300, height=300) # 300x300 PDF
# ...Starting and closing a device can be cumbersome, especially if the
plotting code errors and leaves the device open. For this reason
unigd provides ugd_*_inline functions that
handle device lifecycle automatically:
library(unigd)
temp <- airquality$Temp
ugd_save_inline({
hist(temp, col="darkblue")
}, file="my_plot.png", width=600, height=400)You can get the full list of included renderers with
ugd_renderers().
In-memory render access
For applications like report generation, web services, or interactive
tools, you may want to access the rendered data directly instead of
writing to a file. Use ugd_render() instead of
ugd_save():
temp <- airquality$Temp
ugd()
hist(temp, col="darkblue")
my_svg <- ugd_render(as="svg")
dev.off()
cat(my_svg)There is also an inline variant:
temp <- airquality$Temp
my_svg <- ugd_render_inline({
hist(temp, col="darkblue")
}, as="svg")
cat(my_svg)More features
Zoom
All rendering functions offer a zoom parameter that
scales the size of objects inside a plot independently of the plot
dimensions. For example, zoom=2 doubles all objects to
200%, zoom=0.5 halves them to 50%.
my_svg_1_0 <- ugd_render_inline({
hist(temp, col="darkblue", main = "Zoom 1.0")
}, as="png-base64", width=300, height=300, zoom=1.0)
my_svg_1_5 <- ugd_render_inline({
hist(temp, col="darkblue", main = "Zoom 1.5")
}, as="png-base64", width=300, height=300, zoom=1.5)
my_svg_0_5 <- ugd_render_inline({
hist(temp, col="darkblue", main = "Zoom 0.5")
}, as="png-base64", width=300, height=300, zoom=0.5)
alts <- c("Histogram at zoom 1.0", "Histogram at zoom 1.5", "Histogram at zoom 0.5")
knitr::raw_html(paste0(sprintf("<img src=\"%s\" alt=\"%s\" />",
c(my_svg_1_0, my_svg_1_5, my_svg_0_5), alts)))Paging (by index)
The page parameter selects which plot from the history
to render. By default it is 0, which uses the most recently
created plot. Values ≥ 1 select by index (oldest first), values ≤ 0
select newest-first:
ugd()
for (i in 1:10) {
plot(1, main=paste0("Plot #", i))
}
ugd_save(file="plot.png", page = 3) # Plot #3
ugd_save(file="plot.png") # Plot #10 (latest)
ugd_save(file="plot.png", page = -1) # Plot #9
dev.off()Plots can also be removed from history:
ugd_remove() # Remove last
ugd_remove(page = -1) # Remove second-to-last
ugd_clear() # Remove allPlot IDs
Plot indices shift when plots are added or removed. If you need to
refer to a specific plot later, grab its ID with
ugd_id():
ugd()
plot(rnorm(50)) # A
id_a <- ugd_id() # Get last ID (A)
hist(rnorm(50)) # B
plot(sin((1:100)/3)) # C
id_b <- ugd_id(-1) # Second-to-last (B)
hist(runif(100)) # D
ugd_remove(3) # Remove C
ugd_save(file="plot_a.png", page = id_a)
ugd_save(file="plot_b.png", page = id_b)
dev.off()In practice this is usually simpler: just call ugd_id()
after each plot to capture its ID.
Special renderers
unigd ships with a few special renderers beyond image
formats:
-
"strings": All text elements inside a plot, one per line. Useful for searching through plots. -
"meta": Plot metadata in JSON format. Includes complexity (number of draw calls and clipping planes). Guaranteed O(1) render time regardless of plot complexity. -
"json": All informationunigdhas about a plot, in JSON format.
Performance considerations
For most applications, readability should be prioritized over performance. Unless graphics rendering is bottlenecking your R script, you can safely skip this section.
The key thing to understand is when unigd needs to ask
the R graphics engine to redraw a plot. Rendering happens after drawing,
and the last drawn dimensions are cached. Two rules follow from
this:
- Rendering the same plot in different formats: fast (no redraw needed).
- Rendering the same plot in different dimensions: slower (triggers a redraw).
Grouping renders by dimension avoids unnecessary redraws:
# Two redraws (dimensions keep changing):
ugd_save(file="my_plot.png", width=600, height=400)
ugd_save(file="my_plot.pdf", width=300, height=300) # redraw
ugd_save(file="my_plot.svg", width=600, height=400) # redraw
# One redraw (same dimensions grouped together):
ugd_save(file="my_plot.png", width=600, height=400)
ugd_save(file="my_plot.svg", width=600, height=400)
ugd_save(file="my_plot.pdf", width=300, height=300) # redrawYou can also avoid the initial redraw by setting the target dimensions at device creation time:
ugd(width=300, height=300)
# ...
ugd_save(file="my_plot.png", width=300, height=300) # no redraw neededIf you omit the dimensions when calling rendering functions, the last known dimensions are used and no redraw occurs:
ugd_save(file="my_plot.png")All ugd_*_inline functions also avoid unnecessary
redraws.
Note that zoom interacts with dimensions: the cached
width is width / zoom.
