Rev 42945 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\documentclass[a4paper]{article}%\VignetteIndexEntry{Display Lists in grid}%\VignettePackage{grid}%\VignetteDepends{graphics}%\VignetteDepends{gridBase}\newcommand{\grid}{{\tt grid}}\newcommand{\gridBase}{{\tt gridBase}}\newcommand{\lattice}{{\tt lattice}}\newcommand{\R}{{\tt R}}\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 usedto redraw graphics output when a graphics window is resized,when graphics output is copied from one device to another, andwhen graphics output is edited (via {\tt 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 atthe C code level and records both base graphics output and\grid{} graphics output. The latter is maintained at theR code level and only records \grid{} output.In standard usage,the graphics engine's display list is used to redraw when a windowis resized and when copying between devices. \grid{}'s display listis 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 arerecorded. This particularly impacts on plots which perform calculationsbased on the physical dimensions of the device -- an example isthe legend function which performs calculations in order toarrange the elements of the legend. The effect can be seen fromany example which uses the legend function. Try running{\tt example(legend)} then resize the device (make it quitetall and thin or quite wide and fat); the legend will startto look pretty sick.{\bf NOTE:} that this is a problem with the graphics enginedisplay list -- it is not specific to \grid{}. In fact, muchof \grid{}'s behaviour is protected from this problem becausethings like \grid{} units are ``declarative'' and will be reevaluatedon each redraw. However, there are situations where \grid{} outputcan be afflicted, in particular, whenever the {\tt convertUnit()}function (or one of its variants) is used (the help filefor {\tt 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 performedin order to align base and grid output, but these calculationsare not recorded on the graphics engine display list, soif the device is resized the output will become very yukky.\item \grid{}'s display list does not record base graphicsoutput\footnote{This is not quite true; it is possibleto include base graphics output on the \grid{} display listas we will see later.} so if both base and \grid{} output appearon the same device then the result of editing will not redraw thebase 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 {\tt grid.edit}, the rectangle has been redrawn, but thebase 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 beavoided by using a {\tt drawDetails()} method in \grid{}.When a \grid{} grob is drawn, the {\tt drawDetails} methodfor that grob is called; if calculations are put withina {\tt drawDetails} method, then the calculations will beperformed every time the grob is drawn.This means that it is possible, for example, to use {\tt convertUnit()}and have the result consistent across device resizes orcopies\footnote{Ineach of the examples that follow, you should execute the examplecode, resize the device to see any inconsistency, then close thedevice before trying the next example.}.This next piece of code is an example where the output becomesinconsistent when the device is resized. We specify a width forthe rectangle in inches, but convert it (gratuitously) toNPC coordinates -- when the device is resized, the NPC coordinateswill no longer correspond to 1''.<<results=hide>>=grid.rect(width=convertWidth(unit(1, "inches"), "npc"))@The next piece of code demonstrates that, if weplace the calculations within a {\tt drawDetails} method, thenthe output remains consistent across device resizes andcopies.<<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 showsthat a {\tt drawDetails()} method can also be usedto save base graphics output on the \grid{} display list.This exampleuses\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 normalmanner -- their locations and sizes are not based on specialcalculations\footnote{The example is wrapped inside a check forwhether the {\tt gridBase} package is installed so that the codewill still ``run'' on systems without {\tt gridBase}.}.<<results=hide>>=if ("gridBase" %in% .packages(all.available=TRUE)) {library(gridBase)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{} functionswithin the {\tt drawDetails} method so that they are performedevery time the grob is drawn {\em and} the calls to the base graphicsfunctions 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 grobsinto a collective grob of the appropriate class; the {\tt 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 ("gridBase" %in% .packages()) {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{} handlesall of the redrawing,and performs all of the actions within the{\tt drawDetails} method for each redraw,including redrawing the base graphics output!As a final example, we will harness the \grid{} display listpurely to achieve consistency in base graphics output.The following reproduces the last example from the{\tt legend()} help page, but produces output which can beresized 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/64y <- 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"))@% Start a new page% Not echoed, not evaluated% ONLY here for checkVignettes so that all output doesn't% end up on one enormous page<<eval=FALSE, echo=FALSE>>=grid.newpage()@\end{document}