blob: b485466ce74b6a07f10e9a7d946fd24e4c825c19 [file] [log] [blame]
# File src/library/grid/R/primitives.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2018 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/
# Function that creates a description of an arrow head
# to add to a line
arrow <- function(angle=30, length=unit(0.25, "inches"),
ends="last", type="open") {
angle <- as.numeric(angle)
if (!is.unit(length))
stop("'length' must be a 'unit' object")
ends <- as.integer(match(ends, c("first", "last", "both")))
type <- as.integer(match(type, c("open", "closed")))
if (anyNA(ends) || anyNA(type) ||
length(ends) == 0 || length(type) == 0)
stop("invalid 'ends' or 'type' argument")
a <- list(angle=angle, length=length,
ends=ends, type=type)
class(a) <- "arrow"
a
}
length.arrow <- function(x) {
max(do.call("max", lapply(x, length)),
length(x$length))
}
rep.arrow <- function(x, ...) {
maxn <- length(x)
newa <- list(angle=rep(x$angle, length.out=maxn),
length=rep(x$length, length.out=maxn),
ends=rep(x$ends, length.out=maxn),
type=rep(x$type, length.out=maxn))
newa <- lapply(newa, rep, ...)
class(newa) <- "arrow"
newa
}
# Method for subsetting "arrow" objects
`[.arrow` <- function(x, index, ...) {
if (length(index) == 0)
return(NULL)
maxn <- length(x)
newa <- list(angle=rep(x$angle, length.out=maxn),
length=rep(x$length, length.out=maxn),
ends=rep(x$ends, length.out=maxn),
type=rep(x$type, length.out=maxn))
newa <- lapply(X = newa, FUN = "[", index, ...)
class(newa) <- "arrow"
newa
}
str.arrow <- function(object, ...) {
o <- oldClass(object)
oldClass(object) <- setdiff(o, "arrow")
str(object)
}
######################################
# move-to and line-to primitives
######################################
validDetails.move.to <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
# Make sure that x and y are of length 1
if (length(x$x) > 1 | length(x$y) > 1)
stop("'x' and 'y' must have length 1")
x
}
drawDetails.move.to <- function(x, recording=TRUE) {
grid.Call.graphics(C_moveTo, x$x, x$y)
}
moveToGrob <- function(x=0, y=0,
default.units="npc",
name=NULL, vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y,
name=name, vp=vp, cl="move.to")
}
grid.move.to <- function(x=0, y=0,
default.units="npc",
name=NULL, draw=TRUE, vp=NULL) {
mtg <- moveToGrob(x=x, y=y, default.units=default.units,
name=name, vp=vp)
if (draw)
grid.draw(mtg)
invisible(mtg)
}
validDetails.line.to <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
# Make sure that x and y are of length 1
if (length(x$x) > 1 | length(x$y) > 1)
stop("'x' and 'y' must have length 1")
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
x
}
drawDetails.line.to <- function(x, recording=TRUE) {
grid.Call.graphics(C_lineTo, x$x, x$y, x$arrow)
}
lineToGrob <- function(x=1, y=1,
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, arrow=arrow,
name=name, gp=gp, vp=vp, cl="line.to")
}
grid.line.to <- function(x=1, y=1,
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
ltg <- lineToGrob(x=x, y=y, default.units=default.units, arrow=arrow,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(ltg)
invisible(ltg)
}
######################################
# LINES primitive
######################################
validDetails.lines <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
x
}
drawDetails.lines <- function(x, recording=TRUE) {
grid.Call.graphics(C_lines, x$x, x$y,
list(as.integer(1L:max(length(x$x), length(x$y)))),
x$arrow)
}
xDetails.lines <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.lines <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.lines <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.lines <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
linesGrob <- function(x=unit(c(0, 1), "npc"),
y=unit(c(0, 1), "npc"),
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), vp=NULL) {
# Allow user to specify unitless vector; add default units
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y,
arrow=arrow, name=name, gp=gp, vp=vp, cl="lines")
}
grid.lines <- function(x=unit(c(0, 1), "npc"),
y=unit(c(0, 1), "npc"),
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
lg <- linesGrob(x=x, y=y,
default.units=default.units, arrow=arrow,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(lg)
invisible(lg)
}
######################################
# POLYLINES primitive
######################################
# Very similar to LINES primitive, but allows
# multiple polylines via 'id' and 'id.lengths' args
# as per POLYGON primitive
validDetails.polyline <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
if (!is.null(x$id) && !is.null(x$id.lengths))
stop("it is invalid to specify both 'id' and 'id.lengths'")
if (length(x$x) != length(x$y))
stop("'x' and 'y' must be same length")
if (!is.null(x$id) && (length(x$id) != length(x$x)))
stop("'x' and 'y' and 'id' must all be same length")
if (!is.null(x$id))
x$id <- as.integer(x$id)
if (!is.null(x$id.lengths) && (sum(x$id.lengths) != length(x$x)))
stop("'x' and 'y' and 'id.lengths' must specify same overall length")
if (!is.null(x$id.lengths))
x$id.lengths <- as.integer(x$id.lengths)
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
x
}
drawDetails.polyline <- function(x, recording=TRUE) {
if (is.null(x$id) && is.null(x$id.lengths))
grid.Call.graphics(C_lines, x$x, x$y,
list(as.integer(seq_along(x$x))),
x$arrow)
else {
if (is.null(x$id)) {
n <- length(x$id.lengths)
id <- rep(1L:n, x$id.lengths)
} else {
n <- length(unique(x$id))
id <- x$id
}
index <- split(as.integer(seq_along(x$x)), id)
grid.Call.graphics(C_lines, x$x, x$y, index, x$arrow)
}
}
xDetails.polyline <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.polyline <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.polyline <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.polyline <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
polylineGrob <- function(x=unit(c(0, 1), "npc"),
y=unit(c(0, 1), "npc"),
id=NULL, id.lengths=NULL,
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), vp=NULL) {
# Allow user to specify unitless vector; add default units
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, id=id, id.lengths=id.lengths,
arrow=arrow, name=name, gp=gp, vp=vp, cl="polyline")
}
grid.polyline <- function(...) {
grid.draw(polylineGrob(...))
}
######################################
# SEGMENTS primitive
######################################
validDetails.segments <- function(x) {
if (!is.unit(x$x0) || !is.unit(x$x1) ||
!is.unit(x$y0) || !is.unit(x$y1))
stop("'x0', 'y0', 'x1', and 'y1' must be units")
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
x
}
drawDetails.segments <- function(x, recording=TRUE) {
grid.Call.graphics(C_segments, x$x0, x$y0, x$x1, x$y1, x$arrow)
}
segmentBounds <- function(x, theta) {
n <- max(length(x$x0), length(x$x1),
length(x$y0), length(x$y1))
x0 <- rep(x$x0, length.out=n)
x1 <- rep(x$x1, length.out=n)
y0 <- rep(x$y0, length.out=n)
y1 <- rep(x$y1, length.out=n)
grid.Call(C_locnBounds, unit.c(x0, x1), unit.c(y0, y1), theta)
}
xDetails.segments <- function(x, theta) {
bounds <- segmentBounds(x, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.segments <- function(x, theta) {
bounds <- segmentBounds(x, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.segments <- function(x) {
bounds <- segmentBounds(x, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.segments <- function(x) {
bounds <- segmentBounds(x, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
segmentsGrob <- function(x0=unit(0, "npc"), y0=unit(0, "npc"),
x1=unit(1, "npc"), y1=unit(1, "npc"),
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), vp=NULL) {
# Allow user to specify unitless vector; add default units
if (!is.unit(x0))
x0 <- unit(x0, default.units)
if (!is.unit(x1))
x1 <- unit(x1, default.units)
if (!is.unit(y0))
y0 <- unit(y0, default.units)
if (!is.unit(y1))
y1 <- unit(y1, default.units)
grob(x0=x0, y0=y0, x1=x1, y1=y1, arrow=arrow, name=name, gp=gp, vp=vp,
cl="segments")
}
grid.segments <- function(x0=unit(0, "npc"), y0=unit(0, "npc"),
x1=unit(1, "npc"), y1=unit(1, "npc"),
default.units="npc",
arrow=NULL,
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
sg <- segmentsGrob(x0=x0, y0=y0, x1=x1, y1=y1,
default.units=default.units,
arrow=arrow,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(sg)
invisible(sg)
}
######################################
# ARROWS primitive
######################################
# Superceded by 'arrow' arg to line-drawing primitives
# which contains an "arrow" object
validDetails.arrows <- function(x) {
if ((!is.null(x$x) && !is.unit(x$x)) ||
(!is.null(x$y) && !is.unit(x$y)))
stop("'x' and 'y' must be units or NULL")
if (!is.unit(x$length))
stop("'length' must be a 'unit' object")
x$ends <- as.integer(match(x$ends, c("first", "last", "both")))
x$type <- as.integer(match(x$type, c("open", "closed")))
if (any(is.na(x$ends)) || any(is.na(x$type)))
stop("invalid 'ends' or 'type' argument")
x
}
drawDetails.arrows <- function(x, recording=TRUE) {
if (is.null(x$x)) { # y should be null too
if (!is.null(x$y))
stop("corrupt 'arrows' object")
lineThing <- getGrob(x, childNames(x))
# This could be done via method dispatch, but that really
# seemed like overkill
# OTOH, this is NOT user-extensible
# AND the code for, e.g., "lines" is not located with
# the other grid.lines code so changes there are unlikely
# to propagate to here (e.g., add an id arg to grid.lines?
if (inherits(lineThing, "line.to")) {
x1 <- NULL
x2 <- lineThing$x
y1 <- NULL
y2 <- lineThing$y
xnm1 <- NULL
xn <- lineThing$x
ynm1 <- NULL
yn <- lineThing$y
} else if (inherits(lineThing, "lines")) {
# x or y may be recycled
n <- max(length(lineThing$x),
length(lineThing$y))
xx <- rep(lineThing$x, length.out=2)
x1 <- xx[1L]
x2 <- xx[2L]
xx <- rep(lineThing$x, length.out=n)
xnm1 <- xx[n - 1]
xn <- xx[n]
yy <- rep(lineThing$y, length.out=2)
y1 <- yy[1L]
y2 <- yy[2L]
yy <- rep(lineThing$y, length.out=n)
ynm1 <- yy[n - 1]
yn <- yy[n]
} else { # inherits(lineThing, "segments")
x1 <- lineThing$x0
x2 <- lineThing$x1
xnm1 <- lineThing$x0
xn <- lineThing$x1
y1 <- lineThing$y0
y2 <- lineThing$y1
ynm1 <- lineThing$y0
yn <- lineThing$y1
}
} else {
# x or y may be recycled
n <- max(length(x$x), length(x$y))
xx <- rep(x$x, length.out=2)
x1 <- xx[1L]
x2 <- xx[2L]
xx <- rep(x$x, length.out=n)
xnm1 <- xx[n - 1]
xn <- xx[n]
yy <- rep(x$y, length.out=2)
y1 <- yy[1L]
y2 <- yy[2L]
yy <- rep(x$y, length.out=n)
ynm1 <- yy[n - 1]
yn <- yy[n]
grid.Call.graphics(C_lines, x$x, x$y,
list(as.integer(1L:n)),
NULL)
}
grid.Call.graphics(C_arrows, x1, x2, xnm1, xn, y1, y2, ynm1, yn,
x$angle, x$length, x$ends, x$type)
}
widthDetails.arrows <- function(x) {
if (is.null(x$x)) { # y should be null too
if (!is.null(x$y))
stop("corrupt 'arrows' object")
lineThing <- getGrob(x, childNames(x))
widthDetails(lineThing)
} else {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
}
heightDetails.arrows <- function(x) {
if (is.null(x$x)) { # y should be null too
if (!is.null(x$y))
stop("corrupt 'arrows' object")
lineThing <- getGrob(x, childNames(x))
heightDetails(lineThing)
} else {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
}
arrowsGrob <- function(x=c(0.25, 0.75), y=0.5,
default.units="npc",
grob=NULL,
angle=30, length=unit(0.25, "inches"),
ends="last", type="open",
name=NULL, gp=gpar(), vp=NULL) {
.Defunct(msg="'arrowsGrob' is defunct; use 'arrow' arguments to line drawing functions")
}
grid.arrows <- function(x=c(0.25, 0.75), y=0.5,
default.units="npc",
grob=NULL,
angle=30, length=unit(0.25, "inches"),
ends="last", type="open",
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
.Defunct(msg="'grid.arrows' is defunct; use 'arrow' arguments to line drawing functions")
}
######################################
# POLYGON primitive
######################################
validDetails.polygon <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
if (!is.null(x$id) && !is.null(x$id.lengths))
stop("it is invalid to specify both 'id' and 'id.lengths'")
if (length(x$x) != length(x$y))
stop("'x' and 'y' must be same length")
if (!is.null(x$id) && (length(x$id) != length(x$x)))
stop("'x' and 'y' and 'id' must all be same length")
if (!is.null(x$id))
x$id <- as.integer(x$id)
if (!is.null(x$id.lengths) && (sum(x$id.lengths) != length(x$x)))
stop("'x' and 'y' and 'id.lengths' must specify same overall length")
if (!is.null(x$id.lengths))
x$id.lengths <- as.integer(x$id.lengths)
x
}
drawDetails.polygon <- function(x, recording=TRUE) {
if (is.null(x$id) && is.null(x$id.lengths))
grid.Call.graphics(C_polygon, x$x, x$y,
list(as.integer(seq_along(x$x))))
else {
if (is.null(x$id)) {
n <- length(x$id.lengths)
id <- rep(1L:n, x$id.lengths)
} else {
n <- length(unique(x$id))
id <- x$id
}
index <- split(as.integer(seq_along(x$x)), id)
grid.Call.graphics(C_polygon, x$x, x$y, index)
}
}
xDetails.polygon <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.polygon <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.polygon <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.polygon <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
polygonGrob <- function(x=c(0, 0.5, 1, 0.5), y=c(0.5, 1, 0.5, 0),
id=NULL, id.lengths=NULL,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, id=id,
id.lengths=id.lengths,
name=name, gp=gp, vp=vp, cl="polygon")
}
grid.polygon <- function(x=c(0, 0.5, 1, 0.5), y=c(0.5, 1, 0.5, 0),
id=NULL, id.lengths=NULL,
default.units="npc",
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
pg <- polygonGrob(x=x, y=y, id=id, id.lengths=id.lengths,
default.units=default.units,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(pg)
invisible(pg)
}
######################################
# PATH primitive
######################################
validDetails.pathgrob <- function(x) {
if (!is.unit(x$x) || !is.unit(x$y))
stop("'x' and 'y' must be units")
if (!is.null(x$id) && !is.null(x$id.lengths))
stop("it is invalid to specify both 'id' and 'id.lengths'")
if (length(x$x) != length(x$y))
stop("'x' and 'y' must be same length")
if (!is.null(x$id) && (length(x$id) != length(x$x)))
stop("'x' and 'y' and 'id' must all be same length")
if (!is.null(x$id))
x$id <- as.integer(x$id)
if (!is.null(x$pathId))
x$pathId <- as.integer(x$pathId)
if (!is.null(x$id.lengths) && (sum(x$id.lengths) != length(x$x)))
stop("'x' and 'y' and 'id.lengths' must specify same overall length")
if (!is.null(x$pathId.lengths) && (sum(x$pathId.lengths) != length(x$x)))
stop("'x' and 'y' and 'pathId.lengths' must specify same overall length")
if (!is.null(x$id.lengths))
x$id.lengths <- as.integer(x$id.lengths)
if (!is.null(x$pathId.lengths))
x$pathId.lengths <- as.integer(x$pathId.lengths)
x
}
xDetails.pathgrob <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.pathgrob <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.pathgrob <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.pathgrob <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
drawDetails.pathgrob <- function(x, recording=TRUE) {
hasMultiple <- !(is.null(x$pathId) && is.null(x$pathId.lengths))
if (hasMultiple) {
if (is.null(x$pathId)) {
n <- length(x$pathId.lengths)
pathId <- rep(1L:n, x$pathId.lengths)
} else {
pathId <- x$pathId
}
}
if (is.null(x$id) && is.null(x$id.lengths)) {
if (hasMultiple) {
grid.Call.graphics(C_polygon, x$x, x$y,
split(as.integer(seq_along(x$x)), pathId))
} else {
grid.Call.graphics(C_polygon, x$x, x$y,
list(as.integer(seq_along(x$x))))
}
} else {
if (is.null(x$id)) {
n <- length(x$id.lengths)
id <- rep(1L:n, x$id.lengths)
} else {
n <- length(unique(x$id))
id <- x$id
}
if (hasMultiple) {
index <- mapply(split,
x=split(as.integer(seq_along(x$x)), pathId),
f=split(id, pathId),
SIMPLIFY = FALSE, USE.NAMES = FALSE)
} else {
index <- list(split(as.integer(seq_along(x$x)), id))
}
grid.Call.graphics(C_path, x$x, x$y, index,
switch(x$rule, winding=1L, evenodd=0L))
}
}
pathGrob <- function(x, y,
id=NULL, id.lengths=NULL,
pathId=NULL, pathId.lengths=NULL,
rule="winding",
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, id=id, id.lengths=id.lengths,
pathId=pathId, pathId.lengths=pathId.lengths,
rule=rule,
name=name, gp=gp, vp=vp, cl="pathgrob")
}
grid.path <- function(...) {
grid.draw(pathGrob(...))
}
######################################
# XSPLINE primitive
######################################
validDetails.xspline <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("x and y must be units")
if (!is.null(x$id) && !is.null(x$id.lengths))
stop("it is invalid to specify both 'id' and 'id.lengths'")
nx <- length(x$x)
ny <- length(x$y)
if (nx != ny)
stop("'x' and 'y' must be same length")
if (!is.null(x$id) && (length(x$id) != nx))
stop("'x' and 'y' and 'id' must all be same length")
if (!is.null(x$id))
x$id <- as.integer(x$id)
if (!is.null(x$id.lengths) && (sum(x$id.lengths) != nx))
stop("'x' and 'y' and 'id.lengths' must specify same overall length")
if (!is.null(x$id.lengths))
x$id.lengths <- as.integer(x$id.lengths)
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
if (any(x$shape < -1 | x$shape > 1))
stop("'shape' must be between -1 and 1")
x$open <- as.logical(x$open)
# Force all first and last shapes to be 0 for open xsplines
if (x$open) {
x$shape <- rep(x$shape, length.out=nx)
# Watch out for id or id.length!
index <- xsplineIndex(x)
first <- sapply(index, min)
last <- sapply(index, max)
x$shape[c(first, last)] <- 0
}
x
}
xsplineIndex <- function(x) {
if (is.null(x$id) && is.null(x$id.lengths))
list(as.integer(seq_along(x$x)))
else {
if (is.null(x$id)) {
n <- length(x$id.lengths)
id <- rep(1L:n, x$id.lengths)
} else {
n <- length(unique(x$id))
id <- x$id
}
split(as.integer(seq_along(x$x)), id)
}
}
drawDetails.xspline <- function(x, recording=TRUE) {
grid.Call.graphics(C_xspline, x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, xsplineIndex(x))
}
xDetails.xspline <- function(x, theta) {
bounds <- grid.Call(C_xsplineBounds, x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, xsplineIndex(x), theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.xspline <- function(x, theta) {
bounds <- grid.Call(C_xsplineBounds, x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, xsplineIndex(x), theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.xspline <- function(x) {
bounds <- grid.Call(C_xsplineBounds, x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, list(as.integer(seq_along(x$x))), 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.xspline <- function(x) {
bounds <- grid.Call(C_xsplineBounds, x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, list(as.integer(seq_along(x$x))), 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
xsplineGrob <- function(x=c(0, 0.5, 1, 0.5), y=c(0.5, 1, 0.5, 0),
id=NULL, id.lengths=NULL,
default.units="npc",
shape=0, open=TRUE, arrow=NULL, repEnds=TRUE,
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, shape=shape, open=open,
id=id, id.lengths=id.lengths, arrow=arrow, repEnds=repEnds,
name=name, gp=gp, vp=vp, cl="xspline")
}
grid.xspline <- function(...) {
grid.draw(xsplineGrob(...))
}
xsplinePoints <- function(x) {
# Mimic drawGrob() to ensure x$vp and x$gp enforced
dlon <- grid.Call(C_setDLon, FALSE)
on.exit(grid.Call(C_setDLon, dlon))
tempgpar <- grid.Call(C_getGPar)
on.exit(grid.Call(C_setGPar, tempgpar), add=TRUE)
preDraw(x)
# Raw pts in dev coords
devPoints <- grid.Call(C_xsplinePoints,
x$x, x$y, x$shape, x$open, x$arrow,
x$repEnds, xsplineIndex(x), 0)
postDraw(x)
# Convert to units in inches
unitPoints <- lapply(devPoints,
function(x) {
names(x) <- c("x", "y")
x$x <- unit(x$x, "inches")
x$y <- unit(x$y, "inches")
x
})
if (length(unitPoints) == 1)
unitPoints <- unitPoints[[1]]
unitPoints
}
######################################
# BEZIER primitive
######################################
# A bezier grob that works of a (not-100% accurate) approximation
# using X-splines
# X-Spline approx to Bezier
Ms <- 1/6*rbind(c(1, 4, 1, 0),
c(-3, 0, 3, 0),
c(3, -6, 3, 0),
c(-1, 3, -3, 1))
Msinv <- solve(Ms)
# Bezier control matrix
Mb <- rbind(c(1, 0, 0, 0),
c(-3, 3, 0, 0),
c(3, -6, 3, 0),
c(-1, 3, -3, 1))
splinePoints <- function(xb, yb, idIndex) {
xs <- unlist(lapply(idIndex,
function(i) {
Msinv %*% Mb %*% xb[i]
}))
ys <- unlist(lapply(idIndex,
function(i) {
Msinv %*% Mb %*% yb[i]
}))
list(x=xs, y=ys)
}
splinegrob <- function(x) {
xx <- convertX(x$x, "inches", valueOnly=TRUE)
yy <- convertY(x$y, "inches", valueOnly=TRUE)
sp <- splinePoints(xx, yy, xsplineIndex(x))
xsplineGrob(sp$x, sp$y, default.units="inches",
id=x$id, id.lengths=x$id.lengths,
shape=1, repEnds=FALSE,
arrow=x$arrow, name=x$name,
gp=x$gp, vp=x$vp)
}
validDetails.beziergrob <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("x and y must be units")
if (!is.null(x$id) && !is.null(x$id.lengths))
stop("it is invalid to specify both 'id' and 'id.lengths'")
nx <- length(x$x)
ny <- length(x$y)
if (nx != ny)
stop("'x' and 'y' must be same length")
if (!is.null(x$id) && (length(x$id) != nx))
stop("'x' and 'y' and 'id' must all be same length")
if (!is.null(x$id))
x$id <- as.integer(x$id)
if (!is.null(x$id.lengths) && (sum(x$id.lengths) != nx))
stop("'x' and 'y' and 'id.lengths' must specify same overall length")
if (!is.null(x$id.lengths))
x$id.lengths <- as.integer(x$id.lengths)
if (is.null(x$id) && is.null(x$id.lengths)) {
if (length(x$x) != 4L)
stop("must have exactly 4 control points")
} else {
if (is.null(x$id)) {
n <- length(x$id.lengths)
id <- rep(1L:n, x$id.lengths)
} else {
id <- x$id
}
xper <- split(x$x, id)
if (any(lengths(xper) != 4L))
stop("must have exactly 4 control points per Bezier curve")
}
if (!(is.null(x$arrow) || inherits(x$arrow, "arrow")))
stop("invalid 'arrow' argument")
x
}
makeContent.beziergrob <- function(x) {
splinegrob(x)
}
xDetails.beziergrob <- function(x, theta) {
xDetails(splinegrob(x), theta)
}
yDetails.beziergrob <- function(x, theta) {
yDetails(splinegrob(x), theta)
}
widthDetails.beziergrob <- function(x) {
widthDetails(splinegrob(x))
}
heightDetails.beziergrob <- function(x) {
heightDetails(splinegrob(x))
}
bezierGrob <- function(x=c(0, 0.5, 1, 0.5), y=c(0.5, 1, 0.5, 0),
id=NULL, id.lengths=NULL,
default.units="npc", arrow=NULL,
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y,
id=id, id.lengths=id.lengths, arrow=arrow,
name=name, gp=gp, vp=vp, cl="beziergrob")
}
grid.bezier <- function(...) {
grid.draw(bezierGrob(...))
}
bezierPoints <- function(x) {
sg <- splinegrob(x)
# splinegrob() does not make use of x$vp
sg$vp <- x$vp
xsplinePoints(sg)
}
######################################
# CIRCLE primitive
######################################
validDetails.circle <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y) ||
!is.unit(x$r))
stop("'x', 'y', and 'r' must be units")
x
}
drawDetails.circle <- function(x, recording=TRUE) {
grid.Call.graphics(C_circle, x$x, x$y, x$r)
}
xDetails.circle <- function(x, theta) {
bounds <- grid.Call(C_circleBounds, x$x, x$y, x$r, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.circle <- function(x, theta) {
bounds <- grid.Call(C_circleBounds, x$x, x$y, x$r, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.circle <- function(x) {
bounds <- grid.Call(C_circleBounds, x$x, x$y, x$r, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.circle <- function(x) {
bounds <- grid.Call(C_circleBounds, x$x, x$y, x$r, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
circleGrob <- function(x=0.5, y=0.5, r=0.5,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
if (!is.unit(r))
r <- unit(r, default.units)
grob(x=x, y=y, r=r, name=name, gp=gp, vp=vp, cl="circle")
}
grid.circle <- function(x=0.5, y=0.5, r=0.5,
default.units="npc",
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
cg <- circleGrob(x=x, y=y, r=r,
default.units=default.units,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(cg)
invisible(cg)
}
######################################
# RECT primitive
######################################
validDetails.rect <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y) ||
!is.unit(x$width) ||
!is.unit(x$height))
stop("'x', 'y', 'width', and 'height' must be units")
valid.just(x$just)
if (!is.null(x$hjust))
x$hjust <- as.numeric(x$hjust)
if (!is.null(x$vjust))
x$vjust <- as.numeric(x$vjust)
x
}
drawDetails.rect <- function(x, recording=TRUE) {
grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust))
}
xDetails.rect <- function(x, theta) {
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.rect <- function(x, theta) {
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.rect <- function(x) {
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.rect <- function(x) {
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
rectGrob <- function(x=unit(0.5, "npc"), y=unit(0.5, "npc"),
width=unit(1, "npc"), height=unit(1, "npc"),
just="centre", hjust=NULL, vjust=NULL,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
if (!is.unit(width))
width <- unit(width, default.units)
if (!is.unit(height))
height <- unit(height, default.units)
grob(x=x, y=y, width=width, height=height, just=just,
hjust=hjust, vjust=vjust,
name=name, gp=gp, vp=vp, cl="rect")
}
grid.rect <- function(x=unit(0.5, "npc"), y=unit(0.5, "npc"),
width=unit(1, "npc"), height=unit(1, "npc"),
just="centre", hjust=NULL, vjust=NULL,
default.units="npc",
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
rg <- rectGrob(x=x, y=y, width=width, height=height, just=just,
hjust=hjust, vjust=vjust,
default.units=default.units,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(rg)
invisible(rg)
}
######################################
# RASTER primitive
######################################
validDetails.rastergrob <- function(x) {
if (!(is.raster(x$raster) || inherits(x$raster, "nativeRaster")))
x$raster <- as.raster(x$raster)
if (!is.unit(x$x) ||
!is.unit(x$y) ||
(!is.null(x$width) && !is.unit(x$width)) ||
(!is.null(x$height) && !is.unit(x$height)))
stop("'x', 'y', 'width', and 'height' must be units")
valid.just(x$just)
if (!is.null(x$hjust))
x$hjust <- as.numeric(x$hjust)
if (!is.null(x$vjust))
x$vjust <- as.numeric(x$vjust)
x
}
resolveRasterSize <- function(x) {
if (is.null(x$width)) {
if (is.null(x$height)) {
rasterRatio <- dim(x$raster)[1]/dim(x$raster)[2]
vpWidth <- convertWidth(unit(1, "npc"), "inches", valueOnly=TRUE)
vpHeight <- convertHeight(unit(1, "npc"), "inches", valueOnly=TRUE)
vpRatio <- vpHeight/vpWidth
if (rasterRatio > vpRatio) {
x$height <- unit(vpHeight, "inches")
x$width <- unit(vpHeight*dim(x$raster)[2]/dim(x$raster)[1],
"inches")
} else {
x$width <- unit(vpWidth, "inches")
x$height <- unit(vpWidth*dim(x$raster)[1]/dim(x$raster)[2],
"inches")
}
} else {
h <- convertHeight(x$height, "inches", valueOnly=TRUE)
x$width <- unit(h*dim(x$raster)[2]/dim(x$raster)[1],
"inches")
}
} else {
if (is.null(x$height)) {
w <- convertWidth(x$width, "inches", valueOnly=TRUE)
x$height <- unit(w*dim(x$raster)[1]/dim(x$raster)[2],
"inches")
}
}
x
}
drawDetails.rastergrob <- function(x, recording=TRUE) {
# At this point resolve NULL width/height based on
# image dimensions
x <- resolveRasterSize(x)
if (is.null(x$width)) {
if (is.null(x$height)) {
rasterRatio <- dim(x$raster)[1]/dim(x$raster)[2]
vpWidth <- convertWidth(unit(1, "npc"), "inches", valueOnly=TRUE)
vpHeight <- convertHeight(unit(1, "npc"), "inches", valueOnly=TRUE)
vpRatio <- vpHeight/vpWidth
if (rasterRatio > vpRatio) {
x$height <- unit(vpHeight, "inches")
x$width <- unit(vpHeight*dim(x$raster)[2]/dim(x$raster)[1],
"inches")
} else {
x$width <- unit(vpWidth, "inches")
x$height <- unit(vpWidth*dim(x$raster)[1]/dim(x$raster)[2],
"inches")
}
} else {
h <- convertHeight(x$height, "inches", valueOnly=TRUE)
x$width <- unit(h*dim(x$raster)[2]/dim(x$raster)[1],
"inches")
}
} else {
if (is.null(x$height)) {
w <- convertWidth(x$width, "inches", valueOnly=TRUE)
x$height <- unit(w*dim(x$raster)[1]/dim(x$raster)[2],
"inches")
}
}
grid.Call.graphics(C_raster, x$raster,
x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$interpolate)
}
xDetails.rastergrob <- function(x, theta) {
x <- resolveRasterSize(x)
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.rastergrob <- function(x, theta) {
x <- resolveRasterSize(x)
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.rastergrob <- function(x) {
x <- resolveRasterSize(x)
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.rastergrob <- function(x) {
x <- resolveRasterSize(x)
bounds <- grid.Call(C_rectBounds, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
rasterGrob <- function(image,
x=unit(0.5, "npc"), y=unit(0.5, "npc"),
width=NULL, height=NULL,
just="centre", hjust=NULL, vjust=NULL,
interpolate=TRUE,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (inherits(image, "nativeRaster"))
raster <- image
else
raster <- as.raster(image)
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
if (!is.null(width) && !is.unit(width))
width <- unit(width, default.units)
if (!is.null(height) && !is.unit(height))
height <- unit(height, default.units)
grob(raster=raster, x=x, y=y, width=width, height=height, just=just,
hjust=hjust, vjust=vjust, interpolate=interpolate,
name=name, gp=gp, vp=vp, cl="rastergrob")
}
grid.raster <- function(image,
x=unit(0.5, "npc"), y=unit(0.5, "npc"),
width=NULL, height=NULL,
just="centre", hjust=NULL, vjust=NULL,
interpolate=TRUE,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
rg <- rasterGrob(image,
x=x, y=y, width=width, height=height, just=just,
hjust=hjust, vjust=vjust, interpolate=interpolate,
default.units=default.units,
name=name, gp=gp, vp=vp)
grid.draw(rg)
}
######################################
# TEXT primitive
######################################
validDetails.text <- function(x) {
if (!is.language(x$label))
x$label <- as.character(x$label)
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
x$rot <- as.numeric(x$rot)
if (!all(is.finite(x$rot)) || length(x$rot) == 0)
stop("invalid 'rot' value")
valid.just(x$just)
if (!is.null(x$hjust))
x$hjust <- as.numeric(x$hjust)
if (!is.null(x$vjust))
x$vjust <- as.numeric(x$vjust)
x$check.overlap <- as.logical(x$check.overlap)
x
}
drawDetails.text <- function(x, recording=TRUE) {
grid.Call.graphics(C_text, as.graphicsAnnot(x$label),
x$x, x$y,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$rot, x$check.overlap)
}
xDetails.text <- function(x, theta) {
bounds <- grid.Call(C_textBounds, as.graphicsAnnot(x$label),
x$x, x$y,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$rot, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.text <- function(x, theta) {
bounds <- grid.Call(C_textBounds, as.graphicsAnnot(x$label),
x$x, x$y,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$rot, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.text <- function(x) {
bounds <- grid.Call(C_textBounds, as.graphicsAnnot(x$label),
x$x, x$y,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$rot, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.text <- function(x) {
bounds <- grid.Call(C_textBounds, as.graphicsAnnot(x$label),
x$x, x$y,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust),
x$rot, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
ascentDetails.text <- function(x) {
if (length(x$label) == 1) {
metrics <- grid.Call(C_stringMetric, as.graphicsAnnot(x$label))
unit(metrics[[1]], "inches")
} else {
heightDetails(x)
}
}
descentDetails.text <- function(x) {
if (length(x$label) == 1) {
metrics <- grid.Call(C_stringMetric, as.graphicsAnnot(x$label))
unit(metrics[[2]], "inches")
} else {
unit(0, "inches")
}
}
textGrob <- function(label, x=unit(0.5, "npc"), y=unit(0.5, "npc"),
just="centre", hjust=NULL, vjust=NULL,
rot=0, check.overlap=FALSE,
default.units="npc",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(label=label, x=x, y=y, just=just, hjust=hjust, vjust=vjust,
rot=rot, check.overlap=check.overlap,
name=name, gp=gp, vp=vp, cl="text")
}
grid.text <- function(label, x=unit(0.5, "npc"), y=unit(0.5, "npc"),
just="centre", hjust=NULL, vjust=NULL,
rot=0, check.overlap=FALSE,
default.units="npc",
name=NULL, gp=gpar(), draw=TRUE, vp=NULL) {
tg <- textGrob(label=label, x=x, y=y, just=just,
hjust=hjust, vjust=vjust, rot=rot,
check.overlap=check.overlap,
default.units=default.units,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(tg)
invisible(tg)
}
######################################
# POINTS primitive
######################################
valid.pch <- function(pch) {
if (length(pch) == 0L)
stop("zero-length 'pch'")
if (is.null(pch))
pch <- 1L
else if (!is.character(pch))
pch <- as.integer(pch)
pch
}
validDetails.points <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y) ||
!is.unit(x$size))
stop("'x', 'y' and 'size' must be units")
if (length(x$x) != length(x$y))
stop("'x' and 'y' must be 'unit' objects and have the same length")
x$pch <- valid.pch(x$pch)
x
}
drawDetails.points <- function(x, recording=TRUE) {
grid.Call.graphics(C_points, x$x, x$y, x$pch, x$size)
}
# FIXME: does not take into account the size of the symbols
xDetails.points <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.points <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
widthDetails.points <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[3L], "inches")
}
heightDetails.points <- function(x) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, 0)
if (is.null(bounds))
unit(0, "inches")
else
unit(bounds[4L], "inches")
}
pointsGrob <- function(x=stats::runif(10),
y=stats::runif(10),
pch=1, size=unit(1, "char"),
default.units="native",
name=NULL, gp=gpar(), vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, pch=pch, size=size,
name=name, gp=gp, vp=vp, cl="points")
}
grid.points <- function(x=stats::runif(10),
y=stats::runif(10),
pch=1, size=unit(1, "char"),
default.units="native",
name=NULL, gp=gpar(),
draw=TRUE, vp=NULL) {
pg <- pointsGrob(x=x, y=y, pch=pch, size=size,
default.units=default.units,
name=name, gp=gp, vp=vp)
if (draw)
grid.draw(pg)
invisible(pg)
}
######################################
# CLIP primitive
######################################
validDetails.clip <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y) ||
!is.unit(x$width) ||
!is.unit(x$height))
stop("'x', 'y', 'width', and 'height' must be units")
if (length(x$x) > 1 || length(x$y) > 1 ||
length(x$width) > 1 || length(x$height) > 1)
stop("'x', 'y', 'width', and 'height' must all be units of length 1")
valid.just(x$just)
if (!is.null(x$hjust))
x$hjust <- as.numeric(x$hjust)
if (!is.null(x$vjust))
x$vjust <- as.numeric(x$vjust)
x
}
drawDetails.clip <- function(x, recording=TRUE) {
grid.Call.graphics(C_clip, x$x, x$y, x$width, x$height,
resolveHJust(x$just, x$hjust),
resolveVJust(x$just, x$vjust))
}
clipGrob <- function(x=unit(0.5, "npc"), y=unit(0.5, "npc"),
width=unit(1, "npc"), height=unit(1, "npc"),
just="centre", hjust=NULL, vjust=NULL,
default.units="npc",
name=NULL, vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
if (!is.unit(width))
width <- unit(width, default.units)
if (!is.unit(height))
height <- unit(height, default.units)
grob(x=x, y=y, width=width, height=height, just=just,
hjust=hjust, vjust=vjust,
name=name, vp=vp, cl="clip")
}
grid.clip <- function(...) {
grid.draw(clipGrob(...))
}
######################################
# NULL primitive
######################################
validDetails.null <- function(x) {
if (!is.unit(x$x) ||
!is.unit(x$y))
stop("'x' and 'y' must be units")
if (length(x$x) > 1 || length(x$y) > 1)
stop("'x' and 'y' must all be units of length 1")
x
}
drawDetails.null <- function(x, recording=TRUE) {
# Deliberate null op.
# NOTE: nothing will go on the graphics engine DL
# This is ok I think because these grobs are only
# useful on the grid DL (for other grid code to query
# their size or location).
}
xDetails.null <- function(x, theta) {
bounds <- grid.Call(C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[1L], "inches")
}
yDetails.null <- function(x, theta) {
bounds <- grid.Call( C_locnBounds, x$x, x$y, theta)
if (is.null(bounds))
unit(0.5, "npc")
else
unit(bounds[2L], "inches")
}
# Deliberately ZERO
widthDetails.null <- function(x) {
unit(0, "inches")
}
heightDetails.null <- function(x) {
unit(0, "inches")
}
# A grob with GUARANTEED zero-width
# also GUARANTEED NOT to draw anything
nullGrob <- function(x=unit(0.5, "npc"), y=unit(0.5, "npc"),
default.units="npc",
name=NULL, vp=NULL) {
if (!is.unit(x))
x <- unit(x, default.units)
if (!is.unit(y))
y <- unit(y, default.units)
grob(x=x, y=y, name=name, vp=vp, cl="null")
}
# Convenient way to get nullGrob on the grid display list
grid.null <- function(...) {
grid.draw(nullGrob(...))
}