248 lines
9.6 KiB
Plaintext
248 lines
9.6 KiB
Plaintext
|
% 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{grDevices, graphics, stats}
|
||
|
|
||
|
\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
|
||
|
\abbr{NPC} coordinates -- when the device is resized, the \abbr{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 ``pie plot'' 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}
|
||
|
|