1% File src/library/grid/vignettes/grobs.Rnw 2% Part of the R package, https://www.R-project.org 3% Copyright 2001-13 Paul Murrell and the R Core Team 4% Distributed under GPL 2 or later 5 6\documentclass[a4paper]{article} 7 8\usepackage{Rd} 9 10% \VignetteIndexEntry{Working with grid grobs} 11% \VignettePackage{grid} 12 13\newcommand{\grid}{\pkg{grid}} 14\newcommand{\grob}{\code{grob}} 15\newcommand{\gTree}{\code{gTree}} 16\newcommand{\gPath}{\code{gPath}} 17\newcommand{\lattice}{\CRANpkg{lattice}} 18 19\setlength{\parindent}{0in} 20\setlength{\parskip}{.1in} 21\setlength{\textwidth}{140mm} 22\setlength{\oddsidemargin}{10mm} 23 24\title{Modifying \grid{} \code{grob}s} 25\author{Paul Murrell} 26 27\begin{document} 28\maketitle 29 30<<echo=FALSE, results=hide>>= 31library(grDevices) 32library(grid) 33ps.options(pointsize = 12) 34options(width = 60) 35@ 36There is a distinction between \grob{}s which are just stored 37in user-level \R{} objects and \grob{}s which represent 38drawn output (i.e., \grob{}s on the \grid{} display list). 39There is a naming convention that \code{grid.*()} functions are 40(mainly) used for their side-effect of producing output or 41modifying existing output (they create/affect \grob{}s on the display list). 42Functions of the form \code{*Grob()} are used for their return value; 43the \grob{} that they create/modify. 44For example, the following creates a \grob{} and then modifies it, but 45performs absolutely no drawing; this is purely manipulating a 46description of a graphical object. 47 48<<>>= 49gl <- linesGrob() 50gl <- editGrob(gl, gp = gpar(col = "green")) 51@ 52 53The next example produces output. A \grob{} is returned, 54but that \grob{} is just a description of the output that was drawn 55and has no direct link to the output. It is possible to access the 56grob representing the output by 57using the \grob{}'s \code{name}. In order to access a \grob{} 58which represents drawn output (i.e., a \grob{} on the display list), 59you must specify a \gPath{}. The \gPath{} should be created using the 60\code{gPath()} function for writing scripts, but in interactive use, it 61is possible to specify the \gPath{} directly as a string. The code below 62shows both approaches. 63 64<<results=hide>>= 65grid.newpage() 66grid.lines(name = "lines") 67grid.edit(gPath("lines"), gp = gpar(col = "pink")) 68grid.edit("lines", gp = gpar(col = "red")) 69@ 70 71Complex graphical objects are provided by the \gTree{} class. A 72\gTree{} is a \grob{} which may have other \grob{}s as children. The 73\code{xaxis} and \code{yaxis} \grob{}s provided by \grid{} are 74examples of \gTree{}s; the children of an axis include a lines \grob{} 75for the tick-marks and a text \grob{} for the tick-mark labels. The 76function \code{childNames()} can be used to list the names of the 77children of a \gTree{}. When dealing with these hierarchical objects, 78more complex \gPath{}s can be used to access children of a \gTree{}. 79In the following example, an x-axis is drawn, then the xaxis itself is 80edited to modify the locations of the tick-marks, then the xaxis's 81text child is edited to modify the location of the labels on the 82tick-marks. 83 84<<results=hide>>= 85grid.newpage() 86pushViewport(viewport(w = .5, h = .5)) 87grid.rect(gp = gpar(col = "grey")) 88grid.xaxis(name = "myxaxis") 89grid.edit("myxaxis", at = 1:4/5) 90grid.edit(gPath("myxaxis", "labels"), y = unit(-1, "lines")) 91 92@ 93 94This next example extends the idea a step further to edit the child of 95a child of a \gTree{}. It also shows the use of the \gTree{} function 96to construct a simple \gTree{} (this is just creating an instance of 97the \gTree{} class -- it is also possible to extend the \gTree{} class 98in order to provide specialised behaviour for drawing and other 99things; more on this later). Finally, the example demonstrates how 100\gPath{}s of depth greater than 1 can be specified directly as a 101string. 102 103<<results=hide>>= 104grid.newpage() 105pushViewport(viewport(w = .5, h = .5)) 106myplot <- 107 gTree(name = "myplot", 108 children = gList(rectGrob(name = "box", gp = gpar(col = "grey")), 109 xaxisGrob(name = "xaxis"))) 110grid.draw(myplot) 111grid.edit("myplot::xaxis", at = 1:10/11) 112grid.edit("myplot::xaxis::labels", label = round(1:10/11, 2)) 113grid.edit("myplot::xaxis::labels", y = unit(-1, "lines")) 114@ 115\code{"grobwidth"} units require a \grob{} to give the width of. 116There are two ways to specify the \grob{}. The following example 117shows the most obvious method of simply supplying a \grob{}. Notice 118that if you modify \code{gt} it will have no effect on the width of 119the drawn rectangle. 120 121<<results=hide>>= 122grid.newpage() 123gt <- grid.text("Hi there") 124grid.rect(width = unit(1, "grobwidth", gt)) 125@ 126In order to allow a \code{"grobwidth"} unit to track changes in the 127\grob{}, it is possible to specify a \gPath{} rather than a \grob{} as 128the data for a \code{"grobwidth"} unit. The following example 129modifies the previous example to use a \gPath{}. Now, the width of 130the rectangle changes when the width of the underlying \grob{} 131changes. 132 133<<results=hide>>= 134grid.newpage() 135gt <- grid.text("Hi there", name = "sometext") 136grid.rect(width = unit(1, "grobwidth", "sometext")) 137grid.edit("sometext", label = "Something different") 138@ 139 140One issue in the evaluation of \code{"grobwidth"} units involves 141establishing the correct ``context'' for a \grob{} when determining its width 142(if a \grob{} has a viewport in its \code{vp} slot then that viewport gets 143pushed before the \grob{} is drawn; that viewport should also be pushed 144when determining the width 145of the \grob{}). 146To achieve this there are 147\code{preDrawDetails()}, \code{drawDetails()}, and \code{postDrawDetails()} 148generic functions. 149(suggestions for better names welcome!). The idea is that 150pushing and popping of viewports should occur in the \code{pre} and 151\code{post} generics, and any actual drawing happens in the main 152\code{drawDetails()} generic. This allows the code that calculates a 153\grob{} width to call the \code{preDrawDetails()} in order to establish 154the context in which the \grob{} would be drawn before calculating its 155width. The following example shows a test case; a \grob{} is created 156(extending to a new class to allow specific methods to be written), 157and methods are provided which establish a particular context 158for drawing the \grob{}. These methods are used both in the drawing 159of the \grob{} and in the calculation of the \grob{}'s width (when 160drawing a bounding rectangle). 161 162<<results=hide>>= 163grid.newpage() 164mygrob <- grob(name = "mygrob", cl = "mygrob") 165preDrawDetails.mygrob <- function(x) 166 pushViewport(viewport(gp = gpar(fontsize = 20))) 167 168drawDetails.mygrob <- function(x, recording = TRUE) 169 grid.draw(textGrob("hi there"), recording = FALSE) 170 171postDrawDetails.mygrob <- function(x) popViewport() 172 173widthDetails.mygrob <- function(x) unit(1, "strwidth", "hi there") 174 175grid.draw(mygrob) 176grid.rect(width = unit(1, "grobwidth", mygrob)) 177@ 178 179This next example shows a slightly different test case where the 180standard \code{preDrawDetails()} and \code{postDrawDetails()} methods 181are used, but the \grob{} does have a \code{vp} slot so these methods 182do something. Another interesting feature of this example is the 183slightly more complex \gTree{} that is created. The \gTree{} has a 184\code{childrenvp} specified. When the \gTree{} is drawn, this 185viewport is pushed and then ``up''ed before the children of the 186\gTree{} are drawn. This means that the children of the \gTree{} can 187specify a \code{vpPath} to the viewport they should be in. This 188allows the parent \gTree{} to create a suite of viewports and then 189children of the \gTree{} select which one they want -- this can be 190more efficient than having each child push and pop the viewports it 191needs, especially if several children are drawn within the same 192viewport. Another, more realistic example of this is given later. 193 194<<results=hide>>= 195grid.newpage() 196mygtree <- gTree(name = "mygrob", 197 childrenvp = viewport(name = "labelvp", 198 gp = gpar(fontsize = 20)), 199 children = gList(textGrob("hi there", name = "label", 200 vp = "labelvp")), 201 cl = "mygtree") 202widthDetails.mygtree <- function(x) 203 unit(1, "grobwidth", getGrob(x, "label")) 204 205grid.draw(mygtree) 206grid.rect(width = unit(1, "grobwidth", mygtree)) 207@ 208 209Constructing a description of a \code{frame} \grob{} must be 210done via \code{packGrob()} and \code{placeGrob()}. The following example 211shows the construction of a simple frame consisting of two 212equal-size columns. 213 214<<results = hide>>= 215grid.newpage() 216fg <- frameGrob(layout = grid.layout(1, 2)) 217fg <- placeGrob(fg, textGrob("Hi there"), col = 1) 218fg <- placeGrob(fg, rectGrob(), col = 2) 219grid.draw(fg) 220@ 221 222This next example constructs a slightly fancier frame using packing. 223 224<<results=hide>>= 225grid.newpage() 226pushViewport(viewport(layout = grid.layout(2, 2))) 227drawIt <- function(row, col) { 228 pushViewport(viewport(layout.pos.col = col, layout.pos.row = row)) 229 grid.rect(gp = gpar(col = "grey")) 230 grid.draw(fg) 231 upViewport() 232} 233fg <- frameGrob() 234fg <- packGrob(fg, textGrob("Hi there")) 235fg <- placeGrob(fg, rectGrob()) 236drawIt(1, 1) 237fg <- packGrob(fg, textGrob("Hello again"), side = "right") 238drawIt(1, 2) 239fg <- packGrob(fg, rectGrob(), side = "right", width = unit(1, "null")) 240drawIt(2, 2) 241@ 242 243In order to allow frames to update when the objects packed within them 244are modified, there is a \code{dynamic} argument to \code{packGrob()} 245(and \code{grid.pack()}). The following extends the previous example 246to show how this might be used. Another feature of this example is 247the demonstration of ``non-strict'' searching that occurs in the 248\code{grid.edit()} call; the \grob{} called \code{"midtext"} is not at 249the top-level, but is still found. Something like 250\code{grid.get("midtext", strict = TRUE)} would fail. 251 252<<results=hide>>= 253grid.newpage() 254fg <- frameGrob() 255fg <- packGrob(fg, textGrob("Hi there")) 256fg <- placeGrob(fg, rectGrob()) 257fg <- packGrob(fg, textGrob("Hello again", name = "midtext"), 258 side = "right", dynamic = TRUE) 259fg <- packGrob(fg, rectGrob(), side = "right", width = unit(1, "null")) 260grid.draw(fg) 261grid.edit("midtext", label = "something much longer") 262@ 263 264There have been a few examples already which have involved creating a 265\gTree{}. This next example explicitly demonstrates this technique. 266A \gTree{} is created with two important components. The 267\code{childrenvp} is a \code{vpTree} consisting of a 268\code{"plotRegion"} viewport to provide margins around a plot and a 269\code{"dataRegion"} viewport to provide x- and y-scales. The 270\code{"dataRegion"} gets pushed within the \code{"plotRegion"} and 271both are pushed and then ``up''ed before the children are drawn. The 272\code{children} of the \gTree{} are an \code{xaxis} and a \code{yaxis} 273both drawn within the \code{"dataRegion"}, and a \code{rect} drawn 274around the border of the \code{"plotRegion"}. A further feature of 275this example is the use of the \code{addGrob()} and 276\code{removeGrob()} functions to modify the \gTree{}. The first 277modification involves adding a new child to the \gTree{} which is a 278set of points drawn within the \code{"dataRegion"}. The second 279modification involves adding another set of points with a different 280symbol (NOTE that this second set of points is given a name so that it 281is easy to identify this set amongst the children of the \gTree{}). 282The final modification is to remove the second set of points from the 283\gTree{}. 284 285<<results=hide>>= 286grid.newpage() 287pushViewport(viewport(layout = grid.layout(2, 2))) 288drawIt <- function(row, col) { 289 pushViewport(viewport(layout.pos.col = col, layout.pos.row = row)) 290 grid.rect(gp = gpar(col = "grey")) 291 grid.draw(gplot) 292 upViewport() 293} 294gplot <- gTree(x = NULL, y = NULL, 295 childrenvp = vpTree( 296 plotViewport(c(5, 4, 4, 2), name = "plotRegion"), 297 vpList(viewport(name = "dataRegion"))), 298 children = gList( 299 xaxisGrob(vp = "plotRegion::dataRegion"), 300 yaxisGrob(vp = "plotRegion::dataRegion"), 301 rectGrob(vp = "plotRegion"))) 302drawIt(1, 1) 303gplot <- addGrob(gplot, pointsGrob(vp = "plotRegion::dataRegion")) 304drawIt(1, 2) 305gplot <- addGrob(gplot, pointsGrob(name = "data1", pch = 2, 306 vp = "plotRegion::dataRegion")) 307drawIt(2, 1) 308gplot <- removeGrob(gplot, "data1") 309drawIt(2, 2) 310@ 311 312This next example provides a simple demonstration of saving and 313loading \grid{} \grob{}s. It is also a nice demonstration that 314\grob{}s copy like normal \R{} objects. 315 316<<results=hide>>= 317gplot <- gTree(x = NULL, y = NULL, 318 childrenvp = vpTree( 319 plotViewport(c(5, 4, 4, 2), name = "plotRegion"), 320 vpList(viewport(name = "dataRegion"))), 321 children = gList( 322 xaxisGrob(vp = "plotRegion::dataRegion"), 323 yaxisGrob(vp = "plotRegion::dataRegion"), 324 rectGrob(vp = "plotRegion"))) 325save(gplot, file = "gplot1") 326gplot <- addGrob(gplot, pointsGrob(vp = "plotRegion::dataRegion")) 327save(gplot, file = "gplot2") 328grid.newpage() 329pushViewport(viewport(layout = grid.layout(1, 2))) 330pushViewport(viewport(layout.pos.col = 1)) 331load("gplot1") 332grid.draw(gplot) 333popViewport() 334pushViewport(viewport(layout.pos.col = 2)) 335load("gplot2") 336grid.draw(gplot) 337popViewport() 338@ 339 340This next example just demonstrates that it is possible to use a 341\gPath{} to access the children of a \gTree{} when editing. This is 342the \code{editGrob()} equivalent of an earlier example that used 343\code{grid.edit()}. One useful application of this API is the ability 344to modify the appearance of quite precise elements of a large, complex 345graphical object by editing the \code{gp} slot of a child (of a child 346\ldots{}) of a \gTree{}. 347 348<<results = hide>>= 349myplot <- gTree(name = "myplot", 350 children = gList( 351 rectGrob(name = "box", gp = gpar(col = "grey")), 352 xaxisGrob(name = "xaxis"))) 353myplot <- editGrob(myplot, gPath = "xaxis", at = 1:10/11) 354myplot <- editGrob(myplot, gPath = "xaxis::labels", label = round(1:10/11, 2)) 355myplot <- editGrob(myplot, gPath = "xaxis::labels", y = unit(-1, "lines")) 356grid.newpage() 357pushViewport(viewport(w = .5, h = .5)) 358grid.draw(myplot) 359 360@ 361The following example demonstrates 362the use of the \code{getGrob()} and \code{grid.get()} (along with 363\gPath{}s) to access \grob{}s. 364 365<<results = hide>>= 366myplot <- gTree(name = "myplot", 367 children = gList( 368 rectGrob(name = "box", gp = gpar(col = "grey")), 369 xaxisGrob(name = "xaxis"))) 370getGrob(myplot, "xaxis") 371myplot <- editGrob(myplot, gPath="xaxis", at=1:10/11) 372getGrob(myplot, "xaxis::labels") 373grid.newpage() 374pushViewport(viewport(w=.5, h=.5)) 375grid.draw(myplot) 376grid.get("myplot") 377grid.get("myplot::xaxis") 378grid.get("myplot::xaxis::labels") 379@ 380 381There is also an API for (re)setting children of a \gTree{} 382or any drawn \grob{}. This is not intended for general user use, 383but provides a simple way for developers to perform modifications 384to the structure of a \gTree{} by doing something like \ldots{} 385 386\begin{verbatim} 387grob <- getGrob(<spec>) 388<modify grob> 389setGrob(<spec>, grob) 390\end{verbatim} 391 392This approach is used in the implementation of packing and placing grobs. 393The following example shows some simple usage of the 394\code{setGrob()} and \code{grid.set()} functions to replace 395children of a \gTree{} with different \grob{}s. NOTE that currently 396such replacement can only occur if the name of the new \grob{} 397is the same as the name of the old \grob{}. 398 399<<results=hide>>= 400myplot <- gTree(name = "myplot", 401 children = gList(rectGrob(name = "box", gp = gpar(col = "grey")), 402 xaxisGrob(name = "xaxis"))) 403myplot <- setGrob(myplot, "xaxis", rectGrob(name = "xaxis")) 404grid.newpage() 405pushViewport(viewport(w = .5, h = .5)) 406grid.draw(myplot) 407grid.set("myplot::xaxis", xaxisGrob(name = "xaxis", at = 1:3/4)) 408grid.set("myplot::xaxis::labels", 409 textGrob(name = "labels", x = unit(1:3/4, "native"), 410 y = unit(-1, "lines"), label = letters[1:3])) 411myplot <- setGrob(grid.get("myplot"), "xaxis::labels", 412 circleGrob(name = "labels")) 413grid.newpage() 414pushViewport(viewport(w = .5, h = .5)) 415grid.draw(myplot) 416@ 417 418This next example just shows more complex use of the add/remove 419facilities for modifying \grob{}s. Again, \code{addGrob()} and 420\code{removeGrob()} are for constructing descriptions of graphical 421objects and \code{grid.add()} and \code{grid.remove()} are for 422modifying drawn output. Of particular note are the last two lines 423involving \code{grid.remove()}. The first point is that there are 424multiple \grob{}s on the display list with the same name. The example 425only affects the first one it finds; this could easily be extended to 426affect the display list ``globally'' (for children of \gTree{}s, there 427cannot be multiple children with the same name so the issue does not 428arise). The last line is interesting because it actually erases the 429\grob{} named \code{"plot1"} from the display list altogether (well, 430the first instance on the display list of a \grob{} called 431\code{"plot1"} anyway). 432 433<<results=hide>>= 434drawIt <- function(row, col) { 435 pushViewport(viewport(layout.pos.col = col, layout.pos.row = row)) 436 grid.rect(gp = gpar(col = "grey")) 437 grid.draw(gplot) 438 upViewport() 439} 440gplot <- gTree(name = "plot1", 441 childrenvp = vpTree( 442 plotViewport(c(5, 4, 4, 2), name = "plotRegion"), 443 vpList(viewport(name = "dataRegion"))), 444 children = gList( 445 xaxisGrob(name = "xaxis", vp = "plotRegion::dataRegion"), 446 yaxisGrob(name = "yaxis", vp = "plotRegion::dataRegion"), 447 rectGrob(name = "box", vp = "plotRegion"))) 448grid.newpage() 449pushViewport(viewport(layout = grid.layout(2, 2))) 450drawIt(1, 1) 451grid.add("plot1", pointsGrob(0.5, 0.5, name = "data1", 452 vp = "plotRegion::dataRegion")) 453grid.add("plot1::xaxis", 454 textGrob("X Axis", y = unit(-2, "lines"), name = "xlab")) 455grid.edit("plot1::xaxis::xlab", y = unit(-3, "lines")) 456gplot <- grid.get("plot1") 457gplot <- addGrob(gplot, gPath = "yaxis", 458 textGrob("Y Axis", x = unit(-3, "lines"), rot = 90, 459 name = "ylab")) 460drawIt(1, 2) 461gplot <- removeGrob(gplot, "xaxis::xlab") 462drawIt(2, 1) 463grid.remove("plot1::data1") 464grid.remove("plot1") 465@ 466 467The next example is just a \code{grid.place()} and \code{grid.pack()} 468equivalent of an earlier example involving 469\code{placeGrob()} and \code{packGrob()}. The interesting feature is 470that each action is reflected in the output as it occurs. 471 472<<results=hide>>= 473grid.newpage() 474grid.frame(name = "myframe", layout = grid.layout(1, 2)) 475grid.place("myframe", textGrob("Hi there"), col = 1) 476grid.place("myframe", rectGrob(), col = 2) 477grid.newpage() 478grid.frame(name = "frame2") 479grid.pack("frame2", textGrob("Hi there")) 480grid.place("frame2", rectGrob()) 481grid.pack("frame2", textGrob("Hello again"), side = "right") 482grid.pack("frame2", rectGrob(), side = "right", width = unit(1, "null")) 483@ 484\end{document} 485 486 487 488