blob: 636b00820409fac52aeb975ab8247dded80225db [file] [log] [blame]
% 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}