Update: I asked Paul Murrell (the "final boss"), and he told me the decision of opening a new page is made in src/library/graphics/src/graphics.c
by GNewPlot
:
pGEDevDesc GNewPlot(Rboolean recording)
Effectively it looks at par(mfg)
and par(new)
to decide whether to open a new page. How do I that test in an add-on package?
A solution using either pure R code or some C code is acceptable.
knitr
uses the evaluate
package to evaluate R code chunks, and capture results, including plots. Briefly speaking, evaluate
calls recordPlot()
to record a snapshot of the current plot after each code expression has been evaluated, and also when the plotting hooks like before.plot.new
and before.grid.newpage
are called (normally this happens before a new plot is drawn). For those "Luke's", please use the source if this is not clear enough.
After we make a snapshot, we need to decide whether to keep it, because the snapshot might not be changed after we evaluate a new expression, which is not involved with plotting. Now here comes my problem: when a plot contains sub-plots (for example, pairs()
, coplot()
, or par(mfrow = c(2, 3))
in base R graphics), we should not keep the incomplete snapshots. In the example below, the first three plots should be discarded:
par(mfrow = c(2, 2))
plot(rnorm(10))
plot(rnorm(10))
plot(rnorm(10))
plot(rnorm(10))
To achieve this, we compare par("mfg")[1:2]
to par("mfg")[3:4]
(as you can see in the source code), and this works well in some cases, but not all, for example, the issue #25:
layout(matrix(c(1,3,2,3), 2))
plot(rnorm(10))
plot(rnorm(10))
plot(rnorm(10))
Now how do I know the plot is not complete before line 4? The mfg
trick no longer works.
I have been looking for an answer to this problem for a long time, and I will truly appreciate it if someone can give me a hint; further details are in the two links above, and I can clarify if anything is unclear. I believe there must exist a solution, because all R devices know when to start a new plot screen or file, and incomplete plots will not trigger new plot screens or files.
Update: this is available in R 3.0.2 now.
In his recent commit, Paul Murrell added a new read-only parameter in par()
named page
, which gives TRUE
or FALSE
, indicating whether the next plot needs to open a new page.
This was achieved by copying some code from GNewPlot()
, and is only available in R-devel at the moment.
From ?par
:
‘mfg’ A numerical vector of the form ‘c(i, j)’ where ‘i’ and ‘j’ indicate which figure in an array of figures is to be drawn next (if setting) or is being drawn (if enquiring). The array must already have been set by ‘mfcol’ or ‘mfrow’.
So, it seems that you'd need to use a pairing function to assign a unique id to the initial value of par("mfg")[1:2]
, and then test subsequent values of par("mfg")[1:2]
against that key. A valid paring, for example, would be 2**par("mfg")[1] * 3**par("mfg")[2]
:
Pid <- function(ij, test.id=NA){
mi <- ij[1]
mj <- ij[2]
ijd <- 2**mi * 3**mj
if (!is.na(test.id)) ijd <- ijd == test.id
return(ijd)
}
Back to your example:
layout(matrix(c(1,3,2,3), 2))
x0 <- par("mfg") # 2 1 2 2
id <- Pid(x0) # 12
plot(rnorm(10))
x1 <- par("mfg")
p1 <- Pid(x1, id) # FALSE (layout is not full)
plot(rnorm(10))
x2 <- par("mfg")
p2 <- Pid(x2, id) # FALSE (layout is not full)
plot(rnorm(10))
x3 <- par("mfg")
p3 <- Pid(x3, id) # TRUE (layout is now full)
#
rbind(p1,p2,p3)
# [,1]
#p1 FALSE
#p2 FALSE
#p3 TRUE
I suppose, however, that without the initial id
, you'd be out of luck.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With