Rev 42945 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\documentclass[a4paper]{article}%\VignetteIndexEntry{Working with viewports}\newcommand{\grid}{{\tt grid}}\newcommand{\lattice}{{\tt lattice}}\newcommand{\R}{{\tt R}}\setlength{\parindent}{0in}\setlength{\parskip}{.1in}\setlength{\textwidth}{140mm}\setlength{\oddsidemargin}{10mm}\title{Working with \grid{} Viewports}\author{Paul Murrell}\begin{document}\maketitle<<echo=FALSE, results=hide>>=library(grDevices)library(stats) # for runif()library(grid)ps.options(pointsize=12)options(width=60)@This document describes some features of \grid{} viewports whichmake it easy to travel back and forth between multiple regionson a device (without having to recreate those regions), and provides amechanism for a complex plotting function to provide users with accessto all of the regions created during plotting.\section*{The viewport tree}\grid{} maintains atree of pushed viewports on each device.When the \verb|upViewport()|function is called it works like \verb|popViewport()| except thatit does not remove viewports from the viewport tree.For example, the following code pushes a viewport, then navigatesback up to the top level viewport and pushes another viewport,without removing the first viewport.<<>>=pushViewport(viewport())upViewport()pushViewport(viewport())@There are now two viewports pushed directly beneath the top-levelviewport. This immediately creates an ambiguity; if I navigateback up to the top-level and attempt to revisit one of theviewports, how can I specify which one I want? The answer is thatviewports have a \verb|name| argument\footnote{The print methodfor viewports shows the viewport name within square brackets. Trytyping {\tt current.viewport()}.}. This name, combined withthe \verb|downViewport()| function, makes it possible tonavigate back down to viewports in the viewport tree. Consider thefollowing example, which pushes two viewports called \verb|"A"| and\verb|"B"|, then navigates to viewport \verb|"A"| from thetop level.<<echo=FALSE, results=hide>>=grid.newpage()<<>>=pushViewport(viewport(name="A"))upViewport()pushViewport(viewport(name="B"))upViewport()downViewport("A")@The \verb|downViewport()| function searches down the tree from thecurrent position in the tree. The \verb|seekViewport()|function is similar, but it always starts searching from the top-levelviewport. In the previous example we ended up in viewport\verb|"A"|; the following command navigates from \verb|"A"| to \verb|"B"|in a single step.<<>>=seekViewport("B")@The function \verb|current.vpTree()| provides a(textual) view of the current viewport tree.<<>>=current.vpTree()@\section*{Viewport stacks, lists, and trees}It is possible to create multiple viewport descriptions\emph{and their relationships}. The functions\verb|vpStack()|, \verb|vpList()|, and\verb|vpTree()| can be used to create a stack, a list, or a treeof viewport descriptions, respectively. It is then possible topush these multiple descriptions at once; viewports in a stackare pushed in series, viewports in a list are pushed in parallel,and for a tree of viewports, the parent is pushed then the childrenare pushed in parallel. The following simpleexample demonstrates one usage of this feature; a \grid{} rectangleis drawn \emph{two} viewports below the current level byspecifying a stack of viewports in its \verb|vp| argument.<<vpstackguts>>=vp <- viewport(width=0.5, height=0.5)grid.rect(vp=vpStack(vp, vp))<<echo=FALSE, fig=TRUE>>=grid.rect(gp=gpar(col="grey"))<<vpstackguts>>@\section*{Viewport paths}The previous example demonstrates a subtle feature of \grid{}'s viewporttree. The same viewport, \emph{with the same name}, was pushedtwice (in series). This demonstrates that viewport names onlyhave to be unique for viewports which share the same parent.This makes it possible, especially in repetitive plot arrangements,to reuse convenient viewport names. For example, in a \lattice{}style plot, each panel could have a viewport called \verb|"strip"|to represent the strip region.This design creates a further ambiguity because there may bemore than one viewport with the same name within the viewporttree\footnote{{\tt downViewport()} and {\tt seekViewport()} will stop at thefirst match they find (the search is currently depth-first).}.This ambiguity can be resolved by using the \verb|vpPath()|function to generate a specification of a stack of viewportsthat must be matched by name. This path can be passed toeither \verb|downViewport()| or \verb|seekViewport()| as in thefollowing example; notice that we are calling\verb|current.vpTree(FALSE)| in order to see the current viewporttree \emph{only from the current viewport down}.<<echo=FALSE, results=hide>>=grid.newpage()<<>>=pushViewport(viewport(name="A"))pushViewport(viewport(name="B"))pushViewport(viewport(name="A"))@When we do a seek on just \verb|"A"|, we find the first \verb|"A"|(just below the top-level viewport).<<>>=seekViewport("A")current.vpTree(FALSE)@By specifying a \verb|vpPath|, we can get the \verb|"A"| directlybelow viewport \verb|"B"|.<<>>=seekViewport(vpPath("B", "A"))current.vpTree(FALSE)@A viewport path is conceptually just a concatenation of severalnames using a path separator (currently \verb|::|).<<>>=vpPath("A", "B")@Forinteractive use, it is possible to specify the path as a simplestring, but this is not recommended otherwise in casethe path separator changes in future versions of \grid{}.As an example, the following two commands are currentlyequivalent.<<eval=FALSE>>=seekViewport(vpPath("A", "B"))seekViewport("A::B")@\section*{An example}In this section, we consider a simple example to demonstratehow these new viewport features might be used together.The goal is to produce a simple scatterplot. We will workwith some random data.<<>>=x <- runif(10)y <- runif(10)@We will be establishing some scales appropriate for these data,so we calculate sensible ranges now.<<>>=xscale <- extendrange(x)yscale <- extendrange(y)@We now produce a set of viewports that will be useful in creating theplot. The first viewport contains a layout to divide the drawing regioninto several rows and columns. The left and right columns and top andbottom rows provide room for axes and labels, while the central cell providesa region for plotting the data. The diagram below the code showsthe layout that we create.<<>>=top.vp <- viewport(layout=grid.layout(3, 3,widths=unit(c(5, 1, 2), c("lines", "null", "lines")),heights=unit(c(5, 1, 4), c("lines", "null", "lines"))))<<echo=FALSE, fig=TRUE>>=grid.show.layout(viewport.layout(top.vp))@Next we create a set of viewports which will occupy different areas within thelayout, corresponding to the margins for axes and labels, and the plottingregion.<<>>=margin1 <- viewport(layout.pos.col=2, layout.pos.row=3,name="margin1")margin2 <- viewport(layout.pos.col=1, layout.pos.row=2,name="margin2")margin3 <- viewport(layout.pos.col=2, layout.pos.row=1,name="margin3")margin4 <- viewport(layout.pos.col=3, layout.pos.row=2,name="margin4")plot <- viewport(layout.pos.col=2, layout.pos.row=2,name="plot", xscale=xscale, yscale=yscale)@Notice that we have not pushed any of these viewports yet so no regionsexist on the output device. We first of all arrange the viewports intoa tree structure, with the {\tt top.vp} as the parent node andall of the other viewports as its children.<<>>=splot <- vpTree(top.vp, vpList(margin1, margin2, margin3, margin4, plot))@Now we can push this entire tree of viewports in order to create all of thedifferent areas within the drawing region that we need to draw thescatterplot.The result of this push is that we are left in the {\tt plot} viewport.<<viewports>>=pushViewport(splot)@Now we can navigate to whichever viewport we require and draw thedifferent elements of the plot\footnote{The named viewports that we createdare drawn as grey rectangles as a guide.}.<<grid, echo=FALSE, eval=FALSE>>=labelvp <- function(name) {seekViewport(name)grid.rect(gp=gpar(col="grey", lwd=5))grid.rect(x=0, y=1, width=unit(1, "strwidth", name) + unit(2, "mm"),height=unit(1, "lines"), just=c("left", "top"),gp=gpar(fill="grey", col=NULL))grid.text(name, x=unit(1, "mm"), y=unit(1, "npc") - unit(1, "mm"),just=c("left", "top"), gp=gpar(col="white"))}labelvp("plot")labelvp("margin1")labelvp("margin2")labelvp("margin3")labelvp("margin4")@The data symbols and axes are drawn relative to the plot region ...<<plot, eval=FALSE>>=seekViewport("plot")grid.points(x, y)grid.xaxis()grid.yaxis()grid.rect()@... the x-axis label is drawn in margin 1 ...<<margin1, eval=FALSE>>=seekViewport("margin1")grid.text("Random X", y=unit(1, "lines"))@... and the y-axis label is drawn in margin 2 (the final outputis shown on the next page).<<margin2, eval=FALSE>>=seekViewport("margin2")grid.text("Random Y", x=unit(1, "lines"), rot=90)<<echo=FALSE, results=hide, fig=TRUE>>=pushViewport(viewport(w=0.9, h=0.9))<<viewports>><<grid>><<plot>><<margin1>><<margin2>>@As a final step, we navigate back to the top-level viewport(i.e., back to the viewport we started in)\footnote{Here we haveused {\tt 0} to indicate ``navigate to the top-level viewport''.When writingcode that could be used by others (i.e., a graphical component thatcould be embedded within something else), it would be necessary tospecify a precise number of viewports to navigate back up. In this case,the number would be {\tt} 2. The {\tt downViewport()} functionreturns the number of viewports it went down, so a generalsolution is of the form: {\tt depth <- downViewport("avp");upViewport(depth)}.}.<<>>=upViewport(0)@So far this example has just shownan alternative way of constructing this sort of plot. The output we havegenerated so far could have been done using \verb|pushViewport()|and \verb|popViewport()|. The difference is that we still haveall of the viewports in the \grid{} viewport tree (and they are addressableby name). This means that a user can seek any of the viewportswe used to construct the plot (by name) and add annotations oruse \verb|grid.locator()| or whatever. For example, a user coulduse the following commandsto add a title.<<annguts, eval=FALSE>>=seekViewport("margin3")grid.text("The user adds a title!", gp=gpar(fontsize=20))<<echo=FALSE, results=hide, fig=TRUE>>=pushViewport(viewport(w=0.9, h=0.9))<<viewports>><<grid>><<plot>><<margin1>><<margin2>><<annguts>>popViewport(0)@% 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}