| % File src/library/grid/vignettes/displaylist.Rnw |
| % Part of the R package, https://www.R-project.org |
| % Copyright 2001-13 Paul Murrell and the R Core Team |
| % Distributed under GPL 2 or later |
| |
| \documentclass[a4paper]{article} |
| |
| \usepackage{Rd} |
| |
| % \VignetteIndexEntry{Display Lists in grid} |
| % \VignettePackage{grid} |
| % \VignetteDepends{graphics} |
| % \VignetteDepends{gridBase} |
| |
| \newcommand{\grid}{\pkg{grid}} |
| \newcommand{\gridBase}{\CRANpkg{gridBase}} |
| \newcommand{\lattice}{\CRANpkg{lattice}} |
| |
| \setlength{\parindent}{0in} |
| \setlength{\parskip}{.1in} |
| \setlength{\textwidth}{140mm} |
| \setlength{\oddsidemargin}{10mm} |
| |
| \title{Display Lists in \grid{}} |
| \author{Paul Murrell} |
| |
| \begin{document} |
| \maketitle |
| |
| <<echo=FALSE, results=hide>>= |
| library(grDevices) |
| library(graphics) # for plot() |
| library(stats) # for runif() |
| library(grid) |
| ps.options(pointsize = 12) |
| options(width = 60) |
| @ |
| A display list is a record of drawing operations. It is used |
| to redraw graphics output when a graphics window is resized, |
| when graphics output is copied from one device to another, and |
| when graphics output is edited (via \code{grid.edit}). |
| |
| There are two display lists that can be used when working with |
| \grid{}. \R{}'s graphics engine maintains a display list and |
| \grid{} maintains its own display list. The former is maintained at |
| the C code level and records both base graphics output and |
| \grid{} graphics output. The latter is maintained at the |
| R code level and only records \grid{} output. |
| |
| In standard usage, |
| the graphics engine's display list is used to redraw when a window |
| is resized and when copying between devices. \grid{}'s display list |
| is used for redrawing when editing \grid{} output. |
| |
| There are two main problems with this standard usage: |
| \begin{enumerate} |
| \item The graphics engine display list only records graphics output; |
| none of the calculations leading up to producing the output are |
| recorded. This particularly impacts on plots which perform calculations |
| based on the physical dimensions of the device -- an example is |
| the legend function which performs calculations in order to |
| arrange the elements of the legend. The effect can be seen from |
| any example which uses the legend function. Try running |
| \code{example(legend)} then resize the device (make it quite |
| tall and thin or quite wide and fat); the legend will start |
| to look pretty sick. |
| |
| {\bf NOTE:} that this is a problem with the graphics engine display |
| list -- it is not specific to \grid{}. In fact, much of \grid{}'s |
| behaviour is protected from this problem because things like \grid{} |
| units are ``declarative'' and will be re-evaluated on each redraw. |
| However, there are situations where \grid{} output can be afflicted, |
| in particular, whenever the \code{convertUnit()} function (or one of |
| its variants) is used (the help file for \code{convertUnit()} gives an |
| example). |
| |
| A situation where this problem becomes very relevant for |
| \grid{} output is when the \gridBase{} package is used. |
| This is a situation where lots of calculations are performed |
| in order to align base and grid output, but these calculations |
| are not recorded on the graphics engine display list, so |
| if the device is resized the output will become very yukky. |
| |
| \item \grid{}'s display list does not record base graphics |
| output\footnote{This is not quite true; it is possible |
| to include base graphics output on the \grid{} display list |
| as we will see later.} so if both base and \grid{} output appear |
| on the same device then the result of editing will not redraw the |
| base output. The following code provides a simple example: |
| |
| <<guts1, eval=FALSE>>= |
| plot(1:10) |
| par(new = TRUE) |
| grid.rect(width = 0.5, height = 0.5, gp = gpar(lwd = 3), name = "gr") |
| |
| <<ex1, echo=FALSE, fig=TRUE, width=4, height=3, include=FALSE>>= |
| <<guts1>> |
| grid.rect(width = 0.99, height = 0.99, gp = gpar(lty = "dashed")) |
| @ |
| \includegraphics[width=4in, height=3in]{displaylist-ex1} |
| |
| <<ex2, echo=FALSE, fig=TRUE, width=4, height=3, include=FALSE>>= |
| grid.rect(width = 0.5, height = 0.5, gp = gpar(col = "red", lwd = 3)) |
| grid.rect(width = 0.99, height = 0.99, gp = gpar(lty = "dashed")) |
| |
| <<eval=FALSE>>= |
| grid.edit("gr", gp = gpar(col = "red", lwd = 3)) |
| @ |
| \includegraphics[width=4in, height=3in]{displaylist-ex2} |
| |
| After the \code{grid.edit}, the rectangle has been redrawn, but the |
| base plot has not. |
| \end{enumerate} |
| |
| \section*{Saving calculations on the graphics engine display list\\ |
| and saving base graphics on the \grid{} display list} |
| |
| Both of the problems described in the previous section can be |
| avoided by using a \code{drawDetails()} method in \grid{}. |
| When a \grid{} grob is drawn, the \code{drawDetails} method |
| for that grob is called; if calculations are put within |
| a \code{drawDetails} method, then the calculations will be |
| performed every time the grob is drawn. |
| |
| This means that it is possible, for example, to use \code{convertUnit()} |
| and have the result consistent across device resizes or |
| copies\footnote{In |
| each of the examples that follow, you should execute the example |
| code, resize the device to see any inconsistency, then close the |
| device before trying the next example.}. |
| This next piece of code is an example where the output becomes |
| inconsistent when the device is resized. We specify a width for |
| the rectangle in inches, but convert it (gratuitously) to |
| NPC coordinates -- when the device is resized, the NPC coordinates |
| will no longer correspond to 1''. |
| |
| <<results=hide>>= |
| grid.rect(width = convertWidth(unit(1, "inches"), "npc")) |
| @ |
| |
| The next piece of code demonstrates that, if we |
| place the calculations within a \code{drawDetails} method, then |
| the output remains consistent across device resizes and |
| copies. |
| |
| <<results=hide>>= |
| drawDetails.myrect <- function(x, recording) { |
| gr <- rectGrob(width = convertWidth(unit(1, "inches"), "npc")) |
| grid.draw(gr) |
| } |
| grid.draw(grob(cl = "myrect")) |
| @ |
| |
| The next example shows that a \code{drawDetails()} method can also be |
| used to save base graphics output on the \grid{} display list. This |
| example uses \gridBase{} to combine base and \grid{} graphics output. |
| Here I replicate the last example from the \gridBase{} vignette -- a |
| set of base pie charts within \grid{} viewports within a base plot. |
| In this case, I can produce all of the grobs required in the normal |
| manner -- their locations and sizes are not based on special |
| calculations\footnote{The example is wrapped inside a check for |
| whether the \CRANpkg{gridBase} package is installed so that the code |
| will still ``run'' on systems without \CRANpkg{gridBase}.}. |
| |
| <<results=hide>>= |
| x <- c(0.88, 1.00, 0.67, 0.34) |
| y <- c(0.87, 0.43, 0.04, 0.94) |
| z <- matrix(runif(4*2), ncol = 2) |
| |
| maxpiesize <- unit(1, "inches") |
| totals <- apply(z, 1, sum) |
| sizemult <- totals/max(totals) |
| |
| gs <- segmentsGrob(x0 = unit(c(rep(0, 4), x), |
| rep(c("npc", "native"), each = 4)), |
| x1 = unit(c(x, x), rep("native", 8)), |
| y0 = unit(c(y, rep(0, 4)), |
| rep(c("native", "npc"), each = 4)), |
| y1 = unit(c(y, y), rep("native", 8)), |
| gp = gpar(lty = "dashed", col = "grey")) |
| gr <- rectGrob(gp = gpar(col = "grey", fill = "white", lty = "dashed")) |
| @ |
| What is important is that I place the calls to the \gridBase{} functions |
| within the \code{drawDetails} method so that they are performed |
| every time the grob is drawn {\em and} the calls to the base graphics |
| functions are in here too so that they are called for every redraw. |
| |
| <<results=hide>>= |
| drawDetails.pieplot <- function(x, recording) { |
| plot(x$x, x$y, xlim = c(-0.2, 1.2), ylim = c(-0.2, 1.2), type = "n") |
| vps <- baseViewports() |
| pushViewport(vps$inner, vps$figure, vps$plot, recording = FALSE) |
| grid.draw(x$gs, recording = FALSE) |
| for (i in 1:4) { |
| pushViewport(viewport(x = unit(x$x[i], "native"), |
| y = unit(x$y[i], "native"), |
| width = x$sizemult[i]*x$maxpiesize, |
| height = x$sizemult[i]*x$maxpiesize), |
| recording = FALSE) |
| grid.draw(x$gr, recording = FALSE) |
| par(plt = gridPLT(), new = TRUE) |
| pie(x$z[i, ], radius = 1, labels = rep("", 2)) |
| popViewport(recording = FALSE) |
| } |
| popViewport(3, recording = FALSE) |
| } |
| @ |
| The ``pieplot'' is created by assembling the component grobs |
| into a collective grob of the appropriate class; the \code{drawDetails} |
| method takes care of actually producing the output. |
| |
| % produce figure, but don't include |
| % (to avoid par setting contamination between code segments) |
| |
| <<results=hide, fig=TRUE, width=6, height=6, include=FALSE>>= |
| if (suppressWarnings(require("gridBase", quietly = TRUE))) { |
| grid.draw(grob(x = x, y = y, z = z, |
| maxpiesize = maxpiesize, sizemult = sizemult, |
| gs = gs, gr = gr, cl = "pieplot")) |
| } |
| @ |
| |
| The output from this example can be resized safely; \grid{} handles |
| all of the redrawing, and performs all of the actions within the |
| \code{drawDetails} method for each redraw, including redrawing the |
| base graphics output! |
| |
| As a final example, we will harness the \grid{} display list purely to |
| achieve consistency in base graphics output. The following reproduces |
| the last example from the \code{legend()} help page, but produces |
| output which can be resized without the legend going crazy. |
| |
| % produce figure, but don't include |
| % (to avoid par setting contamination between code segments) |
| |
| <<results=hide, fig=TRUE, include=FALSE>>= |
| drawDetails.mylegend <- function(x, recording) { |
| x <- 0:64/64 |
| y <- sin(3*pi*x) |
| plot(x, y, type = "l", col = "blue", |
| main = "points with bg & legend(*, pt.bg)") |
| points(x, y, pch = 21, bg = "white") |
| legend(.4,1, "sin(c x)", pch = 21, pt.bg = "white", lty = 1, col = "blue") |
| } |
| grid.draw(grob(cl = "mylegend")) |
| @ |
| \end{document} |
| |