| % File src/library/grid/vignettes/grobs.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{Working with grid grobs} |
| % \VignettePackage{grid} |
| |
| \newcommand{\grid}{\pkg{grid}} |
| \newcommand{\grob}{\code{grob}} |
| \newcommand{\gTree}{\code{gTree}} |
| \newcommand{\gPath}{\code{gPath}} |
| \newcommand{\lattice}{\CRANpkg{lattice}} |
| |
| \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 stored |
| in user-level \R{} objects and \grob{}s which represent |
| drawn 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 or |
| modifying 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, but |
| performs absolutely no drawing; this is purely manipulating a |
| description 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 drawn |
| and has no direct link to the output. It is possible to access the |
| grob representing the output by |
| using 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, it |
| is possible to specify the \gPath{} directly as a string. The code below |
| shows 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{} are |
| examples of \gTree{}s; the children of an axis include a lines \grob{} |
| for the tick-marks and a text \grob{} for the tick-mark labels. The |
| function \code{childNames()} can be used to list the names of the |
| children of a \gTree{}. When dealing with these hierarchical objects, |
| more complex \gPath{}s can be used to access children of a \gTree{}. |
| In the following example, an x-axis is drawn, then the xaxis itself is |
| edited to modify the locations of the tick-marks, then the xaxis's |
| text child is edited to modify the 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 edit the child of |
| a child of a \gTree{}. It also shows the use of the \gTree{} function |
| to construct a simple \gTree{} (this is just creating an instance of |
| the \gTree{} class -- it is also possible to extend the \gTree{} class |
| in order to provide specialised behaviour for drawing and other |
| things; more on this later). Finally, the example demonstrates how |
| \gPath{}s of depth greater than 1 can be 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 of simply 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 changes in the |
| \grob{}, it is possible to specify a \gPath{} rather than a \grob{} as |
| the data for a \code{"grobwidth"} unit. The following example |
| modifies the previous example to use a \gPath{}. Now, the width of |
| 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 involves |
| establishing the correct ``context'' for a \grob{} when determining its width |
| (if a \grob{} has a viewport in its \code{vp} slot then that viewport gets |
| pushed before the \grob{} is drawn; that viewport should also be pushed |
| when determining the width |
| of the \grob{}). |
| To achieve this there are |
| \code{preDrawDetails()}, \code{drawDetails()}, and \code{postDrawDetails()} |
| generic functions. |
| (suggestions for better names welcome!). The idea is that |
| pushing 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 establish |
| the context in which the \grob{} would be drawn before calculating its |
| width. 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 context |
| for drawing the \grob{}. These methods are used both in the drawing |
| of the \grob{} and in the calculation of the \grob{}'s width (when |
| drawing 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 the |
| standard \code{preDrawDetails()} and \code{postDrawDetails()} methods |
| are used, but the \grob{} does have a \code{vp} slot so these methods |
| do something. Another interesting feature of this example is the |
| slightly more complex \gTree{} that is created. The \gTree{} has a |
| \code{childrenvp} specified. When the \gTree{} is drawn, this |
| viewport is 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 |
| children of the \gTree{} select which one they want -- this can be |
| more efficient than having each child push and pop the viewports it |
| needs, especially if several children are drawn within the same |
| viewport. 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 be |
| done via \code{packGrob()} and \code{placeGrob()}. The following example |
| shows the construction of a simple frame consisting of two |
| equal-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 using packing. |
| |
| <<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 them |
| are modified, there is a \code{dynamic} argument to \code{packGrob()} |
| (and \code{grid.pack()}). The following extends 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} is a \code{vpTree} consisting of a |
| \code{"plotRegion"} viewport to provide margins around a plot and a |
| \code{"dataRegion"} viewport to provide x- and y-scales. The |
| \code{"dataRegion"} gets pushed within the \code{"plotRegion"} and |
| both are pushed and then ``up''ed before the children are drawn. The |
| \code{children} of the \gTree{} are an \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 first |
| modification involves adding a new child to the \gTree{} which is a |
| set of points drawn within the \code{"dataRegion"}. The second |
| modification involves adding another set of points with a different |
| symbol (NOTE that this second set of points is given a name so that it |
| 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 simple demonstration of saving and |
| loading \grid{} \grob{}s. It is also a nice demonstration 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 example that used |
| \code{grid.edit()}. One useful application of this API is the ability |
| to modify the appearance of quite precise elements of a large, complex |
| graphical object by editing the \code{gp} slot of a child (of a child |
| \ldots{}) of a \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 demonstrates |
| the 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 modifications |
| to the structure of a \gTree{} by doing something like \ldots{} |
| |
| \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 replace |
| children of a \gTree{} with different \grob{}s. NOTE that currently |
| such 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/remove |
| facilities for modifying \grob{}s. Again, \code{addGrob()} and |
| \code{removeGrob()} are for constructing descriptions of graphical |
| objects and \code{grid.add()} and \code{grid.remove()} are for |
| modifying drawn output. Of particular note are the last two lines |
| involving \code{grid.remove()}. The first point is that there are |
| multiple \grob{}s on the display list with the same name. The example |
| only affects the first one it finds; this could easily be extended to |
| affect the display list ``globally'' (for children of \gTree{}s, there |
| cannot be multiple children with the same name so the issue does not |
| arise). The last line is interesting because it actually erases the |
| \grob{} named \code{"plot1"} from the display list altogether (well, |
| the first instance on the display list of a \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 is |
| that 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")) |
| @ |
| \end{document} |
| |
| |
| |