An introduction to legendplot

Ruben Fernandez-Casal

2026-09-11

Introduction

legendplot provides tools to combine standard R plots, or rgl 3D plots, with a legend representing either a continuous color scale or a categorical (factor) legend.

The basic graphics device only provides the legend() function for adding legends, with the problem that it can obscure the main plot; furthermore, it is only suitable for categorical values and is not useful for adding a continuous color scale. The rgl package only offers legend3d(), which incorporates the standard legend as a bitmap background for the current RGL subscene.

legendplot (currently) provides the following main functions:

Other packages that may be of interest are:

Nevertheless, I prefer standard or rgl graphics, mainly because they render faster when dealing with large amounts of data (also following the “keep it small and simple”, KISS, principle).

library(legendplot)
## legendplot: Standard and 'rgl' Plots with Legends,
##   version 0.4-1 (built on 2026-09-11).
##   Copyright (C) R. Fernandez-Casal 2012-2026.
##   Type `vignette("legendplot", package = "legendplot")`
##   or visit https://rubenfcasal.github.io/legendplot
##   for an overview.

Continuous scales in standard plots

The base function for a continuous legend is splot(). It splits the plotting region into a main panel and a legend strip showing a continuous color scale (based on fields::image.plot()). After calling this function, the main graph can be draw as usual. For example:

scale.range <- range(mtcars$mpg)
res <- splot(slim = scale.range, legend.lab = "mpg")
with(mtcars, 
     plot(hp, qsec, col = scolor(mpg, slim = scale.range),
          pch = 16, cex = 1.5, main = "Motor Trend Car Road Tests")
)

par(res$old.par) # restore graphical parameters; equivalent to `par.reset()`

scolor() maps a numeric vector to colors from a continuous palette, such as:

splot() can be also used with add = TRUE to attach a legend to an existing plot. For instance, several plots can share a common color scale:

set.seed(1)
nx <- c(40, 40)
x1 <- seq(-1, 1, length.out = nx[1])
x2 <- seq(-1, 1, length.out = nx[2])
trend <- outer(x1, x2, function(x, y) x^2 - y^2)
y <- trend + rnorm(prod(nx), 0, 0.1)

scale.range <- c(-1.2, 1.2)
scale.color <- jet.colors(256)

old.par <- par(mfrow = c(1, 2), omd = c(0.05, 0.85, 0.05, 0.95))
image(x1, x2, trend, zlim = scale.range, main = "Trend", col = scale.color)
image(x1, x2, y, zlim = scale.range, main = "Data", col = scale.color)
par(old.par)
splot(slim = scale.range, col = scale.color, legend.shrink = 0.7, add = TRUE)

The package also supplies high-level functions that make this pattern automatic. The sxxxx() family of functions (spoints(), simage() and spersp()) draws the color-scale legend and the corresponding base plot (plot(), image() or persp()), with the appropriate colors, in a single call:

with(mtcars, 
     spoints(hp, qsec, mpg, main = "Motor Trend Car Road Tests",
             xlab = "Horsepower", ylab = "1/4 mile time", 
             legend.lab = "Miles per gallon")
)

These functions are “compatible” with the mfcol and mfrow graphics parameters:

old.par <- par(mfrow = c(1, 2))
spersp(x1, x2, trend, slim = scale.range, main = "Trend", zlab = "y", legend = FALSE)
simage(x1, x2, y, slim = scale.range, main = "Data", legend.mar = 10, legend.width = 3)

par(old.par)

All of them share the same legend-related arguments as splot(), and accept legend = FALSE to draw the main plot without a legend. By default, the graphical parameters are reset to the values before entering the function. If reset = FALSE they will not be restored to make it possible to add more features to the plot (e.g. using functions such as points or lines). The graphical parameters can be restored using the old.par returned values or by calling function par.reset().

Categorical legends in standard plots

fplot() is the categorical counterpart of splot(): instead of a color bar it draws a classic factor-level legend (with boxes, points or line segments), using legend() internally.

f <- as.factor(mtcars$cyl)
res <- fplot(levels(f), col = cat.colors(nlevels(f)), type = "point",
             legend.lab = "cyl")
with(mtcars, plot(hp, qsec, col = fcolor(f, col = res$col),
                   pch = 16, cex = 1.5, main = "Motor Trend Car Road Tests"))

par.reset() # par(res$old.par)

fcolor() maps a factor (or a vector coercible to one) to colors, using a categorical palette such as hcld.colors() (based on hcl.colors() “Dark 3”) or cat.colors() (based on ColorBrewer 2.0).

The plot shown above can also be generated with the following command:

with(mtcars, 
     fpoints(hp, qsec, f = cyl, col = cat.colors(cyl), 
             main = "Motor Trend Car Road Tests")
)

Currently, only the high-level function fpoints() has been implemented. Users can follow the same approach shown previously to generate other types of graphs or to develop additional plot functions.

rgl 3D plots with legends

The same ideas extend to interactive 3D scenes built with the rgl package. splot3d() and fplot3d() split the active rgl device into a main subscene and a legend subscene. After calling one of these functions, rgl plotting functions can be used as usual. For example:

library(rgl)
# Use `open3d()` or `new3d()` to open a new device.
scale.range <- range(mtcars$mpg)
splot3d(slim = scale.range, legend.lab = "mpg")
with(mtcars, 
     plot3d(hp, qsec, wt, type = "s",
            col = scolor(mpg, slim = scale.range))
)

new3d() serves as a replacement for the open3d() and clear3d() functions1. It opens a new device if none exists (or if argument open = TRUE is set) and, otherwise, clears the current one. In addition, it changes several of the default mouse actions in ‘rgl’, assigning the middle button to zoom (via setmouse3d()), the right button to pan (via pan3d()), and enabling a double-click with the left button to restore the scene’s default viewpoint (via dbltrack3d(); keeping the mouse acting as a virtual trackball, rotating the scene, when this button is held down). Unfortunately, these mouse actions currently do not work with RMarkdown documents.

High-level 3D plot functions are also available, named in the form sxxx3d() and fxxx3d(), which allow the corresponding 3D graph to be plotted along with a legend, either continuous or categorical, in a single call. For example, the plot shown above could be generated with the following command:

with(mtcars, spoints3d(hp, qsec, wt, s = mpg, type = "s"))

Similarly, fpoints3d() draws a 3D scatter plot with a categorical legend, spersp3d() draws a 3D surface plot with a continuous color scale, and sshade3d() or fshade3d() draw a colored triangular mesh together with a continuous or categorical legend, respectively. As a final example, the topography of Auckland’s Maunga Whau volcano (bundled as the volcanom mesh) can be displayed with the following code:

sshade3d(volcanom, s = volcanom$vb[3, ], meshColor = "facesvertices")

Other rgl utilities

Beyond the plotting functions above, legendplot also provides a few usefull tools for working with rgl plots:

Further help

See the function reference pages for the full list of arguments and additional examples.


  1. Note that it is not necessary to use these functions in RMarkdown code chunks (see rgl::rglwidget() and Documents with ‘rgl’ Scenes).↩︎