blob: 76d918d9e79d7f5d0fde73a47aec9f8eabd0b366 [file] [log] [blame]
# 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(format("["(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(format("["(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(format("["(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(format("["(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)
}