Rev 49817 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\documentclass[a4paper]{article}%\VignetteIndexEntry{Working with grid grobs}%\VignettePackage{grid}\newcommand{\code}[1]{\texttt{#1}}\newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}}\newcommand{\grid}{\pkg{grid}}\newcommand{\grob}{\code{grob}}\newcommand{\gTree}{\code{gTree}}\newcommand{\gPath}{\code{gPath}}\newcommand{\lattice}{\pkg{lattice}}\newcommand{\R}{{\sffamily R}}\setlength{\parindent}{0in}\setlength{\parskip}{.1in}\setlength{\textwidth}{140mm}\setlength{\oddsidemargin}{10mm}\title{Modifying \grid{} \code{grob}s}\author{Paul Murrell}\begin{document}\maketitle<<echo=FALSE, results=hide>>=library(grDevices)library(grid)ps.options(pointsize=12)options(width=60)@There is a distinction between \grob{}s which are just storedin user-level \R{} objects and \grob{}s which representdrawn output (i.e., \grob{}s on the \grid{} display list).There is a naming convention that \code{grid.*()} functions are(mainly) used for their side-effect of producing output ormodifying existing output (they create/affect \grob{}s on the display list).Functions of the form \code{*Grob()} are used for their return value;the \grob{} that they create/modify.For example, the following creates a \grob{} and then modifies it, butperforms absolutely no drawing; this is purely manipulating adescription of a graphical object.<<>>=gl <- linesGrob()gl <- editGrob(gl, gp=gpar(col="green"))@The next example produces output. A \grob{} is returned,but that \grob{} is just a description of the output that was drawnand has no direct link to the output. It is possible to access thegrob representing the output byusing the \grob{}'s \code{name}. In order to access a \grob{}which represents drawn output (i.e., a \grob{} on the display list),you must specify a \gPath{}. The \gPath{} should be created using the\code{gPath()} function for writing scripts, but in interactive use, itis possible to specify the \gPath{} directly as a string. The code belowshows both approaches.<<results=hide>>=grid.newpage()grid.lines(name="lines")grid.edit(gPath("lines"), gp=gpar(col="pink"))grid.edit("lines", gp=gpar(col="red"))@Complex graphical objects are provided by the \gTree{} class.A \gTree{} is a \grob{} which may have other \grob{}s as children.The \code{xaxis} and \code{yaxis} \grob{}s provided by \grid{} areexamples of \gTree{}s; the children of an axis includea lines \grob{} for the tick-marksand a text \grob{} for the tick-mark labels.The function \code{childNames()} can be used to list thenames of the children of a \gTree{}.When dealing with these hierarchicalobjects, more complex \gPath{}s can be used to access childrenof a \gTree{}. In the following example, an x-axis is drawn, thenthe xaxis itself is edited to modify the locations of the tick-marks, thenthe xaxis's text child is edited to modifythe location of the labels on the tick-marks.<<results=hide>>=grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.rect(gp=gpar(col="grey"))grid.xaxis(name="myxaxis")grid.edit("myxaxis", at=1:4/5)grid.edit(gPath("myxaxis", "labels"), y=unit(-1, "lines"))@This next example extends the idea a step further to editthe child of a child of a \gTree{}. It also shows the use ofthe \gTree{} function to construct a simple \gTree{}(this is just creating an instance of the \gTree{} class -- it isalso possible to extend the \gTree{} class in order to providespecialised behaviour for drawing and other things; more on thislater). Finally, the example demonstrates how \gPath{}s of depthgreater than 1 canbe specified directly as a string.<<results=hide>>=grid.newpage()pushViewport(viewport(w=.5, h=.5))myplot <- gTree(name="myplot",children=gList(rectGrob(name="box", gp=gpar(col="grey")),xaxisGrob(name="xaxis")))grid.draw(myplot)grid.edit("myplot::xaxis", at=1:10/11)grid.edit("myplot::xaxis::labels", label=round(1:10/11, 2))grid.edit("myplot::xaxis::labels", y=unit(-1, "lines"))@\code{"grobwidth"} units require a \grob{} to give the width of.There are two ways to specify the \grob{}.The following example shows the most obvious method ofsimply supplying a \grob{}. Notice that if you modify\code{gt} it will have no effect on the width of the drawn rectangle.<<results=hide>>=grid.newpage()gt <- grid.text("Hi there")grid.rect(width=unit(1, "grobwidth", gt))@In order to allow a \code{"grobwidth"} unit to track changesin the \grob{},it is possible to specify a \gPath{} rather than a \grob{}as the data for a \code{"grobwidth"} unit. The following examplemodifies the previous example to use a \gPath{}. Now, the widthof the rectangle changes when the width of the underlying \grob{}changes.<<results=hide>>=grid.newpage()gt <- grid.text("Hi there", name="sometext")grid.rect(width=unit(1, "grobwidth", "sometext"))grid.edit("sometext", label="Something different")@One issue in the evaluation of \code{"grobwidth"} units involvesestablishing the correct ``context'' for a \grob{} when determining its width(if a \grob{} has a viewport in its \code{vp} slot then that viewport getspushed before the \grob{} is drawn; that viewport should also be pushedwhen determining the widthof the \grob{}).To achieve this there are\code{preDrawDetails()}, \code{drawDetails()}, and \code{postDrawDetails()}generic functions.(suggestions for better names welcome!). The idea is thatpushing and popping of viewports should occur in the \code{pre} and\code{post} generics, and any actual drawing happens in the main\code{drawDetails()} generic. This allows the code that calculates a\grob{} width to call the \code{preDrawDetails()} in order to establishthe context in which the \grob{} would be drawn before calculating itswidth. The following example shows a test case; a \grob{} is created(extending to a new class to allow specific methods to be written),and methods are provided which establish a particular contextfor drawing the \grob{}. These methods are used both in the drawingof the \grob{} and in the calculation of the \grob{}'s width (whendrawing a bounding rectangle).<<results=hide>>=grid.newpage()mygrob <- grob(name="mygrob", cl="mygrob")preDrawDetails.mygrob <- function(x) {pushViewport(viewport(gp=gpar(fontsize=20)))}drawDetails.mygrob <- function(x, recording=TRUE) {grid.draw(textGrob("hi there"), recording=FALSE)}postDrawDetails.mygrob <- function(x) {popViewport()}widthDetails.mygrob <- function(x) {unit(1, "strwidth", "hi there")}grid.draw(mygrob)grid.rect(width=unit(1, "grobwidth", mygrob))@This next example shows a slightly different test case where thestandard \code{preDrawDetails()} and \code{postDrawDetails()} methodsare used, but the \grob{} does have a \code{vp} slot so thesemethods do something. Another interesting feature of this exampleis the slightly more complex \gTree{} that is created. The\gTree{} has a \code{childrenvp} specified. When the \gTree{} is drawn,this viewportis pushed and then ``up''ed before the children of the \gTree{} are drawn.This means that the children of the \gTree{} can specify a \code{vpPath}to the viewport they should be in. This allows the parent\gTree{} to create a suite of viewports and then childrenof the \gTree{} select which one they want -- this can be moreefficient than having each child push and pop the viewportsit needs, especially if several children are drawn within the sameviewport. Another, more realistic example of this is given later.<<results=hide>>=grid.newpage()mygtree <- gTree(name="mygrob",childrenvp=viewport(name="labelvp", gp=gpar(fontsize=20)),children=gList(textGrob("hi there", name="label",vp="labelvp")),cl="mygtree")widthDetails.mygtree <- function(x) {unit(1, "grobwidth", getGrob(x, "label"))}grid.draw(mygtree)grid.rect(width=unit(1, "grobwidth", mygtree))@Constructing a description of a \code{frame} \grob{} must bedone via \code{packGrob()} and \code{placeGrob()}. The following exampleshows the construction of a simple frame consisting of twoequal-size columns.<<results=hide>>=grid.newpage()fg <- frameGrob(layout=grid.layout(1, 2))fg <- placeGrob(fg, textGrob("Hi there"), col=1)fg <- placeGrob(fg, rectGrob(), col=2)grid.draw(fg)@This next example constructs a slightly fancier frame usingpacking.<<results=hide>>=grid.newpage()pushViewport(viewport(layout=grid.layout(2, 2)))drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col=col, layout.pos.row=row))grid.rect(gp=gpar(col="grey"))grid.draw(fg)upViewport()}fg <- frameGrob()fg <- packGrob(fg, textGrob("Hi there"))fg <- placeGrob(fg, rectGrob())drawIt(1, 1)fg <- packGrob(fg, textGrob("Hello again"), side="right")drawIt(1, 2)fg <- packGrob(fg, rectGrob(), side="right", width=unit(1, "null"))drawIt(2, 2)@In order to allow frames to update when the objects packed within themare modified, there is a \code{dynamic} argument to\code{packGrob()} (and \code{grid.pack()}). The followingextends the previous example to show how this might be used.Another feature of this example is the demonstration of``non-strict'' searching that occurs in the \code{grid.edit()}call; the \grob{} called \code{"midtext"} is not at the top-level,but is still found. Something like\code{grid.get("midtext", strict=TRUE)} would fail.<<results=hide>>=grid.newpage()fg <- frameGrob()fg <- packGrob(fg, textGrob("Hi there"))fg <- placeGrob(fg, rectGrob())fg <- packGrob(fg, textGrob("Hello again", name="midtext"),side="right", dynamic=TRUE)fg <- packGrob(fg, rectGrob(), side="right", width=unit(1, "null"))grid.draw(fg)grid.edit("midtext", label="something much longer")@There have been a few examples already which have involved creating a \gTree{}.This next example explicitly demonstrates this technique. A \gTree{}is created with two important components. The \code{childrenvp} isa \code{vpTree} consisting of a \code{"plotRegion"} viewport to providemargins around a plot and a \code{"dataRegion"} viewport to providex- and y-scales. The \code{"dataRegion"} gets pushed within the\code{"plotRegion"} and both are pushed and then ``up''ed before thechildren are drawn. The \code{children} of the \gTree{} arean \code{xaxis} and a \code{yaxis} both drawn within the \code{"dataRegion"},and a \code{rect} drawn around the border of the \code{"plotRegion"}.A further feature of this example is the use of the \code{addGrob()}and \code{removeGrob()} functions to modify the \gTree{}. The firstmodification involves adding a new child to the \gTree{} which is aset of points drawn within the \code{"dataRegion"}. The secondmodification involves adding another set of points with a differentsymbol (NOTE that this second set of points is given a name so thatit is easy to identify this set amongst the children of the \gTree{}).The final modification is to remove the second set of points from the\gTree{}.<<results=hide>>=grid.newpage()pushViewport(viewport(layout=grid.layout(2, 2)))drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col=col, layout.pos.row=row))grid.rect(gp=gpar(col="grey"))grid.draw(gplot)upViewport()}gplot <- gTree(x=NULL, y=NULL,childrenvp=vpTree(plotViewport(c(5, 4, 4, 2), name="plotRegion"),vpList(viewport(name="dataRegion"))),children=gList(xaxisGrob(vp="plotRegion::dataRegion"),yaxisGrob(vp="plotRegion::dataRegion"),rectGrob(vp="plotRegion")))drawIt(1, 1)gplot <- addGrob(gplot, pointsGrob(vp="plotRegion::dataRegion"))drawIt(1, 2)gplot <- addGrob(gplot, pointsGrob(name="data1", pch=2,vp="plotRegion::dataRegion"))drawIt(2, 1)gplot <- removeGrob(gplot, "data1")drawIt(2, 2)@This next example provides a simpledemonstration of saving and loading \grid{} \grob{}s. It is also a nicedemonstration that \grob{}s copy like normal\R{} objects.<<results=hide>>=gplot <- gTree(x=NULL, y=NULL,childrenvp=vpTree(plotViewport(c(5, 4, 4, 2), name="plotRegion"),vpList(viewport(name="dataRegion"))),children=gList(xaxisGrob(vp="plotRegion::dataRegion"),yaxisGrob(vp="plotRegion::dataRegion"),rectGrob(vp="plotRegion")))save(gplot, file="gplot1")gplot <- addGrob(gplot, pointsGrob(vp="plotRegion::dataRegion"))save(gplot, file="gplot2")grid.newpage()pushViewport(viewport(layout=grid.layout(1, 2)))pushViewport(viewport(layout.pos.col=1))load("gplot1")grid.draw(gplot)popViewport()pushViewport(viewport(layout.pos.col=2))load("gplot2")grid.draw(gplot)popViewport()@This next example just demonstrates that it is possible to use a\gPath{} to access the children of a \gTree{} when editing.This is the \code{editGrob()} equivalent of an earlier examplethat used \code{grid.edit()}. Oneuseful application of this API is the ability to modify theappearance of quite precise elements of a large, complex graphicalobject by editing the \code{gp} slot of a child (of a child ...) ofa \gTree{}.<<results=hide>>=myplot <- gTree(name="myplot",children=gList(rectGrob(name="box", gp=gpar(col="grey")),xaxisGrob(name="xaxis")))myplot <- editGrob(myplot, gPath="xaxis", at=1:10/11)myplot <- editGrob(myplot, gPath="xaxis::labels", label=round(1:10/11, 2))myplot <- editGrob(myplot, gPath="xaxis::labels", y=unit(-1, "lines"))grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.draw(myplot)@The following example demonstratesthe use of the \code{getGrob()} and \code{grid.get()} (along with\gPath{}s) to access \grob{}s.<<results=hide>>=myplot <- gTree(name="myplot",children=gList(rectGrob(name="box", gp=gpar(col="grey")),xaxisGrob(name="xaxis")))getGrob(myplot, "xaxis")myplot <- editGrob(myplot, gPath="xaxis", at=1:10/11)getGrob(myplot, "xaxis::labels")grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.draw(myplot)grid.get("myplot")grid.get("myplot::xaxis")grid.get("myplot::xaxis::labels")@There is also an API for (re)setting children of a \gTree{}or any drawn \grob{}. This is not intended for general user use,but provides a simple way for developers to perform modificationsto the structure of a \gTree{} by doing something like ...\begin{verbatim}grob <- getGrob(<spec>)<modify grob>setGrob(<spec>, grob)\end{verbatim}This approach is used in the implementation of packing and placing grobs.The following example shows some simple usage of the\code{setGrob()} and \code{grid.set()} functions to replacechildren of a \gTree{} with different \grob{}s. NOTE that currentlysuch replacement can only occur if the name of the new \grob{}is the same as the name of the old \grob{}.<<results=hide>>=myplot <- gTree(name="myplot",children=gList(rectGrob(name="box", gp=gpar(col="grey")),xaxisGrob(name="xaxis")))myplot <- setGrob(myplot, "xaxis", rectGrob(name="xaxis"))grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.draw(myplot)grid.set("myplot::xaxis", xaxisGrob(name="xaxis", at=1:3/4))grid.set("myplot::xaxis::labels",textGrob(name="labels", x=unit(1:3/4, "native"),y=unit(-1, "lines"), label=letters[1:3]))myplot <- setGrob(grid.get("myplot"), "xaxis::labels",circleGrob(name="labels"))grid.newpage()pushViewport(viewport(w=.5, h=.5))grid.draw(myplot)@This next example just shows more complex use of the add/removefacilities for modifying \grob{}s. Again, \code{addGrob()} and\code{removeGrob()} are for constructing descriptions of graphicalobjects and \code{grid.add()} and \code{grid.remove()} are formodifying drawn output. Of particular note are the last twolines involving \code{grid.remove()}. The first point is thatthere are multiple \grob{}s on the display list with the samename. The example only affects the first one it finds; this couldeasily be extended to affect the display list ``globally'' (forchildren of \gTree{}s, there cannot be multiple children with thesame name so the issue does not arise). The last line is interestingbecause it actually erases the \grob{} named \code{"plot1"} fromthe display list altogether (well, the first instance on the display list ofa \grob{} called \code{"plot1"} anyway).<<results=hide>>=drawIt <- function(row, col) {pushViewport(viewport(layout.pos.col=col, layout.pos.row=row))grid.rect(gp=gpar(col="grey"))grid.draw(gplot)upViewport()}gplot <- gTree(name="plot1",childrenvp=vpTree(plotViewport(c(5, 4, 4, 2), name="plotRegion"),vpList(viewport(name="dataRegion"))),children=gList(xaxisGrob(name="xaxis", vp="plotRegion::dataRegion"),yaxisGrob(name="yaxis", vp="plotRegion::dataRegion"),rectGrob(name="box", vp="plotRegion")))grid.newpage()pushViewport(viewport(layout=grid.layout(2, 2)))drawIt(1, 1)grid.add("plot1", pointsGrob(0.5, 0.5, name="data1",vp="plotRegion::dataRegion"))grid.add("plot1::xaxis", textGrob("X Axis", y=unit(-2, "lines"), name="xlab"))grid.edit("plot1::xaxis::xlab", y=unit(-3, "lines"))gplot <- grid.get("plot1")gplot <- addGrob(gplot, gPath="yaxis",textGrob("Y Axis", x=unit(-3, "lines"), rot=90, name="ylab"))drawIt(1, 2)gplot <- removeGrob(gplot, "xaxis::xlab")drawIt(2, 1)grid.remove("plot1::data1")grid.remove("plot1")@The next example is just a \code{grid.place()} and \code{grid.pack()}equivalent of an earlier example involving\code{placeGrob()} and \code{packGrob()}. The interesting feature isthat each action is reflected in the output as it occurs.<<results=hide>>=grid.newpage()grid.frame(name="myframe", layout=grid.layout(1, 2))grid.place("myframe", textGrob("Hi there"), col=1)grid.place("myframe", rectGrob(), col=2)grid.newpage()grid.frame(name="frame2")grid.pack("frame2", textGrob("Hi there"))grid.place("frame2", rectGrob())grid.pack("frame2", textGrob("Hello again"), side="right")grid.pack("frame2", rectGrob(), side="right", width=unit(1, "null"))@% 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}