blob: fb16c0133e8ff61aebcb3f8f06dd3bcb794fa7b1 [file] [log] [blame]
# File src/library/grid/R/components.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/
grid.collection <- function(..., gp=gpar(), draw=TRUE, vp=NULL) {
.Defunct("gTree")
}
######################################
# AXES
######################################
# Axes are extended from the "gTree" class
# This means that the standard (e.g., draw.details)
# methods for gTrees will apply
# The children of an axis are fixed to be:
# NOTE that the `at' parameter is numeric (i.e., NOT a unit) for
# grid.xaxis and grid.yaxis. These functions assume a unit for the `at'
# values rather than letting the user specify a unit.
validDetails.axis <- function(x) {
if (!is.null(x$at)) {
x$at <- as.numeric(x$at)
if (length(x$at) < 1 || !all(is.finite(x$at)))
stop("invalid 'at' location in 'axis'")
}
if (!is.logical(x$label)) {
# labels specified
# Can only spec labels if at is not NULL
if (is.null(x$at))
stop("invalid to specify axis labels when 'at' is NULL")
# Must be either language object or string
x$label <- as.graphicsAnnot(x$label)
# Must be same number of labels as "at" locations
if (length(x$label) != length(x$at))
stop("'labels' and 'at' locations must have same length")
}
x$main <- as.logical(x$main)
x
}
makeContent.xaxis <- function(x) {
# If x$at is NULL, then we must calculate the
# tick marks on-the-fly
if (is.null(x$at)) {
x$at <- grid.pretty(current.viewport()$xscale)
# Add the new output as children
x <- addGrob(x, make.xaxis.major(x$at, x$main))
x <- addGrob(x, make.xaxis.ticks(x$at, x$main))
x <- updateXlabels(x)
# Apply any edits relevant to children
x <- applyEdits(x, x$edits)
}
x
}
# NOTE that this can't be for all axes because it needs to
# call make.XAXIS.ticks and make.XAXIS.labels
editDetails.xaxis <- function(x, specs) {
slot.names <- names(specs)
if ("at" %in% slot.names) {
# NOTE that grid.edit has already set x$at to the new value
# We might set at to NULL to get ticks recalculated at redraw
if (is.null(x$at)) {
x <- removeGrob(x, "major", warn=FALSE)
x <- removeGrob(x, "ticks", warn=FALSE)
x <- removeGrob(x, "labels", warn=FALSE)
} else {
x <- addGrob(x, make.xaxis.major(x$at, x$main))
x <- addGrob(x, make.xaxis.ticks(x$at, x$main))
x <- updateXlabels(x)
}
}
if ("label" %in% slot.names) {
if (!is.null(x$at))
x <- updateXlabels(x)
}
if ("main" %in% slot.names)
if (!is.null(x$at)) {
x <- addGrob(x, make.xaxis.major(x$at, x$main))
x <- addGrob(x, make.xaxis.ticks(x$at, x$main))
x <- updateXlabels(x)
}
x
}
make.xaxis.major <- function(at, main) {
if (main)
y <- c(0, 0)
else
y <- c(1, 1)
linesGrob(unit(c(min(at), max(at)), "native"),
unit(y, "npc"), name="major")
}
make.xaxis.ticks <- function(at, main) {
if (main) {
tick.y0 <- unit(0, "npc")
tick.y1 <- unit(-.5, "lines")
}
else {
tick.y0 <- unit(1, "npc")
tick.y1 <- unit(1, "npc") + unit(.5, "lines")
}
segmentsGrob(unit(at, "native"), tick.y0,
unit(at, "native"), tick.y1,
name="ticks")
}
make.xaxis.labels <- function(at, label, main) {
# FIXME: labels only character versions of "at"
if (main)
label.y <- unit(-1.5, "lines")
else
label.y <- unit(1, "npc") + unit(1.5, "lines")
if (is.logical(label))
labels <- as.character(at)
else
labels <- label
textGrob(labels, unit(at, "native"), label.y,
just="centre", rot=0,
check.overlap=TRUE, name="labels")
}
updateXlabels <- function(x) {
if (is.logical(x$label) && !x$label)
removeGrob(x, "labels", warn=FALSE)
else
addGrob(x, make.xaxis.labels(x$at, x$label, x$main))
}
xaxisGrob <- function(at=NULL, label=TRUE, main=TRUE,
edits=NULL,
name=NULL, gp=gpar(), vp=NULL) {
grid.xaxis(at=at, label=label, main=main,
edits=edits,
name=name, gp=gp, draw=FALSE, vp=vp)
}
# The "main" x-axis is on the bottom when vp$origin is "bottom.*"
# and on the top when vp$origin is "top.*"
grid.xaxis <- function(at=NULL, label=TRUE, main=TRUE,
edits=NULL, name=NULL, gp=gpar(),
draw=TRUE, vp=NULL) {
if (is.null(at)) {
# We do not have enough information to make the ticks and labels
major <- NULL
ticks <- NULL
labels <- NULL
} else {
major <- make.xaxis.major(at, main)
ticks <- make.xaxis.ticks(at, main)
if (is.logical(label) && length(label) == 0)
stop("logical 'label' supplied of length 0")
if (is.logical(label) && !label)
labels <- NULL
else
labels <- make.xaxis.labels(at, label, main)
}
xg <- applyEdits(gTree(at=at, label=label, main=main,
children=gList(major, ticks, labels),
edits=edits,
name=name, gp=gp, vp=vp,
cl=c("xaxis", "axis")),
edits)
if (draw)
grid.draw(xg)
invisible(xg)
}
makeContent.yaxis <- function(x) {
# If x$at is NULL, then we must calculate the
# tick marks on-the-fly
if (is.null(x$at)) {
x$at <- grid.pretty(current.viewport()$yscale)
# Add the new output as children
x <- addGrob(x, make.yaxis.major(x$at, x$main))
x <- addGrob(x, make.yaxis.ticks(x$at, x$main))
x <- updateYlabels(x)
# Apply any edits relevant to children
x <- applyEdits(x, x$edits)
}
x
}
editDetails.yaxis <- function(x, specs) {
slot.names <- names(specs)
if ("at" %in% slot.names) {
if (is.null(x$at)) {
x <- removeGrob(x, "major", warn=FALSE)
x <- removeGrob(x, "ticks", warn=FALSE)
x <- removeGrob(x, "labels", warn=FALSE)
} else {
x <- addGrob(x, make.yaxis.major(x$at, x$main))
x <- addGrob(x, make.yaxis.ticks(x$at, x$main))
x <- updateYlabels(x)
}
}
if ("label" %in% slot.names) {
if (!is.null(x$at))
x <- updateYlabels(x)
}
if ("main" %in% slot.names)
if (!is.null(x$at)) {
x <- addGrob(x, make.yaxis.major(x$at, x$main))
x <- addGrob(x, make.yaxis.ticks(x$at, x$main))
x <- updateYlabels(x)
}
x
}
make.yaxis.major <- function(at, main) {
if (main)
x <- c(0, 0)
else
x <- c(1, 1)
linesGrob(unit(x, "npc"), unit(c(min(at), max(at)), "native"),
name="major")
}
make.yaxis.ticks <- function(at, main) {
if (main) {
tick.x0 <- unit(0, "npc")
tick.x1 <- unit(-.5, "lines")
}
else {
tick.x0 <- unit(1, "npc")
tick.x1 <- unit(1, "npc") + unit(.5, "lines")
}
segmentsGrob(tick.x0, unit(at, "native"),
tick.x1, unit(at, "native"),
name="ticks")
}
make.yaxis.labels <- function(at, label, main) {
if (main) {
hjust <- "right"
label.x <- unit(-1, "lines")
}
else {
hjust <- "left"
label.x <- unit(1, "npc") + unit(1, "lines")
}
just <- c(hjust, "centre")
if (is.logical(label))
labels <- as.character(at)
else
labels <- label
textGrob(labels, label.x, unit(at, "native"),
just=just, rot=0, check.overlap=TRUE, name="labels")
}
updateYlabels <- function(x) {
if (is.logical(x$label) && !x$label)
removeGrob(x, "labels", warn=FALSE)
else
addGrob(x, make.yaxis.labels(x$at, x$label, x$main))
}
yaxisGrob <- function(at=NULL, label=TRUE, main=TRUE,
edits=NULL,
name=NULL, gp=gpar(), vp=NULL) {
grid.yaxis(at=at, label=label, main=main, edits=edits,
name=name, gp=gp, draw=FALSE, vp=vp)
}
# The "main" y-axis is on the left when vp$origin is "*.left"
# and on the right when vp$origin is "*.right"
grid.yaxis <- function(at=NULL, label=TRUE, main=TRUE,
edits=NULL,
name=NULL, gp=gpar(),
draw=TRUE, vp=NULL) {
if (is.null(at)) {
# We do not have enough information to make the ticks and labels
major <- NULL
ticks <- NULL
labels <- NULL
} else {
major <- make.yaxis.major(at, main)
ticks <- make.yaxis.ticks(at, main)
if (is.logical(label) && length(label) == 0)
stop("logical 'label' supplied of length 0")
if (is.logical(label) && !label)
labels <- NULL
else
labels <- make.yaxis.labels(at, label, main)
}
yg <- applyEdits(gTree(at=at, label=label, main=main,
children=gList(major, ticks, labels),
edits=edits,
name=name, gp=gp, vp=vp,
cl=c("yaxis", "axis")),
edits)
if (draw)
grid.draw(yg)
invisible(yg)
}
######################################
# Simple "side-effect" plotting functions
######################################
grid.grill <- function(h=unit(seq(0.25, 0.75, 0.25), "npc"),
v=unit(seq(0.25, 0.75, 0.25), "npc"),
default.units="npc",
gp=gpar(col="grey"), vp=NULL) {
if (!is.unit(h))
h <- unit(h, default.units)
if (!is.unit(v))
v <- unit(v, default.units)
# FIXME: Should replace for loop and call to grid.lines with call to grid.segments
# once the latter exists
if (!is.null(vp))
pushViewport(vp)
grid.segments(v, unit(0, "npc"), v, unit(1, "npc"), gp=gp)
grid.segments(unit(0, "npc"), h, unit(1, "npc"), h, gp=gp)
if (!is.null(vp))
popViewport()
}