The R Project SVN R

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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}