blob: 7ef5c331e28fa28f766e159382b633a060c1598b [file] [log] [blame]
# File src/library/grid/R/grab.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2016 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
#########
# Generate a gTree from the current display list
#
# Or from an expression
# (recording on to a null graphics device)
#########
rootVP <- function(pvp) {
match(pvp$name, "ROOT", nomatch=FALSE)
}
# List the children of the current vp (as a vpList)
current.vpList <- function() {
cpvp <- grid.Call(C_currentViewport)
if (no.children(cpvp$children))
NULL
else
vpListFromNode(cpvp)
}
current.vpNames <- function() {
ls(grid.Call(C_currentViewport)$children)
}
# vp might be a viewport, or a vpList, or a vpStack, or a vpTree
vpExists <- function(vp) {
UseMethod("vpExists")
}
vpExists.viewport <- function(vp) {
exists(vp$name, .Call(C_currentViewport)$children)
}
vpExists.vpStack <- function(vp) {
vpExists(vp[[1L]])
}
vpExists.vpList <- function(vp) {
any(vapply(vp, vpExists, logical(1L), simplify=TRUE))
}
vpExists.vpTree <- function(vp) {
vpExists(vp$parent)
}
# Handle vpPaths in a vpStack or vpTree
# Not a problem to downViewport() to a viewport that already exists
vpExists.vpPath <- function(vp) {
FALSE
}
wrap <- function(x, ...) {
UseMethod("wrap")
}
wrap.default <- function(x, ...) {
if (!is.null(x))
stop("invalid display list element")
NULL
}
wrap.grob <- function(x, grobs=FALSE, ...) {
if (grobs) {
recordGrob(grid.draw(x), list(x=x))
} else {
x
}
}
wrap.viewport <- function(x, ...) {
recordGrob(pushViewport(vp), list(vp=x))
}
wrap.pop <- function(x, ...) {
recordGrob(popViewport(n), list(n=x))
}
wrap.up <- function(x, ...) {
recordGrob(upViewport(n), list(n=x))
}
wrap.vpPath <- function(x, ...) {
recordGrob(downViewport(path), list(path=x))
}
# Grab the display list on the current device
# ... are passed to gTree
# If warn is 0, issue no warnings
# If warn is 1, issue warnings about situations that are definitely
# NOT captured correctly (e.g., reuse of top-level grob name)
# If warn is 2, issue warnings about situations that
# MAY not get captured correctly (e.g., top-level downViewport)
# If wrap is TRUE, grab will wrap all pushes and grobs
# in a gTree
grabDL <- function(warn, wrap, wrap.grobs=FALSE, ...) {
gList <- NULL
dl.index <- grid.Call(C_getDLindex)
if (dl.index > 1) {
if (warn > 0 && !wrap.grobs) {
names <- getNames()
## Check for overwriting existing grob
if (length(unique(names)) != length(names))
warning("one or more grobs overwritten (grab WILL not be faithful; try 'wrap.grobs = TRUE')")
}
if (!wrap) {
grid.newpage(recording=FALSE)
}
## Start at 2 because first element is viewport[ROOT]
for (i in 2:dl.index) {
## Do all of this as a big ifelse rather than
## dispatching to a function call per element because
## we need to work with whole DL at times, not
## just individual elements
elt <- grid.Call(C_getDLelt, as.integer(i - 1))
if (wrap)
gList <- addToGList(wrap(elt, grobs=wrap.grobs), gList)
else {
## ####################
## grabGrob
## ####################
if (inherits(elt, "grob")) {
## Enforce grob$vp now and set grob$vp to NULL
## Will be replaced later with full vpPath
tempvp <- elt$vp
if (warn > 1) {
## Check to see if about to push a viewport
## with existing viewport name
if (inherits(tempvp, "viewport") &&
vpExists(tempvp))
warning("viewport overwritten (grab MAY not be faithful)")
}
if (!is.null(tempvp))
tempdepth <- depth(tempvp)
grid.draw(tempvp, recording=FALSE)
## vpPath after grob$vp slot has been pushed
## Has to be recorded here in case grob drawing
## pushes (and does not pop) more viewports
drawPath <- current.vpPath()
elt$vp <- NULL
grid.draw(elt, recording=FALSE)
if (warn > 1) {
## Compare new vpPath
## If not same, the grob has pushed some viewports
## and not popped or upped them
pathSame <- TRUE
if (!(is.null(drawPath) && is.null(current.vpPath()))) {
if (is.null(drawPath))
pathSame <- FALSE
else if (is.null(current.vpPath()))
pathSame <- FALSE
else if (as.character(drawPath) !=
as.character(current.vpPath()))
pathSame <- FALSE
}
if (!pathSame)
warning("grob pushed viewports and did not pop/up them (grab MAY not be faithful)")
}
elt$vp <- drawPath
if (!is.null(tempvp))
upViewport(tempdepth, recording=FALSE)
gList <- addToGList(elt, gList)
## ####################
## grabViewport
## ####################
} else if (inherits(elt, "viewport")) {
## Includes viewports, vpLists, vpTrees, and vpStacks
## Check to see if about to push a viewport
## with existing viewport name
if (warn > 1) {
if (vpExists(elt))
warning("viewport overwritten (grab MAY not be faithful)")
}
grid.draw(elt, recording=FALSE)
## ####################
## grabPop
## ####################
} else if (inherits(elt, "pop")) {
## Replace pop with up
upViewport(elt, recording=FALSE)
## ####################
## grabDefault
## ####################
} else {
grid.draw(elt, recording=FALSE)
}
} ## matches if (wrap)
}
## Go to top level
upViewport(0, recording=FALSE)
gTree(children=gList, childrenvp=current.vpList(), ...)
} else {
NULL
}
}
# expr is ignored if dev is NULL
# otherwise, it should be an expression, like postscript("myfile.ps")
grid.grab <- function(warn=2, wrap=wrap.grobs, wrap.grobs=FALSE, ...) {
grabDL(warn, wrap, wrap.grobs, ...)
}
offscreen <- function(width, height) {
pdf(file=NULL, width=width, height=height)
}
grid.grabExpr <- function(expr, warn=2, wrap=wrap.grobs, wrap.grobs=FALSE,
width=7, height=7, device=offscreen, ...) {
## Start an "offline" PDF device for this function
cd <- dev.cur()
device(width, height)
grabd <- dev.cur()
on.exit({ dev.set(grabd); dev.off(); dev.set(cd) })
## Run the graphics code in expr
## Rely on lazy evaluation for correct "timing"
eval(expr)
## Grab the DL on the new device
grabDL(warn, wrap, wrap.grobs, ...)
}
#########################
# A different sort of capture ...
# Just grab the screen raster image
#########################
grid.cap <- function() {
# This does not need recording on the display list
grid.Call(C_cap)
}