| 28502 |
murrell |
1 |
\documentclass[a4paper]{article}
|
|
|
2 |
%\VignetteIndexEntry{Writing grid Code}
|
|
|
3 |
%\VignettePackage{grid}
|
|
|
4 |
\newcommand{\grid}{{\tt grid}}
|
|
|
5 |
\newcommand{\grob}{{\tt grob}}
|
|
|
6 |
\newcommand{\gTree}{{\tt gTree}}
|
|
|
7 |
\newcommand{\R}{{\tt R}}
|
|
|
8 |
\setlength{\parindent}{0in}
|
|
|
9 |
\setlength{\parskip}{.1in}
|
| 28707 |
murrell |
10 |
\setlength{\textwidth}{140mm}
|
|
|
11 |
\setlength{\oddsidemargin}{10mm}
|
| 28502 |
murrell |
12 |
|
|
|
13 |
\newcommand{\aside}[1]{\begin{list}{}
|
|
|
14 |
{\setlength{\leftmargin}{1in}
|
|
|
15 |
\setlength{\rightmargin}{1in}
|
|
|
16 |
\setlength{\itemindent}{0in}}
|
|
|
17 |
\item {\sc Aside:} {\it #1}
|
|
|
18 |
\end{list}}
|
|
|
19 |
|
|
|
20 |
\title{Writing \grid{} Code}
|
|
|
21 |
\author{Paul Murrell}
|
|
|
22 |
|
|
|
23 |
\begin{document}
|
|
|
24 |
\maketitle
|
|
|
25 |
|
| 28707 |
murrell |
26 |
<<echo=FALSE, results=hide>>=
|
|
|
27 |
library(grid)
|
|
|
28 |
ps.options(pointsize=12)
|
|
|
29 |
options(width=60)
|
|
|
30 |
|
| 28502 |
murrell |
31 |
@
|
|
|
32 |
The \grid{} system contains a degree of complexity in order
|
|
|
33 |
to allow things like editing graphical objects, ``packing'' graphical
|
|
|
34 |
objects, and so on. This means that many of the
|
|
|
35 |
predefined Grid graphics functions are
|
|
|
36 |
relatively complicated\footnote{Although there are exceptions; some
|
|
|
37 |
functions, such as {\tt grid.show.viewport}, are purely for producing
|
|
|
38 |
illustrative diagrams and remain simple and procedural.}.
|
|
|
39 |
|
|
|
40 |
One design aim of \grid{} is to allow users to create simple
|
|
|
41 |
graphics simply and not to force them to use complicated concepts or
|
|
|
42 |
write complicated code
|
|
|
43 |
unless they actually need to. Along similar lines, it is
|
|
|
44 |
intended that people should be able to prototype even complex graphics
|
|
|
45 |
very simply and then refine the implementation into a more
|
|
|
46 |
sophisticated form if necessary.
|
|
|
47 |
|
|
|
48 |
With the predefined graphics
|
|
|
49 |
functions being fully-developed and complicated implementations,
|
|
|
50 |
there is a lack of examples of simple, prototype code.
|
|
|
51 |
Furthermore, given that the aim is to allow a range of ways to produce the same
|
|
|
52 |
graphical output, there is a need for examples which demonstrate the
|
|
|
53 |
various stages, from simple to complex,
|
|
|
54 |
that a piece of \grid{} code can go through.
|
|
|
55 |
|
|
|
56 |
This document describes the
|
|
|
57 |
construction of a scatterplot object, like that shown below,
|
|
|
58 |
going from the simplest, prototype
|
|
|
59 |
implementation to the most complex and sophisticated.
|
|
|
60 |
It demonstrates that if you
|
|
|
61 |
only want simple graphics output then you can do it pretty
|
|
|
62 |
simply and quickly. It also demonstrates how to write functions
|
|
|
63 |
that allow your graphics to be used by other people. Finally, it
|
|
|
64 |
demonstrates how to make your graphics fully interactive (or at least
|
|
|
65 |
as interactive as Grid will let you make it).
|
|
|
66 |
|
|
|
67 |
@
|
|
|
68 |
This document should be read {\em after} the \grid{} Users'
|
|
|
69 |
Guide. Here we are assuming that the reader has an understanding
|
|
|
70 |
of viewports, layouts, and units. For the later sections of the
|
|
|
71 |
document, it will also be helpful to have an understanding of
|
|
|
72 |
\R{}'s {\tt S3} object system.
|
|
|
73 |
|
|
|
74 |
\section*{Procedural \grid{}}
|
|
|
75 |
|
|
|
76 |
The simplest way to produce graphical output in Grid is just
|
|
|
77 |
like producing standard R graphical output. You simply issue a
|
|
|
78 |
series of graphics commands and each command adds more ink to the
|
|
|
79 |
plot.
|
|
|
80 |
The purpose of the commands is simply to produce
|
|
|
81 |
graphics output; in particular, we are not concerned with any
|
|
|
82 |
values returned by the plotting functions. I will call this
|
|
|
83 |
{\it procedural graphics}.
|
|
|
84 |
|
|
|
85 |
In order to draw a simple scatterplot, we can issue a series
|
|
|
86 |
of commands which draw the various components of the plot.
|
|
|
87 |
|
|
|
88 |
Here are some random data to plot.
|
|
|
89 |
|
|
|
90 |
<<>>=
|
|
|
91 |
x <- runif(10)
|
|
|
92 |
y <- runif(10)
|
|
|
93 |
|
|
|
94 |
@
|
|
|
95 |
\noindent
|
|
|
96 |
The first step in creating the plot involves defining a ``data'' region.
|
|
|
97 |
This is a region which has sensible scales on the axes for plotting the
|
|
|
98 |
data and margins around the outside
|
|
|
99 |
for the axes to fit in, with a space for a title at the top.
|
|
|
100 |
|
|
|
101 |
<<datavp>>=
|
|
|
102 |
data.vp <- viewport(x=unit(5, "lines"),
|
|
|
103 |
y=unit(4, "lines"),
|
|
|
104 |
width=unit(1, "npc") - unit(7, "lines"),
|
|
|
105 |
height=unit(1, "npc") - unit(7, "lines"),
|
|
|
106 |
just=c("left", "bottom"),
|
|
|
107 |
xscale=range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
108 |
yscale=range(y) + c(-.05, .05)*diff(range(y)))
|
|
|
109 |
|
|
|
110 |
@
|
|
|
111 |
\noindent
|
|
|
112 |
Now we create the data region and
|
|
|
113 |
draw the components of the plot relative to it:
|
|
|
114 |
points, axes, labels, and a
|
|
|
115 |
title.
|
|
|
116 |
|
|
|
117 |
<<procplot>>=
|
|
|
118 |
pushViewport(data.vp)
|
|
|
119 |
grid.points(x, y)
|
|
|
120 |
grid.rect()
|
|
|
121 |
grid.xaxis()
|
|
|
122 |
grid.yaxis()
|
|
|
123 |
grid.text("x axis", y=unit(-3, "lines"),
|
|
|
124 |
gp=gpar(fontsize=14))
|
|
|
125 |
grid.text("y axis", x=unit(-4, "lines"),
|
|
|
126 |
gp=gpar(fontsize=14), rot=90)
|
|
|
127 |
grid.text("A Simple Plot",
|
|
|
128 |
y=unit(1, "npc") + unit(1.5, "lines"),
|
|
|
129 |
gp=gpar(fontsize=16))
|
|
|
130 |
popViewport()
|
|
|
131 |
|
|
|
132 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
133 |
<<procplot>>
|
|
|
134 |
|
|
|
135 |
@
|
|
|
136 |
\section*{Facilitating Annotation}
|
|
|
137 |
|
|
|
138 |
Issuing a series of commands to produce a plot, like in the previous
|
|
|
139 |
section, allows the user to have a great deal of flexibility.
|
|
|
140 |
It is always possible to recreate viewports in order to add
|
|
|
141 |
further annotations. For example, the following code
|
|
|
142 |
recreates the data region in order to place the date
|
|
|
143 |
at the bottom right corner.
|
|
|
144 |
|
|
|
145 |
<<ann1>>=
|
|
|
146 |
pushViewport(data.vp)
|
|
|
147 |
grid.text(date(), x=unit(1, "npc"),
|
|
|
148 |
y = 0, just=c("right", "bottom"), gp=gpar(col="grey"))
|
|
|
149 |
popViewport()
|
|
|
150 |
|
|
|
151 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
152 |
<<procplot>>
|
|
|
153 |
<<ann1>>
|
|
|
154 |
|
|
|
155 |
@
|
|
|
156 |
|
|
|
157 |
When more complex arrangements of viewports are involved, there may be
|
|
|
158 |
a bewildering array of viewports created, which may make it difficult
|
|
|
159 |
for other users to revisit a particular region of a plot. A {\tt lattice}
|
|
|
160 |
plot is a good example.
|
|
|
161 |
In such cases, it will be more cooperative to use {\tt upViewport()}
|
|
|
162 |
rather than {\tt popViewport()} and leave the viewports that were
|
|
|
163 |
created during the drawing of the plot. Other users can then use
|
|
|
164 |
{\tt vpPath}s to navigate to the desired region. For example, here
|
|
|
165 |
is a slight modification of the original series of commands, where
|
|
|
166 |
the original data viewport is given a name and
|
|
|
167 |
{\tt upViewport()} is used at the end.
|
|
|
168 |
|
|
|
169 |
<<results=hide>>=
|
|
|
170 |
data.vp <- viewport(name="dataregion",
|
|
|
171 |
x=unit(5, "lines"),
|
|
|
172 |
y=unit(4, "lines"),
|
|
|
173 |
width=unit(1, "npc") - unit(7, "lines"),
|
|
|
174 |
height=unit(1, "npc") - unit(7, "lines"),
|
|
|
175 |
just=c("left", "bottom"),
|
|
|
176 |
xscale=range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
177 |
yscale=range(y) + c(-.05, .05)*diff(range(y)))
|
|
|
178 |
pushViewport(data.vp)
|
|
|
179 |
grid.points(x, y)
|
|
|
180 |
grid.rect()
|
|
|
181 |
grid.xaxis()
|
|
|
182 |
grid.yaxis()
|
|
|
183 |
grid.text("x axis", y=unit(-3, "lines"),
|
|
|
184 |
gp=gpar(fontsize=14))
|
|
|
185 |
grid.text("y axis", x=unit(-4, "lines"),
|
|
|
186 |
gp=gpar(fontsize=14), rot=90)
|
|
|
187 |
grid.text("A Simple Plot",
|
|
|
188 |
y=unit(1, "npc") + unit(1.5, "lines"),
|
|
|
189 |
gp=gpar(fontsize=16))
|
|
|
190 |
upViewport()
|
|
|
191 |
|
|
|
192 |
@
|
|
|
193 |
|
|
|
194 |
The date is now added
|
|
|
195 |
using {\tt downViewport()} to get to the data region.
|
|
|
196 |
|
|
|
197 |
<<results=hide>>=
|
|
|
198 |
downViewport("dataregion")
|
|
|
199 |
grid.text(date(), x=unit(1, "npc"),
|
|
|
200 |
y = 0, just=c("right", "bottom"), gp=gpar(col="grey"))
|
|
|
201 |
upViewport()
|
|
|
202 |
|
|
|
203 |
@
|
|
|
204 |
\section*{Writing a \grid{} Function}
|
|
|
205 |
|
|
|
206 |
Here is the scatterplot code wrapped up as a simple function.
|
|
|
207 |
|
|
|
208 |
<<funcplot>>=
|
|
|
209 |
splot <- function(x=runif(10), y=runif(10), title="A Simple Plot") {
|
|
|
210 |
data.vp <- viewport(name="dataregion",
|
|
|
211 |
x=unit(5, "lines"),
|
|
|
212 |
y=unit(4, "lines"),
|
|
|
213 |
width=unit(1, "npc") - unit(7, "lines"),
|
|
|
214 |
height=unit(1, "npc") - unit(7, "lines"),
|
|
|
215 |
just=c("left", "bottom"),
|
|
|
216 |
xscale=range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
217 |
yscale=range(y) + c(-.05, .05)*diff(range(y)))
|
|
|
218 |
pushViewport(data.vp)
|
|
|
219 |
grid.points(x, y)
|
|
|
220 |
grid.rect()
|
|
|
221 |
grid.xaxis()
|
|
|
222 |
grid.yaxis()
|
|
|
223 |
grid.text("y axis", x=unit(-4, "lines"),
|
|
|
224 |
gp=gpar(fontsize=14), rot=90)
|
|
|
225 |
grid.text(title,
|
|
|
226 |
y=unit(1, "npc") + unit(1.5, "lines"),
|
|
|
227 |
gp=gpar(fontsize=16))
|
|
|
228 |
upViewport()
|
|
|
229 |
}
|
|
|
230 |
|
|
|
231 |
@
|
|
|
232 |
There are several advantages to creating a
|
|
|
233 |
function:
|
|
|
234 |
\begin{enumerate}
|
|
|
235 |
\item We get the standard advantages of a function:
|
|
|
236 |
we can reuse and maintain the plot code more easily.
|
|
|
237 |
\item We can slightly generalise the plot. In this case, we can use it for
|
|
|
238 |
different data and have a different title. We could
|
|
|
239 |
add more arguments to allow different margins, control over
|
|
|
240 |
the axis scales, and so on.
|
|
|
241 |
\item The plot can be embedded in other graphics output.
|
|
|
242 |
\end{enumerate}
|
|
|
243 |
Here is an example which uses the {\tt splot()} function to
|
|
|
244 |
create a slightly modified scatterplot, embedded within
|
|
|
245 |
other \grid{} output.
|
|
|
246 |
|
|
|
247 |
<<embed, fig=TRUE, results=hide>>=
|
|
|
248 |
grid.rect(gp=gpar(fill="grey"))
|
|
|
249 |
message <- paste("I could draw all sorts",
|
|
|
250 |
"of stuff over here",
|
|
|
251 |
"then create a viewport",
|
|
|
252 |
"over there and stick",
|
|
|
253 |
"a scatterplot in it.", sep="\n")
|
|
|
254 |
grid.text(message, x=0.25)
|
|
|
255 |
grid.arrows(x=unit.c(unit(0.25, "npc") + 0.5*stringWidth(message) +
|
|
|
256 |
unit(2, "mm"),
|
|
|
257 |
unit(0.5, "npc") - unit(2, "mm")),
|
|
|
258 |
angle=15, type="closed",
|
|
|
259 |
gp=gpar(lwd=3, fill="black"))
|
|
|
260 |
pushViewport(viewport(x=0.5, height=0.5, width=0.45, just="left",
|
|
|
261 |
gp=gpar(cex=0.5)))
|
|
|
262 |
grid.rect(gp=gpar(fill="white"))
|
|
|
263 |
splot(1:10, 1:10, title="An Embedded Plot")
|
|
|
264 |
upViewport()
|
|
|
265 |
|
|
|
266 |
@
|
|
|
267 |
|
|
|
268 |
It is still straightforward to annotate the scatterplot as long as
|
|
|
269 |
we have enough information about the viewports. In this case,
|
|
|
270 |
a non-strict {\tt downViewport()} will still work (though note
|
|
|
271 |
that {\tt upViewport({\bf 0})} is required to get right back to the
|
|
|
272 |
top level).
|
|
|
273 |
|
|
|
274 |
<<ann2, echo=FALSE, eval=FALSE>>=
|
|
|
275 |
downViewport("dataregion")
|
|
|
276 |
grid.text(date(), x=unit(1, "npc"),
|
|
|
277 |
y = 0, just=c("right", "bottom"), gp=gpar(col="grey"))
|
|
|
278 |
upViewport(0)
|
|
|
279 |
|
|
|
280 |
<<echo=FALSE, results=hide>>=
|
|
|
281 |
<<embed>>
|
|
|
282 |
<<ann2>>
|
|
|
283 |
|
|
|
284 |
@
|
|
|
285 |
\section*{Creating \grid{} Graphical Objects}
|
|
|
286 |
|
|
|
287 |
A \grid{} function like the one in the previous section provides
|
|
|
288 |
output which is very flexible and can be annotated in arbitrary ways
|
|
|
289 |
and can be embedded within other output. This is likely
|
|
|
290 |
to satisfy most uses.
|
|
|
291 |
|
|
|
292 |
However, there are some things that cannot be done (or at least would
|
|
|
293 |
be extremely hard to do) with such a function. The output produced by
|
|
|
294 |
the function cannot be addressed as a coherent whole. It is
|
|
|
295 |
not possible, for example, to
|
|
|
296 |
to change the {\tt x}
|
|
|
297 |
and {\tt y} data used in the plot and have the points and axes update
|
|
|
298 |
automatically. There is no scatterplot object to save; the individual
|
|
|
299 |
components exist, but they are not bound together as a whole. If/when
|
|
|
300 |
these sorts of issues become important, it becomes necessary to
|
|
|
301 |
create a \grid{} graphical object (a \grob{}) to represent the plot.
|
|
|
302 |
|
|
|
303 |
The first step is to write a function which will create a \grob{}
|
|
|
304 |
-- a {\it constructor} function. In most cases, this will involve
|
|
|
305 |
creating a special sort of \grob{} called a \gTree{}; this is just
|
|
|
306 |
a \grob{} that can have other \grob{}s as children. Here's an example
|
|
|
307 |
for creating an {\tt splot} \grob{}. I have put bits of the
|
|
|
308 |
construction into separate functions, for reasons which will become
|
|
|
309 |
apparent later.
|
|
|
310 |
|
|
|
311 |
<<>>=
|
|
|
312 |
splot.data.vp <- function(x, y) {
|
|
|
313 |
viewport(name="dataregion",
|
|
|
314 |
x=unit(5, "lines"),
|
|
|
315 |
y=unit(4, "lines"),
|
|
|
316 |
width=unit(1, "npc") - unit(7, "lines"),
|
|
|
317 |
height=unit(1, "npc") - unit(7, "lines"),
|
|
|
318 |
just=c("left", "bottom"),
|
|
|
319 |
xscale=range(x) + c(-.05, .05)*diff(range(x)),
|
|
|
320 |
yscale=range(y) + c(-.05, .05)*diff(range(y)))
|
|
|
321 |
}
|
|
|
322 |
|
|
|
323 |
splot.title <- function(title) {
|
|
|
324 |
textGrob(title, name="title",
|
|
|
325 |
y=unit(1, "npc") + unit(1.5, "lines"),
|
|
|
326 |
gp=gpar(fontsize=16), vp="dataregion")
|
|
|
327 |
}
|
|
|
328 |
|
|
|
329 |
splot <- function(x, y, title, name=NULL, draw=TRUE, gp=gpar(), vp=NULL) {
|
|
|
330 |
spg <- gTree(
|
|
|
331 |
x=x, y=y, title=title,
|
|
|
332 |
name=name,
|
|
|
333 |
childrenvp = splot.data.vp(x, y),
|
|
|
334 |
children=gList(rectGrob(name="border", vp="dataregion"),
|
|
|
335 |
xaxisGrob(name="xaxis", vp="dataregion"),
|
|
|
336 |
yaxisGrob(name="yaxis", vp="dataregion"),
|
|
|
337 |
pointsGrob(x, y, name="points", vp="dataregion"),
|
|
|
338 |
textGrob("x axis", y=unit(-3, "lines"), name="xlab",
|
|
|
339 |
gp=gpar(fontsize=14), vp="dataregion"),
|
|
|
340 |
textGrob("y axis", x=unit(-4, "lines"), name="ylab",
|
|
|
341 |
gp=gpar(fontsize=14), rot=90, vp="dataregion"),
|
|
|
342 |
splot.title(title)),
|
|
|
343 |
gp=gp, vp=vp,
|
|
|
344 |
cl="splot")
|
|
|
345 |
if (draw)
|
|
|
346 |
grid.draw(spg)
|
|
|
347 |
spg
|
|
|
348 |
}
|
|
|
349 |
|
|
|
350 |
@
|
|
|
351 |
|
|
|
352 |
There are four important additions to the argument list compared
|
|
|
353 |
to the original {\tt splot()} function:
|
|
|
354 |
\begin{enumerate}
|
|
|
355 |
\item The {\tt name} argument allows a string identifier to be
|
|
|
356 |
associated with the scatterplot object we create. This is
|
|
|
357 |
important for being able to specify the scatterplot when we try to edit
|
|
|
358 |
it after drawing it and/or when it is part of a larger \grob{}
|
|
|
359 |
(see later examples).
|
|
|
360 |
\item The {\tt draw} argument
|
|
|
361 |
makes it possible to use the function in a procedural
|
|
|
362 |
manner as before:
|
|
|
363 |
|
|
|
364 |
<<splotgrob, eval=FALSE, echo=FALSE>>=
|
|
|
365 |
sg <- splot(1:10, 1:10, "Same as Before", name="splot", draw=FALSE)
|
|
|
366 |
|
|
|
367 |
<<>>=
|
|
|
368 |
splot(1:10, 1:10, "Same as Before", name="splot")
|
|
|
369 |
downViewport("dataregion")
|
|
|
370 |
grid.text(date(), x=unit(1, "npc"),
|
|
|
371 |
y = 0, just=c("right", "bottom"), gp=gpar(col="grey"))
|
|
|
372 |
upViewport(0)
|
|
|
373 |
|
|
|
374 |
@
|
|
|
375 |
\item The {\tt gp} argument allows the user to supply {\tt gpar()}
|
|
|
376 |
settings for the scatterplot as a whole.
|
|
|
377 |
\item The {\tt vp} argument allows the user to supply a viewport
|
|
|
378 |
for the {\tt splot} \grob{} to be drawn in. This is especially
|
|
|
379 |
useful for specifying a {\tt vpPath} when the {\tt splot} is
|
|
|
380 |
used as a component of another \grob{} (see scatterplot matrix
|
|
|
381 |
example
|
|
|
382 |
below).
|
|
|
383 |
\end{enumerate}
|
|
|
384 |
|
|
|
385 |
The important parts of the \gTree{} definition are:
|
|
|
386 |
\begin{enumerate}
|
|
|
387 |
\item The {\tt children} argument provides a list of \grob{}s which are
|
|
|
388 |
part of the scatterplot. When the scatterplot is drawn, all children
|
|
|
389 |
will be drawn. Notice that instead of the procedural {\tt grid.*()} functions
|
|
|
390 |
we use {\tt *Grob()} functions which just produce \grob{}s and do not
|
|
|
391 |
perform any drawing. Also notice that I have given each of the
|
|
|
392 |
children a name; this will make it possible to access the components
|
|
|
393 |
of the scatterplot (see later examples).
|
|
|
394 |
\item The {\tt childrenvp} argument provides a viewport (or
|
|
|
395 |
{\tt vpStack}, {\tt vpList}, or {\tt vpTree}) which will be pushed before
|
|
|
396 |
the children are drawn. The difference between this argument and the
|
|
|
397 |
{\tt vp} argument common to all \grob{}s is that the {\tt vp} is pushed before
|
|
|
398 |
drawing the children and then popped after, whereas the {\tt childrenvp}
|
|
|
399 |
gets pushed {\it and} then a call to {\tt upViewport()} is made
|
|
|
400 |
before the children are drawn. This allows the children to simply
|
|
|
401 |
specify the viewport they should be drawn in by way of a {\tt vpPath}
|
|
|
402 |
in their {\tt vp} argument. In this way, viewports remain available
|
|
|
403 |
for further annotation such as we have already seen in procedural code.
|
|
|
404 |
\item The {\tt gp} and {\tt vp} arguments are automatically handled by
|
|
|
405 |
the \gTree{} drawing methods so that {\tt gpar()} settings will be
|
|
|
406 |
enforced and the viewport will be pushed when the {\tt splot} is drawn.
|
|
|
407 |
\item The {\tt cl} argument means that the \grob{} created is a special
|
|
|
408 |
sort of \grob{} called {\tt splot}. This will allow us to write methods
|
|
|
409 |
specifically for our scatterplot (see later examples).
|
|
|
410 |
\end{enumerate}
|
|
|
411 |
|
|
|
412 |
|
|
|
413 |
@
|
|
|
414 |
|
|
|
415 |
Now that we have a \grob{}, there are some more interesting things that
|
|
|
416 |
we can do with it.
|
|
|
417 |
First of all, the {\tt splot} \grob{} provides a container for the
|
|
|
418 |
\grob{}s which make up the scatterplot.
|
|
|
419 |
If we modify the {\tt splot} \grob{}, it affects all of the children.
|
|
|
420 |
|
|
|
421 |
<<results=hide>>=
|
|
|
422 |
splot(1:10, 1:10, "Same as Before", name="splot")
|
|
|
423 |
grid.edit("splot", gp=gpar(cex=0.5))
|
|
|
424 |
|
|
|
425 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
426 |
<<splotgrob>>
|
|
|
427 |
sg <- editGrob(sg, gp=gpar(cex=0.5))
|
|
|
428 |
grid.draw(sg)
|
|
|
429 |
|
|
|
430 |
@
|
|
|
431 |
|
|
|
432 |
We can access elements of the {\tt splot} \grob{} to edit them
|
|
|
433 |
individually.
|
|
|
434 |
|
|
|
435 |
<<results=hide>>=
|
|
|
436 |
splot(1:10, 1:10, "Same as Before", name="splot")
|
|
|
437 |
grid.edit(gPath("splot", "points"), gp=gpar(col=1:10))
|
|
|
438 |
|
|
|
439 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
440 |
<<splotgrob>>
|
|
|
441 |
sg <- editGrob(sg, gPath="points", gp=gpar(col=1:10))
|
|
|
442 |
grid.draw(sg)
|
|
|
443 |
|
|
|
444 |
@
|
|
|
445 |
|
|
|
446 |
With a little more work we can make the scatterplot a bit more dynamic.
|
|
|
447 |
The following describes a {\tt editDetails()} method for the
|
|
|
448 |
{\tt splot} \grob{}. This will be called whenever a scatterplot
|
|
|
449 |
is edited and will update the components of the scatterplot.
|
|
|
450 |
|
|
|
451 |
<<>>=
|
|
|
452 |
editDetails.splot <- function(x, specs) {
|
|
|
453 |
if (any(c("x", "y") %in% names(specs))) {
|
|
|
454 |
if (is.null(specs$x))
|
|
|
455 |
xx <- x$x
|
|
|
456 |
else
|
|
|
457 |
xx <- specs$x
|
|
|
458 |
if (is.null(specs$y))
|
|
|
459 |
yy <- x$y
|
|
|
460 |
else
|
|
|
461 |
yy <- specs$y
|
|
|
462 |
x$childrenvp <- splot.data.vp(xx, yy)
|
|
|
463 |
x <- addGrob(x, pointsGrob(xx, yy, name="points", vp="dataregion"))
|
|
|
464 |
}
|
|
|
465 |
x
|
|
|
466 |
}
|
|
|
467 |
splot(1:10, 1:10, "Same as Before", name="splot")
|
|
|
468 |
grid.edit("splot", x=1:100, y=(1:100)^2)
|
|
|
469 |
|
|
|
470 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
471 |
<<splotgrob>>
|
|
|
472 |
sg <- editGrob(sg, x=1:100, y=(1:100)^2)
|
|
|
473 |
grid.draw(sg)
|
|
|
474 |
|
|
|
475 |
@
|
|
|
476 |
|
|
|
477 |
The {\tt splot} \grob{} can also be used in the construction of
|
|
|
478 |
other \grob{}s. Here's a simple scatterplot matrix
|
|
|
479 |
\grob{}\footnote{{\bf Warning:} As the number of \grob{}s in a \gTree{} gets
|
|
|
480 |
larger the construction of the \gTree{} will get slow. If this happens,
|
|
|
481 |
the best solution is to just use a \grid{} function rather than
|
|
|
482 |
a \gTree{}, and wait for me to implement some ideas for speeding things
|
|
|
483 |
up!}.
|
|
|
484 |
|
|
|
485 |
<<fig=TRUE>>=
|
|
|
486 |
cellname <- function(i, j) {
|
|
|
487 |
paste("cell", i, j, sep="")
|
|
|
488 |
}
|
|
|
489 |
|
|
|
490 |
splom.vpTree <- function(n) {
|
|
|
491 |
vplist <- vector("list", n^2)
|
|
|
492 |
for (i in 1:n)
|
|
|
493 |
for (j in 1:n)
|
|
|
494 |
vplist[[(i - 1)*n + j]] <-
|
|
|
495 |
viewport(layout.pos.row=i, layout.pos.col=j,
|
|
|
496 |
name=cellname(i, j))
|
|
|
497 |
vpTree(viewport(layout=grid.layout(n, n), name="cellgrid"),
|
|
|
498 |
do.call("vpList", vplist))
|
|
|
499 |
}
|
|
|
500 |
|
|
|
501 |
cellpath <- function(i, j) {
|
|
|
502 |
vpPath("cellgrid", cellname(i, j))
|
|
|
503 |
}
|
|
|
504 |
|
|
|
505 |
splom <- function(df, name=NULL, draw=TRUE) {
|
|
|
506 |
n <- dim(df)[2]
|
|
|
507 |
glist <- vector("list", n*n)
|
|
|
508 |
for (i in 1:n)
|
|
|
509 |
for (j in 1:n)
|
|
|
510 |
if (i == j)
|
|
|
511 |
glist[[(i - 1)*n + j]] <- textGrob(paste("diag", i, sep=""),
|
|
|
512 |
gp=gpar(col="grey"), vp=cellpath(i, j))
|
|
|
513 |
else if (j > i)
|
|
|
514 |
glist[[(i - 1)*n + j]] <- textGrob(cellname(i, j),
|
|
|
515 |
name=cellname(i, j),
|
|
|
516 |
gp=gpar(col="grey"), vp=cellpath(i, j))
|
|
|
517 |
else
|
|
|
518 |
glist[[(i - 1)*n + j]] <- splot(df[,j], df[,i], "",
|
|
|
519 |
name=paste("plot", i, j, sep=""), vp=cellpath(i, j),
|
|
|
520 |
gp=gpar(cex=0.5), draw=FALSE)
|
|
|
521 |
smg <- gTree(name=name,
|
|
|
522 |
childrenvp=splom.vpTree(n),
|
|
|
523 |
children=do.call("gList", glist))
|
|
|
524 |
if (draw)
|
|
|
525 |
grid.draw(smg)
|
|
|
526 |
smg
|
|
|
527 |
}
|
|
|
528 |
|
|
|
529 |
df <- data.frame(x=rnorm(10), y=rnorm(10), z=rnorm(10))
|
|
|
530 |
splom(df)
|
|
|
531 |
|
|
|
532 |
@
|
|
|
533 |
|
|
|
534 |
This \grob{} can be edited as usual:
|
|
|
535 |
|
|
|
536 |
<<>>=
|
|
|
537 |
splom(df)
|
|
|
538 |
grid.edit("plot21::xlab", label="", redraw=FALSE)
|
|
|
539 |
grid.edit("plot32::ylab", label="", redraw=FALSE)
|
|
|
540 |
grid.edit("plot21::xaxis", label=FALSE, redraw=FALSE)
|
|
|
541 |
grid.edit("plot32::yaxis", label=FALSE)
|
|
|
542 |
|
| 30260 |
murrell |
543 |
<<splomgrob, eval=FALSE, echo=FALSE>>=
|
|
|
544 |
smg <- splom(df, draw=FALSE)
|
|
|
545 |
|
| 28502 |
murrell |
546 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
547 |
<<splomgrob>>
|
|
|
548 |
smg <- editGrob(smg, gPath="plot21::xaxis", label=FALSE)
|
|
|
549 |
smg <- editGrob(smg, gPath="plot21::xlab", label="")
|
|
|
550 |
smg <- editGrob(smg, gPath="plot32::yaxis", label=FALSE)
|
|
|
551 |
smg <- editGrob(smg, gPath="plot32::ylab", label="")
|
|
|
552 |
grid.draw(smg)
|
|
|
553 |
|
|
|
554 |
@
|
|
|
555 |
|
|
|
556 |
But of more interest, because this is a \grob{}, is the {\it programmatic}
|
|
|
557 |
interface. With a \grob{} (as opposed to a function) it is possible
|
|
|
558 |
to modify the description of what is being drawn via an API (as opposed
|
|
|
559 |
to having to edit the original code). In the following, we
|
|
|
560 |
remove one of the ``spare'' cell labels and put in its place the
|
|
|
561 |
current date.
|
|
|
562 |
|
|
|
563 |
<<>>=
|
|
|
564 |
splom(df, name="splom")
|
|
|
565 |
grid.remove("cell12")
|
|
|
566 |
grid.add("splom", textGrob(date(), name="date", gp=gpar(fontface="italic"),
|
|
|
567 |
vp="cellgrid::cell12"))
|
|
|
568 |
|
|
|
569 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
570 |
<<splomgrob>>
|
|
|
571 |
smg <- removeGrob(smg, "cell12")
|
|
|
572 |
smg <- addGrob(smg, textGrob(date(), name="date", gp=gpar(fontface="italic"),
|
|
|
573 |
vp="cellgrid::cell12"))
|
|
|
574 |
grid.draw(smg)
|
|
|
575 |
|
|
|
576 |
@
|
|
|
577 |
|
|
|
578 |
With the date added as a component of the scatterplot matrix, it is
|
|
|
579 |
saved as part of the matrix. The next sequence saves the scatterplot
|
|
|
580 |
matrix, loads it again, extracts the bottom-left plot and the date
|
|
|
581 |
and just draws those two objects together.
|
|
|
582 |
|
|
|
583 |
<<>>=
|
|
|
584 |
splom(df, name="splom")
|
|
|
585 |
grid.remove("cell12")
|
|
|
586 |
grid.add("splom", textGrob(date(), name="date", gp=gpar(fontface="italic"),
|
|
|
587 |
vp="cellgrid::cell12"))
|
|
|
588 |
smg <- grid.get("splom")
|
|
|
589 |
save(smg, file="splom.RData")
|
|
|
590 |
load("splom.RData")
|
|
|
591 |
plot <- getGrob(smg, "plot31")
|
|
|
592 |
date <- getGrob(smg, "date")
|
|
|
593 |
plot <- editGrob(plot, vp=NULL, gp=gpar(cex=1))
|
|
|
594 |
date <- editGrob(date, y=unit(1, "npc") - unit(1, "lines"), vp=NULL)
|
|
|
595 |
grid.newpage()
|
|
|
596 |
grid.draw(plot)
|
|
|
597 |
grid.draw(date)
|
|
|
598 |
|
|
|
599 |
<<fig=TRUE, echo=FALSE, results=hide>>=
|
|
|
600 |
<<splomgrob>>
|
|
|
601 |
smg <- removeGrob(smg, "cell12")
|
|
|
602 |
smg <- addGrob(smg, textGrob(date(), name="date", gp=gpar(fontface="italic"),
|
|
|
603 |
vp="cellgrid::cell12"))
|
|
|
604 |
save(smg, file="splom.RData")
|
|
|
605 |
load("splom.RData")
|
|
|
606 |
plot <- getGrob(smg, "plot31")
|
|
|
607 |
date <- getGrob(smg, "date")
|
|
|
608 |
plot <- editGrob(plot, vp=NULL, gp=gpar(cex=1))
|
|
|
609 |
date <- editGrob(date, y=unit(1, "npc") - unit(1, "lines"), vp=NULL)
|
|
|
610 |
grid.draw(plot)
|
|
|
611 |
grid.draw(date)
|
|
|
612 |
|
|
|
613 |
@
|
|
|
614 |
|
|
|
615 |
All of this may seem a bit irrelevant to interactive use, but it
|
|
|
616 |
does provide a basis for creating an editable plot interface like
|
| 28707 |
murrell |
617 |
M Kondrin's {\tt Rgrace} package.
|
| 28502 |
murrell |
618 |
|
| 30260 |
murrell |
619 |
% Start a new page
|
|
|
620 |
% Not echoed, not evaluated
|
|
|
621 |
% ONLY here for checkVignettes so that all output doesn't
|
|
|
622 |
% end up on one enormous page
|
|
|
623 |
|
|
|
624 |
<<eval=FALSE, echo=FALSE>>=
|
|
|
625 |
grid.newpage()
|
|
|
626 |
|
|
|
627 |
@
|
| 28502 |
murrell |
628 |
\end{document}
|