blob: cec4b2c83f85bc82232b6c569b463d192b8a8b26 [file] [log] [blame]
# File src/library/grid/R/gpar.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/
# A "gpar" object is a list of graphics parameters
# A graphics parameter is a name-value pair
gpar <- function(...) {
gp <- validGP(list(...))
class(gp) <- "gpar"
gp
}
is.gpar <- function(x) {
inherits(x, "gpar")
}
print.gpar <- function(x, ...) {
print(unclass(x), ...)
invisible(x)
}
validGP <- function(gpars) {
# Check a (non-NULL) gpar is not of length 0
check.length <- function(gparname) {
if (length(gpars[[gparname]]) == 0)
stop(gettextf("'gpar' element '%s' must not be length 0", gparname),
domain = NA)
}
# Check a gpar is numeric and not NULL
numnotnull <- function(gparname) {
if (!is.na(match(gparname, names(gpars)))) {
if (is.null(gpars[[gparname]]))
gpars[[gparname]] <<- NULL
else {
check.length(gparname)
gpars[[gparname]] <<- as.numeric(gpars[[gparname]])
}
}
}
checkNA <- function(gparname) {
if (!is.na(match(gparname, names(gpars)))) {
if (any(is.na(gpars[[gparname]]))) {
# ALL NA gets removed (ignored)
if (all(is.na(gpars[[gparname]]))) {
gpars[[gparname]] <<- NULL
} else {
stop(gettextf("mixture of missing and non-missing values for %s",
gparname),
domain=NA)
}
}
}
}
# fontsize, lineheight, cex, lwd should be numeric and not NULL
numnotnull("fontsize")
checkNA("fontsize")
numnotnull("lineheight")
checkNA("lineheight")
numnotnull("cex")
checkNA("cex")
numnotnull("lwd")
numnotnull("lex")
# gamma defunct in 2.7.0
if ("gamma" %in% names(gpars)) {
warning("'gamma' 'gpar' element is defunct")
gpars$gamma <- NULL
}
numnotnull("alpha")
# col and fill are converted in C code
# BUT still want to check length > 0
if (!is.na(match("col", names(gpars)))) {
if (is.null(gpars$col))
gpars$col <- NULL
else
check.length("col")
}
if (!is.na(match("fill", names(gpars)))) {
if (is.null(gpars$fill))
gpars$fill <- NULL
else
check.length("fill")
}
# lty converted in C code
# BUT still want to check for NULL and check length > 0
if (!is.na(match("lty", names(gpars)))) {
if (is.null(gpars$lty))
gpars$lty <- NULL
else
check.length("lty")
}
if (!is.na(match("lineend", names(gpars)))) {
if (is.null(gpars$lineend))
gpars$lineend <- NULL
else
check.length("lineend")
}
if (!is.na(match("linejoin", names(gpars)))) {
if (is.null(gpars$linejoin))
gpars$linejoin <- NULL
else
check.length("linejoin")
}
# linemitre should be larger than 1
numnotnull("linemitre")
if (!is.na(match("linemitre", names(gpars)))) {
if (any(gpars$linemitre < 1))
stop("invalid 'linemitre' value")
}
# alpha should be 0 to 1
if (!is.na(match("alpha", names(gpars)))) {
if (any(gpars$alpha < 0 || gpars$alpha > 1))
stop("invalid 'alpha' value")
}
# font should be integer and not NULL
if (!is.na(match("font", names(gpars)))) {
if (is.null(gpars$font))
gpars$font <- NULL
else {
check.length("font")
gpars$font <- as.integer(gpars$font)
}
}
# fontfamily should be character
if (!is.na(match("fontfamily", names(gpars)))) {
if (is.null(gpars$fontfamily))
gpars$fontfamily <- NULL
else {
check.length("fontfamily")
gpars$fontfamily <- as.character(gpars$fontfamily)
checkNA("fontfamily")
}
}
# fontface can be character or integer; map character to integer
# store value in font
# Illegal to specify both font and fontface
if (!is.na(match("fontface", names(gpars)))) {
if (!is.na(match("font", names(gpars))))
stop("must specify only one of 'font' and 'fontface'")
gpars$font <-
if (is.null(gpars$fontface)) NULL # remove it
else {
check.length("fontface")
if (is.numeric(gpars$fontface))
as.integer(gpars$fontface)
else
vapply(as.character(gpars$fontface),
function(ch) # returns integer
switch(ch,
plain = 1L,
bold = 2L,
italic=, oblique = 3L,
bold.italic = 4L,
symbol= 5L,
# These are Hershey variants
cyrillic=5L,
cyrillic.oblique=6L,
EUC = 7L,
stop("invalid fontface ", ch)), 0L)
}
}
gpars
}
# Method for subsetting "gpar" objects
`[.gpar` <- function(x, index, ...) {
if (length(x) == 0)
return(gpar())
maxn <- do.call("max", lapply(x, length))
newgp <- lapply(x, rep, length.out=maxn)
newgp <- lapply(X = newgp, FUN = "[", index, ...)
class(newgp) <- "gpar"
newgp
}
# possible gpar names
# The order must match the GP_* values in grid.h
.grid.gpar.names <- c("fill", "col", "gamma", "lty", "lwd", "cex",
"fontsize", "lineheight", "font", "fontfamily",
"alpha", "lineend", "linejoin", "linemitre",
"lex",
# Keep fontface at the end because it is never
# used in C code (it gets mapped to font)
"fontface")
set.gpar <- function(gp, engineDL=TRUE) {
if (!is.gpar(gp))
stop("argument must be a 'gpar' object")
temp <- grid.Call(C_getGPar)
# gamma defunct in 2.7.0
if ("gamma" %in% names(gp)) {
warning("'gamma' 'gpar' element is defunct")
gp$gamma <- NULL
}
# Special case "cex" (make it cumulative)
if (match("cex", names(gp), nomatch=0L))
tempcex <- temp$cex * gp$cex
else
tempcex <- temp$cex
# Special case "alpha" (make it cumulative)
if (match("alpha", names(gp), nomatch=0L))
tempalpha <- temp$alpha * gp$alpha
else
tempalpha <- temp$alpha
# Special case "lex" (make it cumulative)
if (match("lex", names(gp), nomatch=0L))
templex <- temp$lex * gp$lex
else
templex <- temp$lex
# All other gpars
temp[names(gp)] <- gp
temp$cex <- tempcex
temp$alpha <- tempalpha
temp$lex <- templex
if (engineDL) {
## Do this as a .Call.graphics to get it onto the base display list
grid.Call.graphics(C_setGPar, temp)
} else {
grid.Call(C_setGPar, temp)
}
}
get.gpar <- function(names=NULL) {
if (is.null(names)) {
result <- grid.Call(C_getGPar)
# drop gamma
result$gamma <- NULL
} else {
if (!is.character(names) ||
!all(names %in% .grid.gpar.names))
stop("must specify only valid 'gpar' names")
# gamma deprecated
if ("gamma" %in% names) {
warning("'gamma' 'gpar' element is defunct")
names <- names[-match("gamma", names)]
}
result <- unclass(grid.Call(C_getGPar))[names]
}
class(result) <- "gpar"
result
}
# When editing a gp slot, only update the specified gpars
# Assume gp is NULL or a gpar
# assume newgp is a gpar (and not NULL)
mod.gpar <- function(gp, newgp) {
if (is.null(gp))
gp <- newgp
else
gp[names(newgp)] <- newgp
gp
}