| # 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) |
| } |
| |
| |