# File src/library/grid/R/highlevel.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1995-2013 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/ ###################################### ## Example applications of grid # ###################################### grid.strip <- function(label="whatever", range.full=c(0, 1), range.thumb=c(.3, .6), fill="#FFBF00", thumb="#FF8000", vp=NULL) { diff.full <- diff(range.full) diff.thumb <- diff(range.thumb) if (!is.null(vp)) pushViewport(vp) grid.rect(gp=gpar(col=NULL, fill=fill)) grid.rect((range.thumb[1L] - range.full[1L])/diff.full, 0, diff.thumb/diff.full, 1, just=c("left", "bottom"), gp=gpar(col=NULL, fill=thumb)) grid.text(as.character(label)) if (!is.null(vp)) popViewport() } grid.panel <- function(x = stats::runif(10), y = stats::runif(10), zrange = c(0, 1), zbin = stats::runif(2), xscale = extendrange(x), yscale = extendrange(y), axis.left = TRUE, axis.left.label = TRUE, axis.right = FALSE, axis.right.label = TRUE, axis.bottom = TRUE, axis.bottom.label = TRUE, axis.top = FALSE, axis.top.label = TRUE, vp=NULL) { if (!is.null(vp)) pushViewport(vp) temp.vp <- viewport(layout=grid.layout(2, 1, heights=unit(c(1, 1), c("lines", "null")))) pushViewport(temp.vp) strip.vp <- viewport(layout.pos.row=1, layout.pos.col=1, xscale=xscale) pushViewport(strip.vp) grid.strip(range.full=zrange, range.thumb=zbin) grid.rect() if (axis.top) grid.xaxis(main=FALSE, label=axis.top.label) popViewport() plot.vp <- viewport(layout.pos.row=2, layout.pos.col=1, xscale=xscale, yscale=yscale) pushViewport(plot.vp) grid.grill() grid.points(x, y, gp=gpar(col="blue")) grid.rect() if (axis.left) grid.yaxis(label=axis.left.label) if (axis.right) grid.yaxis(main=FALSE, label=axis.right.label) if (axis.bottom) grid.xaxis(label=axis.bottom.label) popViewport(2) if (!is.null(vp)) popViewport() invisible(list(strip.vp = strip.vp, plot.vp = plot.vp)) } grid.multipanel <- function(x = stats::runif(90), y = stats::runif(90), z = stats::runif(90), nplots = 9, nrow = 5, ncol = 2, newpage = TRUE, vp = NULL) { if (newpage) grid.newpage() if (!is.null(vp)) pushViewport(vp) stopifnot(nplots >= 1) if((missing(nrow) || missing(ncol)) && !missing(nplots)) { ## determine 'smart' default ones rowcol <- grDevices::n2mfrow(nplots) nrow <- rowcol[1L] ncol <- rowcol[2L] } temp.vp <- viewport(layout = grid.layout(nrow, ncol)) pushViewport(temp.vp) xscale <- extendrange(x) yscale <- extendrange(y) breaks <- seq.int(min(z), max(z), length.out = nplots + 1) for (i in 1L:nplots) { col <- (i - 1) %% ncol + 1 row <- (i - 1) %/% ncol + 1 panel.vp <- viewport(layout.pos.row = row, layout.pos.col = col) panelx <- x[z >= breaks[i] & z <= breaks[i+1]] panely <- y[z >= breaks[i] & z <= breaks[i+1]] grid.panel(panelx, panely, range(z), c(breaks[i], breaks[i+1]), xscale, yscale, axis.left = (col == 1), axis.right = (col == ncol || i == nplots), axis.bottom = (row == nrow), axis.top = (row == 1), axis.left.label = is.even(row), axis.right.label = is.odd(row), axis.bottom.label = is.even(col), axis.top.label = is.odd(col), vp = panel.vp) } grid.text("Compression Ratio", unit(.5, "npc"), unit(-4, "lines"), gp = gpar(fontsize = 20), just = "center", rot = 0) grid.text("NOx (micrograms/J)", unit(-4, "lines"), unit(.5, "npc"), gp = gpar(fontsize = 20), just = "centre", rot = 90) popViewport() if (!is.null(vp)) popViewport() } grid.show.layout <- function(l, newpage=TRUE, vp.ex=0.8, bg="light grey", cell.border="blue", cell.fill="light blue", cell.label=TRUE, label.col="blue", unit.col="red", vp=NULL) { if (!is.layout(l)) stop("'l' must be a layout") if (newpage) grid.newpage() if (!is.null(vp)) pushViewport(vp) grid.rect(gp=gpar(col=NULL, fill=bg)) vp.mid <- viewport(0.5, 0.5, vp.ex, vp.ex, layout=l) pushViewport(vp.mid) grid.rect(gp=gpar(fill="white")) gp.red <- gpar(col=unit.col) for (i in 1L:l$nrow) for (j in 1L:l$ncol) { vp.inner <- viewport(layout.pos.row=i, layout.pos.col=j) pushViewport(vp.inner) grid.rect(gp=gpar(col=cell.border, fill=cell.fill)) if (cell.label) grid.text(paste0("(", i, ", ", j, ")"), gp=gpar(col=label.col)) if (j==1) # recycle heights if necessary grid.text(as.character("["(l$heights, i, top=FALSE)), gp=gp.red, just=c("right", "centre"), x=unit(-.05, "inches"), y=unit(.5, "npc"), rot=0) if (i==l$nrow) # recycle widths if necessary grid.text(as.character("["(l$widths, j, top=FALSE)), gp=gp.red, just=c("centre", "top"), x=unit(.5, "npc"), y=unit(-.05, "inches"), rot=0) if (j==l$ncol) # recycle heights if necessary grid.text(as.character("["(l$heights, i, top=FALSE)), gp=gp.red, just=c("left", "centre"), x=unit(1, "npc") + unit(.05, "inches"), y=unit(.5, "npc"), rot=0) if (i==1) # recycle widths if necessary grid.text(as.character("["(l$widths, j, top=FALSE)), gp=gp.red, just=c("centre", "bottom"), x=unit(.5, "npc"), y=unit(1, "npc") + unit(.05, "inches"), rot=0) popViewport() } popViewport() if (!is.null(vp)) popViewport() ## return the viewport used to represent the parent viewport invisible(vp.mid) } grid.show.viewport <- function(v, parent.layout=NULL, newpage=TRUE, vp.ex=0.8, border.fill="light grey", vp.col="blue", vp.fill="light blue", scale.col="red", vp=NULL) { ## if the viewport has a non-NULL layout.pos.row or layout.pos.col ## AND the viewport has a parent AND the parent has a layout ## represent the location of the viewport in the parent's layout ... if ((!is.null(v$layout.pos.row) || !is.null(v$layout.pos.col)) && !is.null(parent.layout)) { if (!is.null(vp)) pushViewport(vp) vp.mid <- grid.show.layout(parent.layout, vp.ex=vp.ex, cell.border="grey", cell.fill="white", cell.label=FALSE, newpage=newpage) pushViewport(vp.mid) pushViewport(v) gp.red <- gpar(col=scale.col) grid.rect(gp=gpar(col="blue", fill="light blue")) at <- grid.pretty(v$xscale) grid.xaxis(at=c(min(at), max(at)), gp=gp.red) at <- grid.pretty(v$yscale) grid.yaxis(at=c(min(at), max(at)), gp=gp.red) popViewport(2) if (!is.null(vp)) popViewport() } else { if (newpage) grid.newpage() if (!is.null(vp)) pushViewport(vp) grid.rect(gp=gpar(col=NULL, fill=border.fill)) ## generate a viewport within the "top" viewport (vp) to represent the ## parent viewport of the viewport we are "show"ing (v). ## This is so that annotations at the edges of the ## parent viewport will be at least partially visible vp.mid <- viewport(0.5, 0.5, vp.ex, vp.ex) pushViewport(vp.mid) grid.rect(gp=gpar(fill="white")) x <- v$x y <- v$y w <- v$width h <- v$height pushViewport(v) grid.rect(gp=gpar(col=vp.col, fill=vp.fill)) ## represent the "native" scale gp.red <- gpar(col=scale.col) at <- grid.pretty(v$xscale) grid.xaxis(at=c(min(at), max(at)), gp=gp.red) at <- grid.pretty(v$yscale) grid.yaxis(at=c(min(at), max(at)), gp=gp.red) grid.text(as.character(w), gp=gp.red, just=c("centre", "bottom"), x=unit(.5, "npc"), y=unit(1, "npc") + unit(.05, "inches")) grid.text(as.character(h), gp=gp.red, just=c("left", "centre"), x=unit(1, "npc") + unit(.05, "inches"), y=unit(.5, "npc")) popViewport() ## annotate the location and dimensions of the viewport grid.lines(unit.c(x, x), unit.c(unit(0, "npc"), y), gp=gpar(col=scale.col, lty="dashed")) grid.lines(unit.c(unit(0, "npc"), x), unit.c(y, y), gp=gpar(col=scale.col, lty="dashed")) grid.text(as.character(x), gp=gp.red, just=c("centre", "top"), x=x, y=unit(-.05, "inches")) grid.text(as.character(y), gp=gp.red, just=c("bottom"), x=unit(-.05, "inches"), y=y, rot=90) popViewport() if (!is.null(vp)) popViewport() } } ## old grid.legend <- function(pch, labels, frame=TRUE, hgap=unit(0.5, "lines"), vgap=unit(0.5, "lines"), default.units="lines", gp=gpar(), draw=TRUE, vp=NULL) { ## Type checking on arguments labels <- as.character(labels) nkeys <- length(labels) if (length(pch) != nkeys) stop("'pch' and 'labels' not the same length") if (!is.unit(hgap)) hgap <- unit(hgap, default.units) if (length(hgap) != 1) stop("'hgap' must be single unit") if (!is.unit(vgap)) vgap <- unit(vgap, default.units) if (length(vgap) != 1) stop("'vgap' must be single unit") gf <- grid.frame(layout=grid.layout(nkeys, 2), vp=vp, gp=gp, draw=FALSE) for (i in 1L:nkeys) { if (i==1) { symbol.border <- unit.c(vgap, hgap, vgap, hgap) text.border <- unit.c(vgap, unit(0, "npc"), vgap, hgap) } else { symbol.border <- unit.c(vgap, hgap, unit(0, "npc"), hgap) text.border <- unit.c(vgap, unit(0, "npc"), unit(0, "npc"), hgap) } grid.pack(gf, grid.points(.5, .5, pch=pch[i], draw=FALSE), col=1, row=i, border=symbol.border, width=unit(1, "lines"), height=unit(1, "lines"), force.width=TRUE, draw=FALSE) grid.pack(gf, grid.text(labels[i], x=0, y=.5, just=c("left", "centre"), draw=FALSE), col=2, row=i, border=text.border, draw=FALSE) } if (draw) grid.draw(gf) gf } legendGrob <- function(labels, nrow, ncol, byrow=FALSE, do.lines = has.lty || has.lwd, lines.first=TRUE, hgap=unit(1, "lines"), vgap=unit(1, "lines"), default.units="lines", pch, gp=gpar(), vp=NULL) { ## Type checking on arguments; labels: character, symbol or expression: labels <- as.graphicsAnnot(labels) labels <- if(is.character(labels)) as.list(labels) else as.expression(labels) nkeys <- if(is.call(labels)) 1 else length(labels) if(nkeys == 0) return(nullGrob(vp=vp)) if (!is.unit(hgap)) hgap <- unit(hgap, default.units) if (length(hgap) != 1) stop("'hgap' must be single unit") if (!is.unit(vgap)) vgap <- unit(vgap, default.units) if (length(vgap) != 1) stop("'vgap' must be single unit") ## nrow, ncol miss.nrow <- missing(nrow) miss.ncol <- missing(ncol) if(miss.nrow && miss.ncol) {ncol <- 1; nrow <- nkeys} # defaults to 1-column legend else if( miss.nrow && !miss.ncol) nrow <- ceiling(nkeys / ncol) else if(!miss.nrow && miss.ncol) ncol <- ceiling(nkeys / nrow) if(nrow < 1) stop("'nrow' must be >= 1") if(ncol < 1) stop("'ncol' must be >= 1") if(nrow * ncol < nkeys) stop("nrow * ncol < #{legend labels}") ## pch, gp if(has.pch <- !missing(pch) && length(pch) > 0) pch <- rep_len(pch, nkeys) if(doGP <- length(nmgp <- names(gp)) > 0) { if(has.lty <- "lty" %in% nmgp) gp$lty <- rep_len(gp$lty, nkeys) if(has.lwd <- "lwd" %in% nmgp) gp$lwd <- rep_len(gp$lwd, nkeys) if(has.col <- "col" %in% nmgp) gp$col <- rep_len(gp$col, nkeys) if(has.fill <- "fill" %in% nmgp) gp$fill <- rep_len(gp$fill, nkeys) } else { gpi <- gp if(missing(do.lines)) do.lines <- FALSE } ## main u0 <- unit(0, "npc") u1 <- unit(1, "char") ord <- if(lines.first) 1:2 else 2:1 fg <- frameGrob(vp = vp) # set up basic frame grob (for packing) for (i in seq_len(nkeys)) { if(doGP) { gpi <- gp if(has.lty) gpi$lty <- gp$lty[i] if(has.lwd) gpi$lwd <- gp$lwd[i] if(has.col) gpi$col <- gp$col[i] if(has.fill) gpi$fill<- gp$fill[i] } if(byrow) { ci <- 1+ (i-1) %% ncol ri <- 1+ (i-1) %/% ncol } else { ci <- 1+ (i-1) %/% nrow ri <- 1+ (i-1) %% nrow } ## borders; unit.c creates a 4-vector of borders (bottom, left, top, right) vg <- if(ri != nrow) vgap else u0 symbol.border <- unit.c(vg, u0, u0, 0.5 * hgap) text.border <- unit.c(vg, u0, u0, if(ci != ncol) hgap else u0) ## points/lines grob: plGrob <- if(has.pch && do.lines) gTree(children = gList(linesGrob (0:1, 0.5, gp=gpi), pointsGrob(0.5, 0.5, default.units="npc", pch=pch[i], gp=gpi))[ord]) else if(has.pch) pointsGrob(0.5, 0.5, default.units="npc", pch=pch[i], gp=gpi) else if(do.lines) linesGrob(0:1, 0.5, gp=gpi) else nullGrob() # should not happen... fg <- packGrob(fg, plGrob, col = 2*ci-1, row = ri, border = symbol.border, width = u1, height = u1, force.width = TRUE) ## text grob: add the labels gpi. <- gpi gpi.$col <- "black" # maybe needs its own 'gp' in the long run (?) fg <- packGrob(fg, textGrob(labels[[i]], x = 0, y = 0.5, just = c("left", "centre"), gp=gpi.), col = 2*ci, row = ri, border = text.border) } fg } grid.legend <- function(..., draw=TRUE) { g <- legendGrob(...)# will error out if '...' has nonsense if (draw) grid.draw(g) invisible(g) } ## Just a wrapper for a sample series of grid commands grid.plot.and.legend <- function() { grid.newpage() top.vp <- viewport(width=0.8, height=0.8) pushViewport(top.vp) x <- stats::runif(10) y1 <- stats::runif(10) y2 <- stats::runif(10) pch <- 1L:3 labels <- c("Girls", "Boys", "Other") lf <- frameGrob() plot <- gTree(children=gList(rectGrob(), pointsGrob(x, y1, pch=1), pointsGrob(x, y2, pch=2), xaxisGrob(), yaxisGrob())) lf <- packGrob(lf, plot) lf <- packGrob(lf, grid.legend(labels, pch=pch, draw=FALSE), height=unit(1,"null"), side="right") grid.draw(lf) }