[med-svn] [r-cran-gtable] 11/14: New upstream version 0.2.0
Andreas Tille
tille at debian.org
Fri Sep 29 07:23:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-gtable.
commit 6bd5afd2e4e569d8be9a932dabaf3b3ef989a9e2
Author: Andreas Tille <tille at debian.org>
Date: Fri Sep 29 09:20:41 2017 +0200
New upstream version 0.2.0
---
DESCRIPTION | 19 +++
MD5 | 45 +++++++
NAMESPACE | 36 ++++++
NEWS.md | 21 ++++
R/add-grob.r | 69 +++++++++++
R/add-rows-cols.r | 85 +++++++++++++
R/add-space.r | 44 +++++++
R/align.r | 119 ++++++++++++++++++
R/filter.r | 30 +++++
R/grid.r | 81 ++++++++++++
R/gtable-layouts.r | 136 +++++++++++++++++++++
R/gtable.r | 258 +++++++++++++++++++++++++++++++++++++++
R/padding.r | 29 +++++
R/rbind-cbind.r | 85 +++++++++++++
R/trim.r | 36 ++++++
R/utils.r | 72 +++++++++++
R/z.r | 45 +++++++
README.md | 7 ++
debian/README.Debian | 7 --
debian/README.test | 9 --
debian/changelog | 22 ----
debian/compat | 1 -
debian/control | 27 ----
debian/copyright | 28 -----
debian/docs | 4 -
debian/rules | 7 --
debian/source/format | 1 -
debian/tests/control | 3 -
debian/tests/run-unit-test | 11 --
debian/upstream/metadata | 7 --
debian/watch | 3 -
man/bind.Rd | 29 +++++
man/gtable.Rd | 116 ++++++++++++++++++
man/gtable_add_cols.Rd | 41 +++++++
man/gtable_add_grob.Rd | 40 ++++++
man/gtable_add_padding.Rd | 32 +++++
man/gtable_add_rows.Rd | 41 +++++++
man/gtable_add_space.Rd | 24 ++++
man/gtable_col.Rd | 40 ++++++
man/gtable_filter.Rd | 43 +++++++
man/gtable_height.Rd | 15 +++
man/gtable_matrix.Rd | 54 ++++++++
man/gtable_row.Rd | 40 ++++++
man/gtable_show_layout.Rd | 15 +++
man/gtable_spacer.Rd | 21 ++++
man/gtable_trim.Rd | 32 +++++
man/gtable_width.Rd | 15 +++
man/is.gtable.Rd | 15 +++
man/print.gtable.Rd | 19 +++
man/z_arrange_gtables.Rd | 22 ++++
man/z_normalise.Rd | 23 ++++
tests/testthat.R | 4 +
tests/testthat/Rplots.pdf | Bin 0 -> 3830 bytes
tests/testthat/helper-grobs.r | 5 +
tests/testthat/helper-units.r | 6 +
tests/testthat/test-bind.r | 34 ++++++
tests/testthat/test-layout.r | 154 +++++++++++++++++++++++
tests/testthat/test-subsetting.r | 183 +++++++++++++++++++++++++++
tests/testthat/test-z-order.r | 82 +++++++++++++
59 files changed, 2362 insertions(+), 130 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..0d43d50
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,19 @@
+Package: gtable
+Version: 0.2.0
+Title: Arrange 'Grobs' in Tables
+Description: Tools to make it easier to work with "tables" of 'grobs'.
+Authors at R: person("Hadley", "Wickham", , "hadley at rstudio.com", c("aut", "cre"))
+Depends: R (>= 2.14)
+Imports: grid
+Suggests: testthat, covr
+License: GPL-2
+Collate: 'add-grob.r' 'add-rows-cols.r' 'add-space.r' 'grid.r'
+ 'gtable-layouts.r' 'gtable.r' 'rbind-cbind.r' 'utils.r'
+ 'trim.r' 'filter.r' 'align.r' 'padding.r' 'z.r'
+RoxygenNote: 5.0.1
+NeedsCompilation: no
+Packaged: 2016-02-26 13:06:10 UTC; hadley
+Author: Hadley Wickham [aut, cre]
+Maintainer: Hadley Wickham <hadley at rstudio.com>
+Repository: CRAN
+Date/Publication: 2016-02-26 15:23:14
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..222b06c
--- /dev/null
+++ b/MD5
@@ -0,0 +1,45 @@
+834c2d74c10a205f7cc75e6d11a27437 *DESCRIPTION
+24953d6374fd57d6378ea80fc6e5d0a3 *NAMESPACE
+1739646c3828af237b0f19a478cffe97 *NEWS.md
+aa2d8709ce2b301b8ea881d3496fcf55 *R/add-grob.r
+c1425f3b373b0599bf3fa011a7c0556d *R/add-rows-cols.r
+6b08411d0f18bb3dde965d4ab5386da9 *R/add-space.r
+7dfc1ac440b155a85bafc89afe132fca *R/align.r
+d48fb44491240e37551ba41efc82f05b *R/filter.r
+8ee72ad011f0aeefac64f7ba2901ef87 *R/grid.r
+4d96b8d175bf678993ba1040787a9702 *R/gtable-layouts.r
+18986f2b1e7299b2bace2103a1f1a354 *R/gtable.r
+1d01fb56456d45bac11d2a4dcbe05802 *R/padding.r
+06795e1197e28f1bb5744498dba02390 *R/rbind-cbind.r
+4b0ac4a9825b044026e7876424c55bd7 *R/trim.r
+dd8dd039d5b71ccca2668f927acb68d2 *R/utils.r
+1d504b5fcc6c82b8e7e8aa299b215581 *R/z.r
+397255a4b35061779dd5f6c6cd629b8f *README.md
+40f82d8bf9184dfd7122db82f5fa433a *man/bind.Rd
+0a42b01e10d3117d37fbc230e8e6f971 *man/gtable.Rd
+831fed7c891354b2aafc4cc663f4c4f1 *man/gtable_add_cols.Rd
+ec2648c0df22ac8b384592e45214e3ef *man/gtable_add_grob.Rd
+01199e8e89d1abd1c4f513fb2f97506f *man/gtable_add_padding.Rd
+207a726839308e7793d49c65fb7b5077 *man/gtable_add_rows.Rd
+73648b7763f5b73af715cb7f9967030a *man/gtable_add_space.Rd
+a50fc29dfc159bf448a19b94deeba398 *man/gtable_col.Rd
+bf87b34e484fabf5b21d9cf2f5ee2970 *man/gtable_filter.Rd
+d40446b4cf809b989d7b1c8a10928f1f *man/gtable_height.Rd
+5327269a58767b4e4f945eaa973f8781 *man/gtable_matrix.Rd
+05db7f3774dc814808bae44b9bb9aa89 *man/gtable_row.Rd
+cda657bddd395e10142bc824b719ad86 *man/gtable_show_layout.Rd
+77752908335dba386d9b07df474c16cb *man/gtable_spacer.Rd
+4e30951dac9d1c7c7ca803de9ee45249 *man/gtable_trim.Rd
+04428210fe7c892ed5917831884d7e4d *man/gtable_width.Rd
+0c1353fe67a0c7225d471f7e205141f5 *man/is.gtable.Rd
+08b8d71ed27f60e8cb5714d76accce10 *man/print.gtable.Rd
+be91124ea54f780027bec87ba0048d1f *man/z_arrange_gtables.Rd
+3e4c814df6dcb53c34872add3907309a *man/z_normalise.Rd
+3d2bbcf840223423f0471d741a6a33da *tests/testthat.R
+b3e512f11e296324c3f4ef75991a3d13 *tests/testthat/Rplots.pdf
+c3b9c9e87a6e6c5e4b63fcb87a909a15 *tests/testthat/helper-grobs.r
+c3e0b00fedce833baccb19f1941b714f *tests/testthat/helper-units.r
+c315b57f2b0397beb11679f27448019d *tests/testthat/test-bind.r
+9f95edcd48968ecf13d4a288ec9c256e *tests/testthat/test-layout.r
+750bb81e504099eb456d5802d2eb6cb8 *tests/testthat/test-subsetting.r
+322fd392f79b8e530830af4f80799997 *tests/testthat/test-z-order.r
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..5af29f1
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,36 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method("[",gtable)
+S3method("dimnames<-",gtable)
+S3method(cbind,gtable)
+S3method(dim,gtable)
+S3method(dimnames,gtable)
+S3method(heightDetails,gtable)
+S3method(length,gtable)
+S3method(makeContent,gtable)
+S3method(makeContext,gTableChild)
+S3method(makeContext,gtable)
+S3method(plot,gtable)
+S3method(print,gtable)
+S3method(rbind,gtable)
+S3method(t,gtable)
+S3method(widthDetails,gtable)
+export(gtable)
+export(gtable_add_col_space)
+export(gtable_add_cols)
+export(gtable_add_grob)
+export(gtable_add_padding)
+export(gtable_add_row_space)
+export(gtable_add_rows)
+export(gtable_col)
+export(gtable_col_spacer)
+export(gtable_filter)
+export(gtable_height)
+export(gtable_matrix)
+export(gtable_row)
+export(gtable_row_spacer)
+export(gtable_show_layout)
+export(gtable_trim)
+export(gtable_width)
+export(is.gtable)
+import(grid)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..a36a5ec
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,21 @@
+# gtable 0.2.0
+
+* Switch from `preDrawDetails()` and `postDrawDetails()` methods to
+ `makeContent()` and `makeContext()` methods (@pmur002, #50).
+ This is a better approach facilitiated by changes in grid. Learn more
+ at <https://journal.r-project.org/archive/2013-2/murrell.pdf>.
+
+* Added a `NEWS.md` file to track changes to the package.
+
+* Partial argument matches have been fixed.
+
+* Import grid instead of depending on it.
+
+# gtable 0.1.2
+
+* `print.gtable` now prints the z order of the grobs, and it no longer
+ sort the names by z order. Previously, the layout names were sorted by
+ z order, but the grobs weren't. This resulted in a mismatch between
+ the names and the grobs. It's better to not sort by z by default,
+ since that doesn't match how indexing works. The `zsort` option allows
+ the output to be sorted by z.
diff --git a/R/add-grob.r b/R/add-grob.r
new file mode 100644
index 0000000..5f1d5fa
--- /dev/null
+++ b/R/add-grob.r
@@ -0,0 +1,69 @@
+#' Add a single grob, possibly spanning multiple rows or columns.
+#'
+#' This only adds grobs into the table - it doesn't affect the table in
+#' any way. In the gtable model, grobs always fill up the complete table
+#' cell. If you want custom justification you might need to
+#'
+#' @param x a \code{\link{gtable}} object
+#' @param grobs a single grob or a list of grobs
+#' @param t a numeric vector giving the top extent of the grobs
+#' @param l a numeric vector giving the left extent of the grobs
+#' @param b a numeric vector giving the bottom extent of the grobs
+#' @param r a numeric vector giving the right extent of the grobs
+#' @param z a numeric vector giving the order in which the grobs should be
+#' plotted. Use \code{Inf} (the default) to plot above or \code{-Inf}
+#' below all existing grobs. By default positions are on the integers,
+#' giving plenty of room to insert new grobs between existing grobs.
+#' @param clip should drawing be clipped to the specified cells
+#' (\code{"on"}), the entire table (\code{"inherit"}), or not at all
+#' (\code{"off"})
+#' @param name name of the grob - used to modify the grob name before it's
+#' plotted.
+#' @export
+gtable_add_grob <- function(x, grobs, t, l, b = t, r = l, z = Inf, clip = "on", name = x$name)
+{
+ stopifnot(is.gtable(x))
+ if (is.grob(grobs)) grobs <- list(grobs)
+ stopifnot(is.list(grobs))
+
+ # Check that inputs have the right length
+ if(!all(vapply(list(t, r, b, l, z, clip, name), len_same_or_1,
+ logical(1), grobs))) {
+ stop("Not all inputs have either length 1 or same length same as 'grobs'")
+ }
+
+ # If z is just one value, replicate to same length as grobs
+ if (length(z) == 1) {
+ z <- rep(z, length(grobs))
+ }
+
+ # Get the existing z values from x$layout, and new non-Inf z-values
+ zval <- c(x$layout$z, z[!is.infinite(z)])
+ if (length(zval) == 0) {
+ # If there are no existing finite z values, set these so that
+ # -Inf values get assigned ..., -2, -1, 0 and
+ # +Inf values get assigned 1, 2, 3, ...
+ zmin <- 1
+ zmax <- 0
+ } else {
+ zmin <- min(zval)
+ zmax <- max(zval)
+ }
+ z[z == -Inf] <- zmin - rev(seq_len(sum(z == -Inf)))
+ z[z == Inf] <- zmax + seq_len(sum(z == Inf))
+
+ t <- neg_to_pos(t, nrow(x))
+ b <- neg_to_pos(b, nrow(x))
+ l <- neg_to_pos(l, ncol(x))
+ r <- neg_to_pos(r, ncol(x))
+
+ layout <- data.frame(t = t, l = l, b = b, r = r, z = z,
+ clip = clip, name = name,
+ stringsAsFactors = FALSE)
+ stopifnot(length(grobs) == nrow(layout))
+
+ x$grobs <- c(x$grobs, grobs)
+ x$layout <- rbind(x$layout, layout)
+
+ x
+}
diff --git a/R/add-rows-cols.r b/R/add-rows-cols.r
new file mode 100644
index 0000000..c4fac2b
--- /dev/null
+++ b/R/add-rows-cols.r
@@ -0,0 +1,85 @@
+#' Add new rows in specified position.
+#'
+#' @param x a \code{\link{gtable}} object
+#' @param heights a unit vector giving the heights of the new rows
+#' @param pos new row will be added below this position. Defaults to
+#' adding row on bottom. \code{0} adds on the top.
+#' @export
+#' @examples
+#' library(grid)
+#' rect <- rectGrob(gp = gpar(fill = "#00000080"))
+#' tab <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
+#' tab <- gtable_add_grob(tab, rect, t = 1, l = 1, r = 3)
+#' tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 1)
+#' tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 3)
+#' dim(tab)
+#' plot(tab)
+#'
+#' # Grobs will continue to span over new rows if added in the middle
+#' tab2 <- gtable_add_rows(tab, unit(1, "null"), 1)
+#' dim(tab2)
+#' plot(tab2)
+#'
+#' # But not when added to top (0) or bottom (-1, the default)
+#' tab3 <- gtable_add_rows(tab, unit(1, "null"))
+#' tab3 <- gtable_add_rows(tab3, unit(1, "null"), 0)
+#' dim(tab3)
+#' plot(tab3)
+gtable_add_rows <- function(x, heights, pos = -1) {
+ stopifnot(is.gtable(x))
+ stopifnot(length(pos) == 1)
+ n <- length(heights)
+
+ pos <- neg_to_pos(pos, nrow(x))
+
+ # Shift existing rows down
+ x$heights <- insert.unit(x$heights, heights, pos)
+ x$layout$t <- ifelse(x$layout$t > pos, x$layout$t + n, x$layout$t)
+ x$layout$b <- ifelse(x$layout$b > pos, x$layout$b + n, x$layout$b)
+
+ x
+}
+
+#' Add new columns in specified position.
+#'
+#' @param x a \code{\link{gtable}} object
+#' @param widths a unit vector giving the widths of the new columns
+#' @param pos new row will be added below this position. Defaults to
+#' adding col on right. \code{0} adds on the left.
+#' @export
+#' @examples
+#' library(grid)
+#' rect <- rectGrob(gp = gpar(fill = "#00000080"))
+#' tab <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
+#' tab <- gtable_add_grob(tab, rect, t = 1, l = 1, r = 3)
+#' tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 1)
+#' tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 3)
+#' dim(tab)
+#' plot(tab)
+#'
+#' # Grobs will continue to span over new rows if added in the middle
+#' tab2 <- gtable_add_cols(tab, unit(1, "null"), 1)
+#' dim(tab2)
+#' plot(tab2)
+#'
+#' # But not when added to left (0) or right (-1, the default)
+#' tab3 <- gtable_add_cols(tab, unit(1, "null"))
+#' tab3 <- gtable_add_cols(tab3, unit(1, "null"), 0)
+#' dim(tab3)
+#' plot(tab3)
+gtable_add_cols <- function(x, widths, pos = -1) {
+ stopifnot(is.gtable(x))
+ stopifnot(length(pos) == 1)
+ n <- length(widths)
+
+ pos <- neg_to_pos(pos, ncol(x))
+
+ # Shift existing columns right
+ x$widths <- insert.unit(x$widths, widths, pos)
+ x$layout$l <- ifelse(x$layout$l > pos, x$layout$l + n, x$layout$l)
+ x$layout$r <- ifelse(x$layout$r > pos, x$layout$r + n, x$layout$r)
+
+ x
+}
+
+
diff --git a/R/add-space.r b/R/add-space.r
new file mode 100644
index 0000000..eaa1a93
--- /dev/null
+++ b/R/add-space.r
@@ -0,0 +1,44 @@
+#' Add row/column spacing.
+#'
+#' Adds \code{width} space between the columns or \code{height} space between
+#' the rows.
+#'
+#' @name gtable_add_space
+#' @param x a gtable object
+NULL
+
+#' @param width a vector of units of length 1 or ncol - 1
+#' @export
+#' @rdname gtable_add_space
+gtable_add_col_space <- function(x, width) {
+ stopifnot(is.gtable(x))
+ n <- ncol(x) - 1
+ if (n == 0) return(x)
+
+ stopifnot(length(width) == 1 || length(width) == n)
+ width <- rep(width, length.out = n)
+
+ for(i in rev(seq_len(n))) {
+ x <- gtable_add_cols(x, width[i], pos = i)
+ }
+
+ x
+}
+
+#' @param height a vector of units of length 1 or nrow - 1
+#' @export
+#' @rdname gtable_add_space
+gtable_add_row_space <- function(x, height) {
+ stopifnot(is.gtable(x))
+ n <- nrow(x) - 1
+ if (n == 0) return(x)
+
+ stopifnot(length(height) == 1 || length(height) == n)
+ height <- rep(height, length.out = n)
+
+ for(i in rev(seq_len(n))) {
+ x <- gtable_add_rows(x, height[i], pos = i)
+ }
+
+ x
+}
diff --git a/R/align.r b/R/align.r
new file mode 100644
index 0000000..d968ee1
--- /dev/null
+++ b/R/align.r
@@ -0,0 +1,119 @@
+# Code does not currently work - need to thinking about how indexing a gtable
+# should work in more detail. How do the grobs move around?
+
+# Join two gtables together based on row/column names.
+#
+# @inheritParams gtable_align
+# @param along dimension to align along, \code{1} = rows, \code{2} = cols.
+# Join will occur perpendicular to this direction.
+# @examples
+# rect <- rectGrob(gp = gpar(fill = "black"))
+# circ <- circleGrob(gp = gpar(fill = "red"))
+# a <- gtable_col("a", list(rect, circ), width = unit(5, "cm"))
+# rownames(a) <- c("top", "mid")
+# b <- gtable_col("b", list(circ, rect), width = unit(5, "cm"))
+# rownames(b) <- c("mid", "bot")
+#
+# # Commented out example below because it causes R CMD check to fail
+# # when this function is not exported. Uncomment when this function
+# # is fixed and exported again.
+# # gtable_join(a, b)
+gtable_join <- function(x, y, along = 1L, join = "left") {
+ aligned <- gtable_align(x, y, along = along, join = join)
+ switch(along,
+ cbind(aligned$x, aligned$y),
+ rbind(aligned$x, aligned$y),
+ stop("along > 2 no implemented"))
+}
+
+# Align two gtables based on their row/col names.
+#
+# @param x \code{\link{gtable}}
+# @param y \code{\link{gtable}}
+# @param along dimension to align along, \code{1} = rows, \code{2} = cols.
+# @param join when x and y have different names, how should the difference
+# be resolved? \code{inner} keep names that appear in both, \code{outer}
+# keep names that appear in either, \code{left} keep names from \code{x},
+# and \code{right} keep names from \code{y}.
+# @seealso \code{\link{gtable_join}} to return the two gtables combined
+# in to a single gtable.
+# @return a list with elements \code{x} and \code{y} corresponding to the
+# input gtables with extra rows/columns so that they now align.
+gtable_align <- function(x, y, along = 1L, join = "left") {
+ join <- match.arg(join, c("left", "right", "inner", "outer"))
+
+ names_x <- dimnames(x)[[along]]
+ names_y <- dimnames(y)[[along]]
+
+ if (is.null(names_x) || is.null(names_y)) {
+ stop("Both gtables must have names along dimension to be aligned")
+ }
+
+ idx <- switch(join,
+ left = names_x,
+ right = names_y,
+ inner = intersect(names_x, names_y),
+ outer = union(names_x, names_y)
+ )
+
+ list(
+ x = gtable_reindex(x, idx, along),
+ y = gtable_reindex(y, idx, along)
+ )
+}
+
+# Reindex a gtable.
+#
+# @keywords internal
+# @examples
+# gt <- gtable(heights = unit(rep(1, 3), "cm"), rownames = c("a", "b", "c"))
+# rownames(gtable:::gtable_reindex(gt, c("a", "b", "c")))
+# rownames(gtable:::gtable_reindex(gt, c("a", "b")))
+# rownames(gtable:::gtable_reindex(gt, c("a")))
+# rownames(gtable:::gtable_reindex(gt, c("a", "d", "e")))
+gtable_reindex <- function(x, index, along = 1) {
+ stopifnot(is.character(index))
+ if (length(dim(x)) > 2L || along > 2L) {
+ stop("reindex only supports 2d objects")
+ }
+ old_index <- switch(along, rownames(x), colnames(x))
+ stopifnot(!is.null(old_index))
+
+ if (identical(index, old_index)) {
+ return(x)
+ }
+
+ if (!(old_index %contains% index)) {
+ missing <- setdiff(index, old_index)
+ # Create and add dummy space rows
+
+ if (along == 1L) {
+ spacer <- gtable(
+ widths = unit(rep(0, ncol(x)), "cm"),
+ heights = rep_along(unit(0, "cm"), missing),
+ rownames = missing)
+ x <- rbind(x, spacer, size = "first")
+ } else if (along == 2L){
+ spacer <- gtable(
+ heights = unit(rep(0, nrow(x)), "cm"),
+ widths = rep_along(unit(0, "cm"), missing),
+ colnames = missing)
+
+ x <- cbind(x, spacer, size = "first")
+ }
+ }
+
+
+ # Reorder & subset
+
+ switch(along,
+ x[index, ],
+ x[, index])
+}
+
+"%contains%" <- function(x, y) all(y %in% x)
+
+rep_along <- function(x, y) {
+ if (length(y) == 0) return(NULL)
+ rep(x, length(y))
+}
diff --git a/R/filter.r b/R/filter.r
new file mode 100644
index 0000000..414d30b
--- /dev/null
+++ b/R/filter.r
@@ -0,0 +1,30 @@
+#' Filter cells by name.
+#'
+#' @param x a gtable object
+#' @inheritParams base::grepl
+#' @param trim if \code{TRUE}, \code{\link{gtable_trim}} will be used to trim
+#' off any empty cells.
+#' @export
+#' @examples
+#' library(grid)
+#' gt <- gtable(unit(rep(5, 3), c("cm")), unit(5, "cm"))
+#' rect <- rectGrob(gp = gpar(fill = "black"))
+#' circ <- circleGrob(gp = gpar(fill = "red"))
+#'
+#' gt <- gtable_add_grob(gt, rect, 1, 1, name = "rect")
+#' gt <- gtable_add_grob(gt, circ, 1, 3, name = "circ")
+#'
+#' plot(gtable_filter(gt, "rect"))
+#' plot(gtable_filter(gt, "rect", trim = FALSE))
+#' plot(gtable_filter(gt, "circ"))
+#' plot(gtable_filter(gt, "circ", trim = FALSE))
+gtable_filter <- function(x, pattern, fixed = FALSE, trim = TRUE) {
+
+ matches <- grepl(pattern, x$layout$name, fixed = fixed)
+ x$layout <- x$layout[matches, , drop = FALSE]
+ x$grobs <- x$grobs[matches]
+
+ if (trim) x <- gtable_trim(x)
+
+ x
+}
diff --git a/R/grid.r b/R/grid.r
new file mode 100644
index 0000000..96caec7
--- /dev/null
+++ b/R/grid.r
@@ -0,0 +1,81 @@
+#' Visualise the layout of a gtable.
+#'
+#' @export
+#' @param x a gtable object
+gtable_show_layout <- function(x) {
+ stopifnot(is.gtable(x))
+
+ grid.show.layout(gtable_layout(x))
+}
+
+gtable_layout <- function(x) {
+ stopifnot(is.gtable(x))
+
+ grid.layout(
+ nrow = nrow(x), heights = x$heights,
+ ncol = ncol(x), widths = x$widths,
+ respect = x$respect
+ )
+}
+
+vpname <- function(row) {
+ paste(row$name, ".", row$t, "-", row$r, "-", row$b, "-", row$l, sep = "")
+}
+
+#' @export
+widthDetails.gtable <- function(x) absolute.size(gtable_width(x))
+
+#' @export
+heightDetails.gtable <- function(x) absolute.size(gtable_height(x))
+
+#' @export
+makeContext.gtable <- function(x) {
+ layoutvp <- viewport(layout = gtable_layout(x), name = x$name)
+ if (is.null(x$vp)) {
+ x$vp <- layoutvp
+ } else {
+ x$vp <- vpStack(x$vp, layoutvp)
+ }
+ x
+}
+
+#' @export
+makeContent.gtable <- function(x) {
+ children_vps <- mapply(child_vp,
+ vp_name = vpname(x$layout),
+ t = x$layout$t, r = x$layout$r,
+ b = x$layout$b, l = x$layout$l,
+ clip = x$layout$clip,
+ SIMPLIFY = FALSE)
+ x$grobs <- mapply(wrap_gtableChild, x$grobs, children_vps,
+ SIMPLIFY = FALSE)
+ setChildren(x, do.call("gList", x$grobs[order(x$layout$z)]))
+}
+
+#' @export
+makeContext.gTableChild <- function(x) {
+ if (is.null(x$vp)) {
+ x$vp <- x$wrapvp
+ } else {
+ x$vp <- vpStack(x$wrapvp, x$vp)
+ }
+ # A gTableChild extends an arbitrary grob class
+ # so allow existing makeContext() behaviour of
+ # original grob class to still occur
+ NextMethod()
+}
+
+# Return the viewport for a child grob in a gtable
+child_vp <- function(vp_name, t, r, b, l, clip) {
+ viewport(name = vp_name, layout.pos.row = t:b,
+ layout.pos.col = l:r, clip = clip)
+}
+
+# Turn a grob into a gtableChild, and store information about the
+# viewport used within the gtable
+wrap_gtableChild <- function(grob, vp) {
+ grob$wrapvp <- vp
+ grob$name <- vp$name
+ class(grob) <- c("gTableChild", class(grob))
+ grob
+}
diff --git a/R/gtable-layouts.r b/R/gtable-layouts.r
new file mode 100644
index 0000000..8af20cd
--- /dev/null
+++ b/R/gtable-layouts.r
@@ -0,0 +1,136 @@
+#' Create a single column gtable.
+#'
+#' @inheritParams gtable
+#' @inheritParams gtable_add_grob
+#' @param width a unit vector giving the width of this column
+#' @param vp a grid viewport object (or NULL).
+#' @export
+#' @examples
+#' library(grid)
+#' a <- rectGrob(gp = gpar(fill = "red"))
+#' b <- circleGrob()
+#' c <- linesGrob()
+#' gt <- gtable_col("demo", list(a, b, c))
+#' gt
+#' plot(gt)
+#' gtable_show_layout(gt)
+gtable_col <- function(name, grobs, width = NULL, heights = NULL,
+ z = NULL, vp = NULL) {
+ width <- width %||% unit(max(unlist(lapply(grobs, width_cm))), "cm")
+ heights <- heights %||% rep(unit(1, "null"), length(grobs))
+
+ # z is either NULL, or a vector of the same length as grobs
+ stopifnot(is.null(z) || length(z) == length(grobs))
+ if (is.null(z))
+ z <- Inf
+
+ table <- gtable(name = name, vp = vp)
+
+ table <- gtable_add_rows(table, heights)
+ table <- gtable_add_cols(table, width)
+ table <- gtable_add_grob(table, grobs, t = seq_along(grobs), l = 1,
+ z = z, clip = "off")
+
+ table
+}
+
+#' Create a single row gtable.
+#'
+#' @inheritParams gtable
+#' @inheritParams gtable_add_grob
+#' @param height a unit vector giving the height of this row
+#' @param vp a grid viewport object (or NULL).
+#' @export
+#' @examples
+#' library(grid)
+#' a <- rectGrob(gp = gpar(fill = "red"))
+#' b <- circleGrob()
+#' c <- linesGrob()
+#' gt <- gtable_row("demo", list(a, b, c))
+#' gt
+#' plot(gt)
+#' gtable_show_layout(gt)
+gtable_row <- function(name, grobs, height = NULL, widths = NULL,
+ z = NULL, vp = NULL) {
+ height <- height %||% unit(max(unlist(lapply(grobs, height_cm))), "cm")
+ widths <- widths %||% rep(unit(1, "null"), length(grobs))
+
+ # z is either NULL, or a vector of the same length as grobs
+ stopifnot(is.null(z) || length(z) == length(grobs))
+ if (is.null(z))
+ z <- Inf
+
+ table <- gtable(name = name, vp = vp)
+
+ table <- gtable_add_cols(table, widths)
+ table <- gtable_add_rows(table, height)
+ table <- gtable_add_grob(table, grobs, l = seq_along(grobs), t = 1,
+ z = z, clip = "off")
+
+ table
+}
+
+#' Create a gtable from a matrix of grobs.
+#'
+#' @export
+#' @inheritParams gtable
+#' @inheritParams gtable_add_grob
+#' @param z a numeric matrix of the same dimensions as \code{grobs},
+#' specifying the order that the grobs are drawn.
+#' @param vp a grid viewport object (or NULL).
+#' @examples
+#' library(grid)
+#' a <- rectGrob(gp = gpar(fill = "red"))
+#' b <- circleGrob()
+#' c <- linesGrob()
+#'
+#' row <- matrix(list(a, b, c), nrow = 1)
+#' col <- matrix(list(a, b, c), ncol = 1)
+#' mat <- matrix(list(a, b, c, nullGrob()), nrow = 2)
+#'
+#' gtable_matrix("demo", row, unit(c(1, 1, 1), "null"), unit(1, "null"))
+#' gtable_matrix("demo", col, unit(1, "null"), unit(c(1, 1, 1), "null"))
+#' gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"))
+#'
+#' # Can specify z ordering
+#' z <- matrix(c(3, 1, 2, 4), nrow = 2)
+#' gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"), z = z)
+gtable_matrix <- function(name, grobs, widths = NULL, heights = NULL,
+ z = NULL, respect = FALSE, clip = "on", vp = NULL) {
+
+ table <- gtable(name = name, respect = respect, vp = vp)
+
+ stopifnot(length(widths) == ncol(grobs))
+ stopifnot(length(heights) == nrow(grobs))
+ # z is either NULL or a matrix of the same dimensions as grobs
+ stopifnot(is.null(z) || identical(dim(grobs), dim(z)))
+ if (is.null(z))
+ z <- Inf
+
+ table <- gtable_add_cols(table, widths)
+ table <- gtable_add_rows(table, heights)
+
+ table <- gtable_add_grob(table, grobs, t = c(row(grobs)), l = c(col(grobs)),
+ z = as.vector(z), clip = clip)
+
+ table
+}
+
+#' Create a row/col spacer gtable.
+#'
+#' @name gtable_spacer
+NULL
+
+#' @param widths unit vector of widths
+#' @rdname gtable_spacer
+#' @export
+gtable_row_spacer <- function(widths) {
+ gtable_add_cols(gtable(), widths)
+}
+
+#' @param heights unit vector of heights
+#' @rdname gtable_spacer
+#' @export
+gtable_col_spacer <- function(heights) {
+ gtable_add_rows(gtable(), heights)
+}
diff --git a/R/gtable.r b/R/gtable.r
new file mode 100644
index 0000000..9a137f6
--- /dev/null
+++ b/R/gtable.r
@@ -0,0 +1,258 @@
+#' gtable
+#'
+#' @import grid
+#' @docType package
+#' @name gtable
+NULL
+
+#' Create a new grob table.
+#'
+#' A grob table captures all the information needed to layout grobs in a table
+#' structure. It supports row and column spanning, offers some tools to
+#' automatically figure out the correct dimensions, and makes it easy to
+#' align and combine multiple tables.
+#'
+#' Each grob is put in its own viewport - grobs in the same location are
+#' not combined into one cell. Each grob takes up the entire cell viewport
+#' so justification control is not available.
+#'
+#' It constructs both the viewports and the gTree needed to display the table.
+#'
+#' @section Components:
+#'
+#' There are three basics components to a grob table: the specification of
+#' table (cell heights and widths), the layout (for each grob, its position,
+#' name and other settings), and global parameters.
+#'
+#' It's easier to understand how \code{gtable} works if in your head you keep
+#' the table separate from it's contents. Each cell can have 0, 1, or many
+#' grobs inside. Each grob must belong to at least one cell, but can span
+#' across many cells.
+#'
+#' @section Layout:
+#'
+#' The layout details are stored in a data frame with one row for each grob,
+#' and columns:
+#'
+#' \itemize{
+#' \item \code{t} top extent of grob
+#' \item \code{r} right extent of grob
+#' \item \code{b} bottom extent of
+#' \item \code{l} left extent of grob
+#' \item \code{z} the z-order of the grob - used to reorder the grobs
+#' before they are rendered
+#' \item \code{clip} a string, specifying how the grob should be clipped:
+#' either \code{"on"}, \code{"off"} or \code{"inherit"}
+#' \item \code{name}, a character vector used to name each grob and its
+#' viewport
+#' }
+#'
+#' You should not need to modify this data frame directly - instead use
+#' functions like \code{gtable_add_grob}.
+#'
+#' @param widths a unit vector giving the width of each column
+#' @param heights a unit vector giving the height of each row
+#' @param respect a logical vector of length 1: should the aspect ratio of
+#' height and width specified in null units be respected. See
+#' \code{\link{grid.layout}} for more details
+#' @param name a string giving the name of the table. This is used to name
+#' the layout viewport
+#' @param rownames,colnames character vectors of row and column names, used
+#' for characteric subsetting, particularly for \code{gtable_align},
+#' and \code{gtable_join}.
+#' @param vp a grid viewport object (or NULL).
+#' @export
+#' @aliases gtable-package
+#' @seealso \code{\link{gtable_row}}, \code{\link{gtable_col}} and
+#' \code{\link{gtable_matrix}} for convenient ways of creating gtables.
+#' @examples
+#' library(grid)
+#' a <- gtable(unit(1:3, c("cm")), unit(5, "cm"))
+#' a
+#' gtable_show_layout(a)
+#'
+#' # Add a grob:
+#' rect <- rectGrob(gp = gpar(fill = "black"))
+#' a <- gtable_add_grob(a, rect, 1, 1)
+#' a
+#' plot(a)
+#'
+#' # gtables behave like matrices:
+#' dim(a)
+#' t(a)
+#' plot(t(a))
+#'
+#' # when subsetting, grobs are retained if their extents lie in the
+#' # rows/columns that retained.
+#'
+#' b <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))
+#' b <- gtable_add_grob(b, rect, 2, 2)
+#' b[1, ]
+#' b[, 1]
+#' b[2, 2]
+#'
+#' # gtable have row and column names
+#' rownames(b) <- 1:3
+#' rownames(b)[2] <- 200
+#' colnames(b) <- letters[1:3]
+#' dimnames(b)
+gtable <- function(widths = list(), heights = list(), respect = FALSE,
+ name = "layout", rownames = NULL, colnames = NULL, vp = NULL) {
+
+ if (length(widths) > 0) {
+ stopifnot(is.unit(widths))
+ stopifnot(is.null(colnames) || length(colnames == length(widths)))
+ }
+ if (length(heights) > 0) {
+ stopifnot(is.unit(heights))
+ stopifnot(is.null(rownames) || length(rownames == length(heights)))
+ }
+
+ layout <- data.frame(
+ t = numeric(), l = numeric(), b = numeric(), r = numeric(), z = numeric(),
+ clip = character(), name = character(), stringsAsFactors = FALSE)
+
+ if (!is.null(vp)) {
+ vp <- viewport(name = name,
+ x = vp$x, y = vp$y,
+ width = vp$width, height = vp$height,
+ just = vp$just, gp = vp$gp, xscale = vp$xscale,
+ yscale = vp$yscale, angle = vp$angle, clip = vp$clip)
+ }
+
+ gTree(
+ grobs = list(), layout = layout, widths = widths,
+ heights = heights, respect = respect, name = name,
+ rownames = rownames, colnames = colnames, vp = vp,
+ cl = "gtable")
+}
+
+#' Print a gtable object
+#'
+#' @param x A gtable object.
+#' @param zsort Sort by z values? Default \code{FALSE}.
+#' @param ... Other arguments (not used by this method).
+#' @export
+#' @method print gtable
+print.gtable <- function(x, zsort = FALSE, ...) {
+ cat("TableGrob (", nrow(x), " x ", ncol(x), ") \"", x$name, "\": ",
+ length(x$grobs), " grobs\n", sep = "")
+
+ if (nrow(x$layout) == 0) return()
+
+ pos <- as.data.frame(format(as.matrix(x$layout[c("t", "r", "b", "l")])),
+ stringsAsFactors = FALSE)
+ grobNames <- vapply(x$grobs, as.character, character(1))
+
+ info <- data.frame(
+ z = x$layout$z,
+ cells = paste("(", pos$t, "-", pos$b, ",", pos$l, "-", pos$r, ")", sep =""),
+ name = x$layout$name,
+ grob = grobNames
+ )
+ if (zsort) info <- info[order(x$layout$z), ]
+
+ print(info)
+}
+
+
+#' @export
+dim.gtable <- function(x) c(length(x$heights), length(x$widths))
+
+#' @export
+dimnames.gtable <- function(x, ...) list(x$rownames, x$colnames)
+
+#' @export
+"dimnames<-.gtable" <- function(x, value) {
+ x$rownames <- value[[1]]
+ x$colnames <- value[[2]]
+
+ if (anyDuplicated(x$rownames)) stop("rownames must be distinct",
+ call. = FALSE)
+ if (anyDuplicated(x$colnames)) stop("colnames must be distinct",
+ call. = FALSE)
+
+ x
+}
+
+#' @export
+plot.gtable <- function(x, ...) {
+ grid.newpage()
+ grid.rect(gp = gpar(fill = "grey95"))
+ grid <- seq(0, 1, length = 20)
+ grid.grill(h = grid, v = grid, gp = gpar(col = "white"))
+ grid.draw(x)
+}
+
+#' Is this a gtable?
+#'
+#' @param x object to test
+#' @export
+is.gtable <- function(x) {
+ inherits(x, "gtable")
+}
+
+#' @export
+t.gtable <- function(x) {
+ new <- x
+
+ new$layout$t <- x$layout$l
+ new$layout$r <- x$layout$b
+ new$layout$b <- x$layout$r
+ new$layout$l <- x$layout$t
+
+ new$widths <- x$heights
+ new$heights <- x$widths
+
+ new
+}
+
+#' @export
+"[.gtable" <- function(x, i, j) {
+ # Convert indicies to (named) numeric
+ rows <- stats::setNames(seq_along(x$heights), rownames(x))[i]
+ cols <- stats::setNames(seq_along(x$widths), colnames(x))[j]
+
+ i <- seq_along(x$heights) %in% seq_along(x$heights)[rows]
+ j <- seq_along(x$widths) %in% seq_along(x$widths)[cols]
+
+ x$heights <- x$heights[rows]
+ x$rownames <- x$rownames[rows]
+ x$widths <- x$widths[cols]
+ x$colnames <- x$colnames[cols]
+
+ keep <- x$layout$t %in% rows & x$layout$b %in% rows &
+ x$layout$l %in% cols & x$layout$r %in% cols
+ x$grobs <- x$grobs[keep]
+
+ adj_rows <- cumsum(!i)
+ adj_cols <- cumsum(!j)
+
+ x$layout$r <- x$layout$r - adj_cols[x$layout$r]
+ x$layout$l <- x$layout$l - adj_cols[x$layout$l]
+ x$layout$t <- x$layout$t - adj_rows[x$layout$t]
+ x$layout$b <- x$layout$b - adj_rows[x$layout$b]
+
+ # Drop the unused rows from layout
+ x$layout <- x$layout[keep, ]
+ x
+}
+
+#' @export
+length.gtable <- function(x) length(x$grobs)
+
+#' Returns the height of a gtable, in the gtable's units
+#'
+#' Note that unlike heightDetails.gtable, this can return relative units.
+#'
+#' @param x A gtable object
+#' @export
+gtable_height <- function(x) sum(x$heights)
+
+#' Returns the width of a gtable, in the gtable's units
+#'
+#' Note that unlike widthDetails.gtable, this can return relative units.
+#'
+#' @param x A gtable object
+#' @export
+gtable_width <- function(x) sum(x$widths)
diff --git a/R/padding.r b/R/padding.r
new file mode 100644
index 0000000..f19b193
--- /dev/null
+++ b/R/padding.r
@@ -0,0 +1,29 @@
+#' Add padding around edges of table.
+#'
+#' @param x a \code{\link{gtable}} object
+#' @param padding vector of length 4: top, right, bottom, left. Normal
+#' recycling rules apply.
+#' @export
+#' @examples
+#' library(grid)
+#' gt <- gtable(unit(1, "null"), unit(1, "null"))
+#' gt <- gtable_add_grob(gt, rectGrob(gp = gpar(fill = "black")), 1, 1)
+#'
+#' plot(gt)
+#' plot(cbind(gt, gt))
+#' plot(rbind(gt, gt))
+#'
+#' pad <- gtable_add_padding(gt, unit(1, "cm"))
+#' plot(pad)
+#' plot(cbind(pad, pad))
+#' plot(rbind(pad, pad))
+gtable_add_padding <- function(x, padding) {
+ padding <- rep(padding, length.out = 4)
+
+ x <- gtable_add_rows(x, pos = 0, heights = padding[1])
+ x <- gtable_add_cols(x, pos = -1, widths = padding[2])
+ x <- gtable_add_rows(x, pos = -1, heights = padding[3])
+ x <- gtable_add_cols(x, pos = 0, widths = padding[4])
+ x
+}
+
diff --git a/R/rbind-cbind.r b/R/rbind-cbind.r
new file mode 100644
index 0000000..38adae8
--- /dev/null
+++ b/R/rbind-cbind.r
@@ -0,0 +1,85 @@
+#' Row and column binding for gtables.
+#'
+#' @param ... gtables to combine (\code{x} and \code{y})
+#' @param size How should the widths (for rbind) and the heights (for cbind)
+#' be combined across the gtables: take values from \code{first},
+#' or \code{last} gtable, or compute the \code{min} or \code{max} values.
+#' Defaults to \code{max}.
+#' @param z A numeric vector indicating the relative z values of each gtable.
+#' The z values of each object in the resulting gtable will be modified
+#' to fit this order. If \code{NULL}, then the z values of obects within
+#' each gtable will not be modified.
+#' @name bind
+NULL
+
+#' @rdname bind
+#' @method rbind gtable
+#' @export
+rbind.gtable <- function(..., size = "max", z = NULL) {
+ gtables <- list(...)
+ if (!is.null(z)) {
+ gtables <- z_arrange_gtables(gtables, z)
+ }
+ Reduce(function(x, y) rbind_gtable(x, y, size = size), gtables)
+}
+
+rbind_gtable <- function(x, y, size = "max") {
+ stopifnot(ncol(x) == ncol(y))
+ if (nrow(x) == 0) return(y)
+ if (nrow(y) == 0) return(x)
+
+ y$layout$t <- y$layout$t + nrow(x)
+ y$layout$b <- y$layout$b + nrow(x)
+ x$layout <- rbind(x$layout, y$layout)
+
+ x$heights <- insert.unit(x$heights, y$heights)
+ x$rownames <- c(x$rownames, y$rownames)
+
+ size <- match.arg(size, c("first", "last", "max", "min"))
+ x$widths <- switch(size,
+ first = x$widths,
+ last = y$widths,
+ min = compare_unit(x$widths, y$widths, pmin),
+ max = compare_unit(x$widths, y$widths, pmax)
+ )
+
+ x$grobs <- append(x$grobs, y$grobs)
+
+ x
+}
+
+#' @rdname bind
+#' @method cbind gtable
+#' @export
+cbind.gtable <- function(..., size = "max", z = NULL) {
+ gtables <- list(...)
+ if (!is.null(z)) {
+ gtables <- z_arrange_gtables(gtables, z)
+ }
+ Reduce(function(x, y) cbind_gtable(x, y, size = size), gtables)
+}
+
+cbind_gtable <- function(x, y, size = "max") {
+ stopifnot(nrow(x) == nrow(y))
+ if (ncol(x) == 0) return(y)
+ if (ncol(y) == 0) return(x)
+
+ y$layout$l <- y$layout$l + ncol(x)
+ y$layout$r <- y$layout$r + ncol(x)
+ x$layout <- rbind(x$layout, y$layout)
+
+ x$widths <- insert.unit(x$widths, y$widths)
+ x$colnames <- c(x$colnames, y$colnames)
+
+ size <- match.arg(size, c("first", "last", "max", "min"))
+ x$heights <- switch(size,
+ first = x$heights,
+ last = y$heights,
+ min = compare_unit(x$heights, y$heights, pmin),
+ max = compare_unit(x$heights, y$heights, pmax)
+ )
+
+ x$grobs <- append(x$grobs, y$grobs)
+
+ x
+}
diff --git a/R/trim.r b/R/trim.r
new file mode 100644
index 0000000..72964d8
--- /dev/null
+++ b/R/trim.r
@@ -0,0 +1,36 @@
+#' Trim off empty cells.
+#'
+#' @param x a gtable object
+#' @export
+#' @examples
+#' library(grid)
+#' rect <- rectGrob(gp = gpar(fill = "black"))
+#' base <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))
+#'
+#' center <- gtable_add_grob(base, rect, 2, 2)
+#' plot(center)
+#' plot(gtable_trim(center))
+#'
+#' col <- gtable_add_grob(base, rect, 1, 2, 3, 2)
+#' plot(col)
+#' plot(gtable_trim(col))
+#'
+#' row <- gtable_add_grob(base, rect, 2, 1, 2, 3)
+#' plot(row)
+#' plot(gtable_trim(row))
+gtable_trim <- function(x) {
+ stopifnot(is.gtable(x))
+
+ w <- range(x$layout$l, x$layout$r)
+ h <- range(x$layout$t, x$layout$b)
+
+ x$widths <- x$widths[seq.int(w[1], w[2])]
+ x$heights <- x$heights[seq.int(h[1], h[2])]
+
+ x$layout$l <- x$layout$l - w[1] + 1
+ x$layout$r <- x$layout$r - w[1] + 1
+ x$layout$t <- x$layout$t - h[1] + 1
+ x$layout$b <- x$layout$b - h[1] + 1
+
+ x
+}
diff --git a/R/utils.r b/R/utils.r
new file mode 100644
index 0000000..0a3034a
--- /dev/null
+++ b/R/utils.r
@@ -0,0 +1,72 @@
+
+neg_to_pos <- function(x, max) {
+ ifelse(x >= 0, x, max + 1 + x)
+}
+
+compare_unit <- function(x, y, comp = `=`) {
+ if (length(x) == 0) return(y)
+ if (length(y) == 0) return(x)
+
+ x_val <- unclass(x)
+ y_val <- unclass(y)
+
+ x_unit <- attr(x, "unit")
+ y_unit <- attr(x, "unit")
+
+ if (!all(x_unit == y_unit)) {
+ stop("Comparison of units with different types currently not supported")
+ }
+
+ unit(comp(x_val, y_val), x_unit)
+}
+
+
+insert.unit <- function (x, values, after = length(x)) {
+ lengx <- length(x)
+ if (lengx == 0) return(values)
+ if (length(values) == 0) return(x)
+
+ if (after <= 0) {
+ unit.c(values, x)
+ } else if (after >= lengx) {
+ unit.c(x, values)
+ } else {
+ unit.c(x[1L:after], values, x[(after + 1L):lengx])
+ }
+}
+
+"%||%" <- function(a, b) {
+ if (!is.null(a)) a else b
+}
+
+width_cm <- function(x) {
+ if (is.grob(x)) {
+ convertWidth(grobWidth(x), "cm", TRUE)
+ } else if (is.list(x)) {
+ vapply(x, width_cm, numeric(1))
+ } else if (is.unit(x)) {
+ convertWidth(x, "cm", TRUE)
+ } else {
+ stop("Unknown input")
+ }
+}
+height_cm <- function(x) {
+ if (is.grob(x)) {
+ convertWidth(grobHeight(x), "cm", TRUE)
+ } else if (is.list(x)) {
+ vapply(x, height_cm, numeric(1))
+ } else if (is.unit(x)) {
+ convertHeight(x, "cm", TRUE)
+ } else {
+ stop("Unknown input")
+ }
+}
+
+# Check that x is same length as g, or length 1
+len_same_or_1 <- function(x, g) {
+ if(length(x) == 1 || length(x) == length(g)) {
+ TRUE
+ } else {
+ FALSE
+ }
+}
diff --git a/R/z.r b/R/z.r
new file mode 100644
index 0000000..103aafa
--- /dev/null
+++ b/R/z.r
@@ -0,0 +1,45 @@
+#' Normalise z values within a gtable object
+#'
+#' The z values within a gtable object can be any numeric values.
+#' This function will change them to integers (starting from 1),
+#' preserving the original order.
+#'
+#' Ties are handled by the \code{"first"} method: the first occurrence
+#' of a value wins.
+#'
+#' @param x A gtable object
+#' @param i The z value to start counting up from (default is 1)
+z_normalise <- function(x, i = 1) {
+ x$layout$z <- rank(x$layout$z, ties.method = "first") + i - 1
+ x
+}
+
+
+#' Arrange the z values within gtable objects
+#'
+#' This is usually used before rbinding or cbinding the gtables together.
+#' The resulting z values will be normalized.
+#'
+#' Ties are handled by the \code{"first"} method: the first occurrence
+#' of a value wins.
+#'
+#' @param gtables A list of gtable objects
+#' @param z A numeric vector of relative z values
+z_arrange_gtables <- function(gtables, z) {
+ if (length(gtables) != length(z)) {
+ stop("'gtables' and 'z' must be the same length")
+ }
+
+ # Keep track of largest z value encountered so far
+ zmax <- 0
+ # Go through each gtable, in the order of z
+ for (i in order(z)) {
+ # max() gives a warning if zero-length input
+ if (nrow(gtables[[i]]$layout) > 0) {
+ gtables[[i]] <- z_normalise(gtables[[i]], zmax + 1)
+ zmax <- max(gtables[[i]]$layout$z)
+ }
+ }
+
+ gtables
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..42816b6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# gtable
+
+[![Travis-CI Build Status](https://travis-ci.org/hadley/gtable.svg?branch=master)](https://travis-ci.org/hadley/gtable)
+[![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/gtable)](http://cran.r-project.org/package=gtable)
+[![Coverage Status](https://img.shields.io/codecov/c/github/hadley/gtable/master.svg)](https://codecov.io/github/hadley/gtable?branch=master)
+
+gtable provides internal tools used to draw ggplot2 graphics.
diff --git a/debian/README.Debian b/debian/README.Debian
deleted file mode 100644
index 4d4750a..0000000
--- a/debian/README.Debian
+++ /dev/null
@@ -1,7 +0,0 @@
-r-cran-gtable for Debian
-------------------------
-
-This package can be tested by loading it into R with the command
-'library(gtable)' in order to confirm its integrity.
-
- -- Ivo Maintz <ivo at maintz.de> Mon, 10 Dec 2012 10:03:04 +0100
diff --git a/debian/README.test b/debian/README.test
deleted file mode 100644
index 8d70ca3..0000000
--- a/debian/README.test
+++ /dev/null
@@ -1,9 +0,0 @@
-Notes on how this package can be tested.
-────────────────────────────────────────
-
-This package can be tested by running the provided test:
-
-cd tests
-LC_ALL=C R --no-save < testthat.R
-
-in order to confirm its integrity.
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index a2a8a81..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,22 +0,0 @@
-gtable (0.2.0-1) unstable; urgency=medium
-
- * New upstream version
- * Ivo Maintz is MIA, take over package into team maintenance
- * cme fix dpkg-control
- * add autopkgtest
- * canonical homepage for cran
-
- -- Andreas Tille <tille at debian.org> Tue, 18 Oct 2016 12:45:51 +0200
-
-gtable (0.1.2-1.1) unstable; urgency=low
-
- * Non-maintainer upload
- * Rebuild for R 3.0 (Closes: #706995)
-
- -- Don Armstrong <don at debian.org> Wed, 08 May 2013 15:13:25 -0700
-
-gtable (0.1.2-1) unstable; urgency=low
-
- * Initial release (Closes: #700861)
-
- -- Ivo Maintz <ivo at maintz.de> Sun, 24 Feb 2013 16:24:01 +0100
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index b1a21b7..0000000
--- a/debian/control
+++ /dev/null
@@ -1,27 +0,0 @@
-Source: gtable
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>
-Section: gnu-r
-Priority: optional
-Build-Depends: debhelper (>= 9.0.0),
- cdbs,
- r-base-dev
-Standards-Version: 3.9.8
-Vcs-Browser: https://anonscm.debian.org/viewvc/debian-med/trunk/packages/R/r-cran-gtable/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/R/r-cran-gtable/trunk/
-Homepage: https://cran.r-project.org/package=gtable
-
-Package: r-cran-gtable
-Architecture: all
-Depends: ${shlibs:Depends},
- ${misc:Depends},
- ${R:Depends}
-Suggests: r-cran-plyr
-Description: Arrange grobs in tables
- A grob table captures all the information needed to layout grobs in a
- table structure. It supports row and column spanning, offers some
- tools to automatically figure out the correct dimensions, and makes it
- easy to align and combine multiple tables.
- Each grob is put in its own viewport - grobs in the same location are
- not combined into one cell. Each grob takes up the entire cell viewport
- so justification control is not available.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 659611c..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,28 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: gtable
-Source: http://cran.r-project.org/web/packages/gtable/index.html
-
-Files: *
-Copyright: 2012 Hadley Wickham <h.wickham at gmail.com>
-License: GPL-2.0+
-
-Files: debian/*
-Copyright: 2012 Ivo Maintz <ivo at maintz.de>
-License: GPL-2.0+
-
-License: GPL-2.0+
- This package 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 package 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.
- .
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>
- .
- On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
diff --git a/debian/docs b/debian/docs
deleted file mode 100644
index af15354..0000000
--- a/debian/docs
+++ /dev/null
@@ -1,4 +0,0 @@
-NEWS.md
-tests
-debian/README.test
-debian/tests/run-unit-test
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 8c50502..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-
-include /usr/share/R/debian/r-cran.mk
-
-install/$(package)::
- chmod 644 debian/$(package)/usr/lib/R/site-library/$(cranName)/INDEX
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/tests/control b/debian/tests/control
deleted file mode 100644
index b044b0c..0000000
--- a/debian/tests/control
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests: run-unit-test
-Depends: @, r-cran-testthat
-Restrictions: allow-stderr
diff --git a/debian/tests/run-unit-test b/debian/tests/run-unit-test
deleted file mode 100644
index 568b640..0000000
--- a/debian/tests/run-unit-test
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh -e
-
-pkg=r-cran-gtable
-
-if [ "$ADTTMP" = "" ] ; then
- ADTTMP=`mktemp -d /tmp/${pkg}-test.XXXXXX`
- trap "rm -rf $ADTTMP" 0 INT QUIT ABRT PIPE TERM
-fi
-cd $ADTTMP
-cp -a /usr/share/doc/${pkg}/tests/* $ADTTMP
-LC_ALL=C R --no-save < testthat.R
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
deleted file mode 100644
index 222a1f4..0000000
--- a/debian/upstream/metadata
+++ /dev/null
@@ -1,7 +0,0 @@
-Archive: CRAN
-Contact: Hadley Wickham <h.wickham at gmail.com>
-Download: http://cran.r-project.org/src/contrib/
-Homepage: http://cran.r-project.org/web/packages/gtable/
-CRAN: gtable
-Name: gtable
-Watch: http://cran.r-project.org/src/contrib/gtable_([\d.-]*)\.tar.gz
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 0d0dd1e..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-version=3
-opts="uversionmangle=s/-/./" \
-http://cran.r-project.org/src/contrib/gtable_([\d.-]*)\.tar.gz
diff --git a/man/bind.Rd b/man/bind.Rd
new file mode 100644
index 0000000..07b0539
--- /dev/null
+++ b/man/bind.Rd
@@ -0,0 +1,29 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rbind-cbind.r
+\name{bind}
+\alias{bind}
+\alias{cbind.gtable}
+\alias{rbind.gtable}
+\title{Row and column binding for gtables.}
+\usage{
+\method{rbind}{gtable}(..., size = "max", z = NULL)
+
+\method{cbind}{gtable}(..., size = "max", z = NULL)
+}
+\arguments{
+\item{...}{gtables to combine (\code{x} and \code{y})}
+
+\item{size}{How should the widths (for rbind) and the heights (for cbind)
+be combined across the gtables: take values from \code{first},
+or \code{last} gtable, or compute the \code{min} or \code{max} values.
+Defaults to \code{max}.}
+
+\item{z}{A numeric vector indicating the relative z values of each gtable.
+The z values of each object in the resulting gtable will be modified
+to fit this order. If \code{NULL}, then the z values of obects within
+each gtable will not be modified.}
+}
+\description{
+Row and column binding for gtables.
+}
+
diff --git a/man/gtable.Rd b/man/gtable.Rd
new file mode 100644
index 0000000..6fca4f5
--- /dev/null
+++ b/man/gtable.Rd
@@ -0,0 +1,116 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable.r
+\docType{package}
+\name{gtable}
+\alias{gtable}
+\alias{gtable-package}
+\title{gtable}
+\usage{
+gtable(widths = list(), heights = list(), respect = FALSE,
+ name = "layout", rownames = NULL, colnames = NULL, vp = NULL)
+}
+\arguments{
+\item{widths}{a unit vector giving the width of each column}
+
+\item{heights}{a unit vector giving the height of each row}
+
+\item{respect}{a logical vector of length 1: should the aspect ratio of
+height and width specified in null units be respected. See
+\code{\link{grid.layout}} for more details}
+
+\item{name}{a string giving the name of the table. This is used to name
+the layout viewport}
+
+\item{rownames, colnames}{character vectors of row and column names, used
+for characteric subsetting, particularly for \code{gtable_align},
+and \code{gtable_join}.}
+
+\item{vp}{a grid viewport object (or NULL).}
+}
+\description{
+gtable
+
+A grob table captures all the information needed to layout grobs in a table
+structure. It supports row and column spanning, offers some tools to
+automatically figure out the correct dimensions, and makes it easy to
+align and combine multiple tables.
+}
+\details{
+Each grob is put in its own viewport - grobs in the same location are
+not combined into one cell. Each grob takes up the entire cell viewport
+so justification control is not available.
+
+It constructs both the viewports and the gTree needed to display the table.
+}
+\section{Components}{
+
+
+There are three basics components to a grob table: the specification of
+table (cell heights and widths), the layout (for each grob, its position,
+name and other settings), and global parameters.
+
+It's easier to understand how \code{gtable} works if in your head you keep
+the table separate from it's contents. Each cell can have 0, 1, or many
+grobs inside. Each grob must belong to at least one cell, but can span
+across many cells.
+}
+
+\section{Layout}{
+
+
+The layout details are stored in a data frame with one row for each grob,
+and columns:
+
+\itemize{
+ \item \code{t} top extent of grob
+ \item \code{r} right extent of grob
+ \item \code{b} bottom extent of
+ \item \code{l} left extent of grob
+ \item \code{z} the z-order of the grob - used to reorder the grobs
+ before they are rendered
+ \item \code{clip} a string, specifying how the grob should be clipped:
+ either \code{"on"}, \code{"off"} or \code{"inherit"}
+ \item \code{name}, a character vector used to name each grob and its
+ viewport
+}
+
+You should not need to modify this data frame directly - instead use
+functions like \code{gtable_add_grob}.
+}
+\examples{
+library(grid)
+a <- gtable(unit(1:3, c("cm")), unit(5, "cm"))
+a
+gtable_show_layout(a)
+
+# Add a grob:
+rect <- rectGrob(gp = gpar(fill = "black"))
+a <- gtable_add_grob(a, rect, 1, 1)
+a
+plot(a)
+
+# gtables behave like matrices:
+dim(a)
+t(a)
+plot(t(a))
+
+# when subsetting, grobs are retained if their extents lie in the
+# rows/columns that retained.
+
+b <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))
+b <- gtable_add_grob(b, rect, 2, 2)
+b[1, ]
+b[, 1]
+b[2, 2]
+
+# gtable have row and column names
+rownames(b) <- 1:3
+rownames(b)[2] <- 200
+colnames(b) <- letters[1:3]
+dimnames(b)
+}
+\seealso{
+\code{\link{gtable_row}}, \code{\link{gtable_col}} and
+ \code{\link{gtable_matrix}} for convenient ways of creating gtables.
+}
+
diff --git a/man/gtable_add_cols.Rd b/man/gtable_add_cols.Rd
new file mode 100644
index 0000000..648234d
--- /dev/null
+++ b/man/gtable_add_cols.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add-rows-cols.r
+\name{gtable_add_cols}
+\alias{gtable_add_cols}
+\title{Add new columns in specified position.}
+\usage{
+gtable_add_cols(x, widths, pos = -1)
+}
+\arguments{
+\item{x}{a \code{\link{gtable}} object}
+
+\item{widths}{a unit vector giving the widths of the new columns}
+
+\item{pos}{new row will be added below this position. Defaults to
+adding col on right. \code{0} adds on the left.}
+}
+\description{
+Add new columns in specified position.
+}
+\examples{
+library(grid)
+rect <- rectGrob(gp = gpar(fill = "#00000080"))
+tab <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
+tab <- gtable_add_grob(tab, rect, t = 1, l = 1, r = 3)
+tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 1)
+tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 3)
+dim(tab)
+plot(tab)
+
+# Grobs will continue to span over new rows if added in the middle
+tab2 <- gtable_add_cols(tab, unit(1, "null"), 1)
+dim(tab2)
+plot(tab2)
+
+# But not when added to left (0) or right (-1, the default)
+tab3 <- gtable_add_cols(tab, unit(1, "null"))
+tab3 <- gtable_add_cols(tab3, unit(1, "null"), 0)
+dim(tab3)
+plot(tab3)
+}
+
diff --git a/man/gtable_add_grob.Rd b/man/gtable_add_grob.Rd
new file mode 100644
index 0000000..7aeb1dc
--- /dev/null
+++ b/man/gtable_add_grob.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add-grob.r
+\name{gtable_add_grob}
+\alias{gtable_add_grob}
+\title{Add a single grob, possibly spanning multiple rows or columns.}
+\usage{
+gtable_add_grob(x, grobs, t, l, b = t, r = l, z = Inf, clip = "on",
+ name = x$name)
+}
+\arguments{
+\item{x}{a \code{\link{gtable}} object}
+
+\item{grobs}{a single grob or a list of grobs}
+
+\item{t}{a numeric vector giving the top extent of the grobs}
+
+\item{l}{a numeric vector giving the left extent of the grobs}
+
+\item{b}{a numeric vector giving the bottom extent of the grobs}
+
+\item{r}{a numeric vector giving the right extent of the grobs}
+
+\item{z}{a numeric vector giving the order in which the grobs should be
+ plotted. Use \code{Inf} (the default) to plot above or \code{-Inf}
+ below all existing grobs. By default positions are on the integers,
+giving plenty of room to insert new grobs between existing grobs.}
+
+\item{clip}{should drawing be clipped to the specified cells
+(\code{"on"}), the entire table (\code{"inherit"}), or not at all
+(\code{"off"})}
+
+\item{name}{name of the grob - used to modify the grob name before it's
+plotted.}
+}
+\description{
+This only adds grobs into the table - it doesn't affect the table in
+any way. In the gtable model, grobs always fill up the complete table
+cell. If you want custom justification you might need to
+}
+
diff --git a/man/gtable_add_padding.Rd b/man/gtable_add_padding.Rd
new file mode 100644
index 0000000..78346b9
--- /dev/null
+++ b/man/gtable_add_padding.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/padding.r
+\name{gtable_add_padding}
+\alias{gtable_add_padding}
+\title{Add padding around edges of table.}
+\usage{
+gtable_add_padding(x, padding)
+}
+\arguments{
+\item{x}{a \code{\link{gtable}} object}
+
+\item{padding}{vector of length 4: top, right, bottom, left. Normal
+recycling rules apply.}
+}
+\description{
+Add padding around edges of table.
+}
+\examples{
+library(grid)
+gt <- gtable(unit(1, "null"), unit(1, "null"))
+gt <- gtable_add_grob(gt, rectGrob(gp = gpar(fill = "black")), 1, 1)
+
+plot(gt)
+plot(cbind(gt, gt))
+plot(rbind(gt, gt))
+
+pad <- gtable_add_padding(gt, unit(1, "cm"))
+plot(pad)
+plot(cbind(pad, pad))
+plot(rbind(pad, pad))
+}
+
diff --git a/man/gtable_add_rows.Rd b/man/gtable_add_rows.Rd
new file mode 100644
index 0000000..3b87fe2
--- /dev/null
+++ b/man/gtable_add_rows.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add-rows-cols.r
+\name{gtable_add_rows}
+\alias{gtable_add_rows}
+\title{Add new rows in specified position.}
+\usage{
+gtable_add_rows(x, heights, pos = -1)
+}
+\arguments{
+\item{x}{a \code{\link{gtable}} object}
+
+\item{heights}{a unit vector giving the heights of the new rows}
+
+\item{pos}{new row will be added below this position. Defaults to
+adding row on bottom. \code{0} adds on the top.}
+}
+\description{
+Add new rows in specified position.
+}
+\examples{
+library(grid)
+rect <- rectGrob(gp = gpar(fill = "#00000080"))
+tab <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
+tab <- gtable_add_grob(tab, rect, t = 1, l = 1, r = 3)
+tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 1)
+tab <- gtable_add_grob(tab, rect, t = 1, b = 3, l = 3)
+dim(tab)
+plot(tab)
+
+# Grobs will continue to span over new rows if added in the middle
+tab2 <- gtable_add_rows(tab, unit(1, "null"), 1)
+dim(tab2)
+plot(tab2)
+
+# But not when added to top (0) or bottom (-1, the default)
+tab3 <- gtable_add_rows(tab, unit(1, "null"))
+tab3 <- gtable_add_rows(tab3, unit(1, "null"), 0)
+dim(tab3)
+plot(tab3)
+}
+
diff --git a/man/gtable_add_space.Rd b/man/gtable_add_space.Rd
new file mode 100644
index 0000000..33a7d7a
--- /dev/null
+++ b/man/gtable_add_space.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add-space.r
+\name{gtable_add_space}
+\alias{gtable_add_col_space}
+\alias{gtable_add_row_space}
+\alias{gtable_add_space}
+\title{Add row/column spacing.}
+\usage{
+gtable_add_col_space(x, width)
+
+gtable_add_row_space(x, height)
+}
+\arguments{
+\item{x}{a gtable object}
+
+\item{width}{a vector of units of length 1 or ncol - 1}
+
+\item{height}{a vector of units of length 1 or nrow - 1}
+}
+\description{
+Adds \code{width} space between the columns or \code{height} space between
+the rows.
+}
+
diff --git a/man/gtable_col.Rd b/man/gtable_col.Rd
new file mode 100644
index 0000000..522bcb1
--- /dev/null
+++ b/man/gtable_col.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable-layouts.r
+\name{gtable_col}
+\alias{gtable_col}
+\title{Create a single column gtable.}
+\usage{
+gtable_col(name, grobs, width = NULL, heights = NULL, z = NULL,
+ vp = NULL)
+}
+\arguments{
+\item{name}{a string giving the name of the table. This is used to name
+the layout viewport}
+
+\item{grobs}{a single grob or a list of grobs}
+
+\item{width}{a unit vector giving the width of this column}
+
+\item{heights}{a unit vector giving the height of each row}
+
+\item{z}{a numeric vector giving the order in which the grobs should be
+ plotted. Use \code{Inf} (the default) to plot above or \code{-Inf}
+ below all existing grobs. By default positions are on the integers,
+giving plenty of room to insert new grobs between existing grobs.}
+
+\item{vp}{a grid viewport object (or NULL).}
+}
+\description{
+Create a single column gtable.
+}
+\examples{
+library(grid)
+a <- rectGrob(gp = gpar(fill = "red"))
+b <- circleGrob()
+c <- linesGrob()
+gt <- gtable_col("demo", list(a, b, c))
+gt
+plot(gt)
+gtable_show_layout(gt)
+}
+
diff --git a/man/gtable_filter.Rd b/man/gtable_filter.Rd
new file mode 100644
index 0000000..46a9d88
--- /dev/null
+++ b/man/gtable_filter.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/filter.r
+\name{gtable_filter}
+\alias{gtable_filter}
+\title{Filter cells by name.}
+\usage{
+gtable_filter(x, pattern, fixed = FALSE, trim = TRUE)
+}
+\arguments{
+\item{x}{a gtable object}
+
+\item{pattern}{character string containing a \link{regular expression}
+ (or character string for \code{fixed = TRUE}) to be matched
+ in the given character vector. Coerced by
+ \code{\link{as.character}} to a character string if possible. If a
+ character vector of length 2 or more is supplied, the first element
+ is used with a warning. Missing values are allowed except for
+ \code{regexpr} and \code{gregexpr}.}
+
+\item{fixed}{logical. If \code{TRUE}, \code{pattern} is a string to be
+ matched as is. Overrides all conflicting arguments.}
+
+\item{trim}{if \code{TRUE}, \code{\link{gtable_trim}} will be used to trim
+off any empty cells.}
+}
+\description{
+Filter cells by name.
+}
+\examples{
+library(grid)
+gt <- gtable(unit(rep(5, 3), c("cm")), unit(5, "cm"))
+rect <- rectGrob(gp = gpar(fill = "black"))
+circ <- circleGrob(gp = gpar(fill = "red"))
+
+gt <- gtable_add_grob(gt, rect, 1, 1, name = "rect")
+gt <- gtable_add_grob(gt, circ, 1, 3, name = "circ")
+
+plot(gtable_filter(gt, "rect"))
+plot(gtable_filter(gt, "rect", trim = FALSE))
+plot(gtable_filter(gt, "circ"))
+plot(gtable_filter(gt, "circ", trim = FALSE))
+}
+
diff --git a/man/gtable_height.Rd b/man/gtable_height.Rd
new file mode 100644
index 0000000..855a9b5
--- /dev/null
+++ b/man/gtable_height.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable.r
+\name{gtable_height}
+\alias{gtable_height}
+\title{Returns the height of a gtable, in the gtable's units}
+\usage{
+gtable_height(x)
+}
+\arguments{
+\item{x}{A gtable object}
+}
+\description{
+Note that unlike heightDetails.gtable, this can return relative units.
+}
+
diff --git a/man/gtable_matrix.Rd b/man/gtable_matrix.Rd
new file mode 100644
index 0000000..3d7a24a
--- /dev/null
+++ b/man/gtable_matrix.Rd
@@ -0,0 +1,54 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable-layouts.r
+\name{gtable_matrix}
+\alias{gtable_matrix}
+\title{Create a gtable from a matrix of grobs.}
+\usage{
+gtable_matrix(name, grobs, widths = NULL, heights = NULL, z = NULL,
+ respect = FALSE, clip = "on", vp = NULL)
+}
+\arguments{
+\item{name}{a string giving the name of the table. This is used to name
+the layout viewport}
+
+\item{grobs}{a single grob or a list of grobs}
+
+\item{widths}{a unit vector giving the width of each column}
+
+\item{heights}{a unit vector giving the height of each row}
+
+\item{z}{a numeric matrix of the same dimensions as \code{grobs},
+specifying the order that the grobs are drawn.}
+
+\item{respect}{a logical vector of length 1: should the aspect ratio of
+height and width specified in null units be respected. See
+\code{\link{grid.layout}} for more details}
+
+\item{clip}{should drawing be clipped to the specified cells
+(\code{"on"}), the entire table (\code{"inherit"}), or not at all
+(\code{"off"})}
+
+\item{vp}{a grid viewport object (or NULL).}
+}
+\description{
+Create a gtable from a matrix of grobs.
+}
+\examples{
+library(grid)
+a <- rectGrob(gp = gpar(fill = "red"))
+b <- circleGrob()
+c <- linesGrob()
+
+row <- matrix(list(a, b, c), nrow = 1)
+col <- matrix(list(a, b, c), ncol = 1)
+mat <- matrix(list(a, b, c, nullGrob()), nrow = 2)
+
+gtable_matrix("demo", row, unit(c(1, 1, 1), "null"), unit(1, "null"))
+gtable_matrix("demo", col, unit(1, "null"), unit(c(1, 1, 1), "null"))
+gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"))
+
+# Can specify z ordering
+z <- matrix(c(3, 1, 2, 4), nrow = 2)
+gtable_matrix("demo", mat, unit(c(1, 1), "null"), unit(c(1, 1), "null"), z = z)
+}
+
diff --git a/man/gtable_row.Rd b/man/gtable_row.Rd
new file mode 100644
index 0000000..b7f9443
--- /dev/null
+++ b/man/gtable_row.Rd
@@ -0,0 +1,40 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable-layouts.r
+\name{gtable_row}
+\alias{gtable_row}
+\title{Create a single row gtable.}
+\usage{
+gtable_row(name, grobs, height = NULL, widths = NULL, z = NULL,
+ vp = NULL)
+}
+\arguments{
+\item{name}{a string giving the name of the table. This is used to name
+the layout viewport}
+
+\item{grobs}{a single grob or a list of grobs}
+
+\item{height}{a unit vector giving the height of this row}
+
+\item{widths}{a unit vector giving the width of each column}
+
+\item{z}{a numeric vector giving the order in which the grobs should be
+ plotted. Use \code{Inf} (the default) to plot above or \code{-Inf}
+ below all existing grobs. By default positions are on the integers,
+giving plenty of room to insert new grobs between existing grobs.}
+
+\item{vp}{a grid viewport object (or NULL).}
+}
+\description{
+Create a single row gtable.
+}
+\examples{
+library(grid)
+a <- rectGrob(gp = gpar(fill = "red"))
+b <- circleGrob()
+c <- linesGrob()
+gt <- gtable_row("demo", list(a, b, c))
+gt
+plot(gt)
+gtable_show_layout(gt)
+}
+
diff --git a/man/gtable_show_layout.Rd b/man/gtable_show_layout.Rd
new file mode 100644
index 0000000..37351dd
--- /dev/null
+++ b/man/gtable_show_layout.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/grid.r
+\name{gtable_show_layout}
+\alias{gtable_show_layout}
+\title{Visualise the layout of a gtable.}
+\usage{
+gtable_show_layout(x)
+}
+\arguments{
+\item{x}{a gtable object}
+}
+\description{
+Visualise the layout of a gtable.
+}
+
diff --git a/man/gtable_spacer.Rd b/man/gtable_spacer.Rd
new file mode 100644
index 0000000..01068ce
--- /dev/null
+++ b/man/gtable_spacer.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable-layouts.r
+\name{gtable_spacer}
+\alias{gtable_col_spacer}
+\alias{gtable_row_spacer}
+\alias{gtable_spacer}
+\title{Create a row/col spacer gtable.}
+\usage{
+gtable_row_spacer(widths)
+
+gtable_col_spacer(heights)
+}
+\arguments{
+\item{widths}{unit vector of widths}
+
+\item{heights}{unit vector of heights}
+}
+\description{
+Create a row/col spacer gtable.
+}
+
diff --git a/man/gtable_trim.Rd b/man/gtable_trim.Rd
new file mode 100644
index 0000000..b5f21fc
--- /dev/null
+++ b/man/gtable_trim.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/trim.r
+\name{gtable_trim}
+\alias{gtable_trim}
+\title{Trim off empty cells.}
+\usage{
+gtable_trim(x)
+}
+\arguments{
+\item{x}{a gtable object}
+}
+\description{
+Trim off empty cells.
+}
+\examples{
+library(grid)
+rect <- rectGrob(gp = gpar(fill = "black"))
+base <- gtable(unit(c(2, 2, 2), "cm"), unit(c(2, 2, 2), "cm"))
+
+center <- gtable_add_grob(base, rect, 2, 2)
+plot(center)
+plot(gtable_trim(center))
+
+col <- gtable_add_grob(base, rect, 1, 2, 3, 2)
+plot(col)
+plot(gtable_trim(col))
+
+row <- gtable_add_grob(base, rect, 2, 1, 2, 3)
+plot(row)
+plot(gtable_trim(row))
+}
+
diff --git a/man/gtable_width.Rd b/man/gtable_width.Rd
new file mode 100644
index 0000000..84482aa
--- /dev/null
+++ b/man/gtable_width.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable.r
+\name{gtable_width}
+\alias{gtable_width}
+\title{Returns the width of a gtable, in the gtable's units}
+\usage{
+gtable_width(x)
+}
+\arguments{
+\item{x}{A gtable object}
+}
+\description{
+Note that unlike widthDetails.gtable, this can return relative units.
+}
+
diff --git a/man/is.gtable.Rd b/man/is.gtable.Rd
new file mode 100644
index 0000000..c05cc81
--- /dev/null
+++ b/man/is.gtable.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable.r
+\name{is.gtable}
+\alias{is.gtable}
+\title{Is this a gtable?}
+\usage{
+is.gtable(x)
+}
+\arguments{
+\item{x}{object to test}
+}
+\description{
+Is this a gtable?
+}
+
diff --git a/man/print.gtable.Rd b/man/print.gtable.Rd
new file mode 100644
index 0000000..c56320a
--- /dev/null
+++ b/man/print.gtable.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/gtable.r
+\name{print.gtable}
+\alias{print.gtable}
+\title{Print a gtable object}
+\usage{
+\method{print}{gtable}(x, zsort = FALSE, ...)
+}
+\arguments{
+\item{x}{A gtable object.}
+
+\item{zsort}{Sort by z values? Default \code{FALSE}.}
+
+\item{...}{Other arguments (not used by this method).}
+}
+\description{
+Print a gtable object
+}
+
diff --git a/man/z_arrange_gtables.Rd b/man/z_arrange_gtables.Rd
new file mode 100644
index 0000000..de05889
--- /dev/null
+++ b/man/z_arrange_gtables.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/z.r
+\name{z_arrange_gtables}
+\alias{z_arrange_gtables}
+\title{Arrange the z values within gtable objects}
+\usage{
+z_arrange_gtables(gtables, z)
+}
+\arguments{
+\item{gtables}{A list of gtable objects}
+
+\item{z}{A numeric vector of relative z values}
+}
+\description{
+This is usually used before rbinding or cbinding the gtables together.
+The resulting z values will be normalized.
+}
+\details{
+Ties are handled by the \code{"first"} method: the first occurrence
+of a value wins.
+}
+
diff --git a/man/z_normalise.Rd b/man/z_normalise.Rd
new file mode 100644
index 0000000..9091df8
--- /dev/null
+++ b/man/z_normalise.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/z.r
+\name{z_normalise}
+\alias{z_normalise}
+\title{Normalise z values within a gtable object}
+\usage{
+z_normalise(x, i = 1)
+}
+\arguments{
+\item{x}{A gtable object}
+
+\item{i}{The z value to start counting up from (default is 1)}
+}
+\description{
+The z values within a gtable object can be any numeric values.
+This function will change them to integers (starting from 1),
+preserving the original order.
+}
+\details{
+Ties are handled by the \code{"first"} method: the first occurrence
+of a value wins.
+}
+
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..807b29d
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,4 @@
+library(testthat)
+library(gtable)
+
+test_check("gtable")
diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf
new file mode 100644
index 0000000..09b0766
Binary files /dev/null and b/tests/testthat/Rplots.pdf differ
diff --git a/tests/testthat/helper-grobs.r b/tests/testthat/helper-grobs.r
new file mode 100644
index 0000000..98c2fc6
--- /dev/null
+++ b/tests/testthat/helper-grobs.r
@@ -0,0 +1,5 @@
+library(grid)
+grob1 <- rectGrob()
+grob2 <- circleGrob()
+grob3 <- linesGrob()
+grob4 <- polygonGrob()
diff --git a/tests/testthat/helper-units.r b/tests/testthat/helper-units.r
new file mode 100644
index 0000000..8516978
--- /dev/null
+++ b/tests/testthat/helper-units.r
@@ -0,0 +1,6 @@
+library(grid)
+cm <- unit(1, "cm")
+cm2 <- unit(2, "cm")
+cm5 <- unit(5, "cm")
+
+null <- unit(1, "null")
diff --git a/tests/testthat/test-bind.r b/tests/testthat/test-bind.r
new file mode 100644
index 0000000..40f6bb9
--- /dev/null
+++ b/tests/testthat/test-bind.r
@@ -0,0 +1,34 @@
+context("Bind")
+
+test_that("Number of rows grow with rbind", {
+
+ lay1 <- gtable_add_rows(gtable(), cm)
+ lay2 <- gtable_add_rows(gtable(), rep(cm, 2))
+
+ expect_that(nrow(rbind(lay1, lay2)), equals(3))
+ expect_that(nrow(rbind(lay2, lay1)), equals(3))
+})
+
+test_that("Number of cols grow with cbind", {
+
+ lay1 <- gtable_add_cols(gtable(), cm)
+ lay2 <- gtable_add_cols(gtable(), rep(cm, 2))
+
+ expect_that(ncol(cbind(lay1, lay2)), equals(3))
+ expect_that(ncol(cbind(lay2, lay1)), equals(3))
+})
+
+test_that("Heights and widths vary with size parameter", {
+ col1 <- gtable_col("col1", list(grob1), cm, cm)
+ col2 <- gtable_col("col1", list(grob1), cm2, cm2)
+
+ expect_equal(cbind(col1, col2, size = "first")$heights, cm)
+ expect_equal(cbind(col1, col2, size = "last")$heights, cm2)
+ expect_equal(cbind(col1, col2, size = "min")$heights, cm)
+ expect_equal(cbind(col1, col2, size = "max")$heights, cm2)
+
+ expect_equal(rbind(col1, col2, size = "first")$widths, cm)
+ expect_equal(rbind(col1, col2, size = "last")$widths, cm2)
+ expect_equal(rbind(col1, col2, size = "min")$widths, cm)
+ expect_equal(rbind(col1, col2, size = "max")$widths, cm2)
+})
diff --git a/tests/testthat/test-layout.r b/tests/testthat/test-layout.r
new file mode 100644
index 0000000..d046cce
--- /dev/null
+++ b/tests/testthat/test-layout.r
@@ -0,0 +1,154 @@
+library(testthat)
+
+# Find location of a grob
+gtable_find <- function(x, grob) {
+ pos <- vapply(x$grobs, identical, logical(1), grob)
+ x$layout[pos, ]
+}
+
+loc_df <- function(t, l, b, r) {
+ data.frame(t, l, b, r, z = 1, clip = "on", name = "layout",
+ stringsAsFactors = FALSE)
+}
+
+context("gtable")
+
+test_that("Number of rows grows with add_rows", {
+ layout <- gtable()
+ expect_that(nrow(layout), equals(0))
+
+ layout <- gtable_add_rows(layout, unit(1, "cm"))
+ expect_that(nrow(layout), equals(1))
+
+ layout <- gtable_add_rows(layout, unit(1, "cm"))
+ layout <- gtable_add_rows(layout, unit(1, "cm"))
+ expect_that(nrow(layout), equals(3))
+
+ layout <- gtable_add_rows(layout, unit(1:2, "cm"))
+ expect_that(nrow(layout), equals(5))
+})
+
+
+test_that("Number of columns grows with add_cols", {
+ layout <- gtable()
+ expect_that(ncol(layout), equals(0))
+
+ layout <- gtable_add_cols(layout, unit(1, "cm"))
+ expect_that(ncol(layout), equals(1))
+
+ layout <- gtable_add_cols(layout, unit(c(1, 1), "cm"))
+ expect_that(ncol(layout), equals(3))
+
+ layout <- gtable_add_cols(layout, unit(1:2, "cm"))
+ expect_that(ncol(layout), equals(5))
+})
+
+
+test_that("Setting and getting works", {
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), cm), cm)
+
+ layout <- gtable_add_grob(layout, grob1, 1, 1)
+ loc <- gtable_find(layout, grob1)
+
+ expect_that(nrow(loc), equals(1))
+ expect_that(loc$t, equals(1))
+ expect_that(loc$r, equals(1))
+ expect_that(loc$b, equals(1))
+ expect_that(loc$l, equals(1))
+})
+
+test_that("Spanning grobs continue to span after row insertion", {
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), rep(cm, 3)), rep(cm, 3))
+ layout <- gtable_add_grob(layout, grob1, 1, 1, 3, 3)
+
+ within <- gtable_add_rows(gtable_add_cols(layout, cm, pos = 2), cm, pos = 2)
+ loc <- gtable_find(within, grob1)
+
+ expect_that(loc, equals(loc_df(t = 1, l = 1, b = 4, r = 4)))
+
+ top_left <- layout
+ top_left <- gtable_add_cols(top_left, cm, pos = 0)
+ top_left <- gtable_add_rows(top_left, cm, pos = 0)
+
+ loc <- gtable_find(top_left, grob1)
+ expect_that(loc, equals(loc_df(t = 2, l = 2, b = 4, r = 4)))
+
+ bottom_right <- layout
+ bottom_right <- gtable_add_cols(bottom_right, cm)
+ bottom_right <- gtable_add_rows(bottom_right, cm)
+
+ loc <- gtable_find(bottom_right, grob1)
+ expect_that(loc, equals(loc_df(t = 1, l = 1, b = 3, r = 3)))
+})
+
+
+test_that("n + 1 new rows/cols after spacing", {
+ layout <- gtable()
+ layout <- gtable_add_rows(layout, rep(cm, 3))
+ layout <- gtable_add_cols(layout, rep(cm, 3))
+
+ layout <- gtable_add_col_space(layout, cm)
+ expect_that(ncol(layout), equals(5))
+
+ layout <- gtable_add_row_space(layout, cm)
+ expect_that(ncol(layout), equals(5))
+})
+
+test_that("Spacing adds rows/cols in correct place", {
+ layout <- gtable()
+ layout <- gtable_add_rows(layout, rep(cm, 2))
+ layout <- gtable_add_cols(layout, rep(cm, 2))
+
+ layout <- gtable_add_col_space(layout, null)
+ layout <- gtable_add_row_space(layout, null)
+
+ expect_that(as.vector(layout$heights), equals(rep(1, 3)))
+ expect_that(attr(layout$heights, "unit"), equals(c("cm", "null", "cm")))
+
+ expect_that(as.vector(layout$widths), equals(rep(1, 3)))
+ expect_that(attr(layout$widths, "unit"), equals(c("cm", "null", "cm")))
+
+})
+
+test_that("Negative positions place from end", {
+ layout <- gtable()
+ layout <- gtable_add_rows(layout, rep(cm, 3))
+ layout <- gtable_add_cols(layout, rep(cm, 3))
+
+ col_span <- gtable_add_grob(layout, grob1, t = 1, l = 1, r = -1)
+ expect_that(gtable_find(col_span, grob1),
+ equals(loc_df(t = 1, l = 1, b = 1, r = 3)))
+
+ row_span <- gtable_add_grob(layout, grob1, t = 1, l = 1, b = -1)
+ expect_that(gtable_find(row_span, grob1),
+ equals(loc_df(t = 1, l = 1, b = 3, r = 1)))
+})
+
+test_that("Adding multiple grobs", {
+ grobs <- rep(list(grob1), 8)
+
+ # With z = Inf, and t value for each grob
+ tval <- c(1, 2, 3, 1, 2, 3, 1, 2)
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), rep(cm, 3)), rep(cm, 3))
+ layout <- gtable_add_grob(layout, grobs, tval, 1, 3, 3, z = Inf)
+ expect_equal(layout$layout$t, tval)
+ expect_equal(layout$layout$z, 1:8)
+
+ # With z = -Inf
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), rep(cm, 3)), rep(cm, 3))
+ layout <- gtable_add_grob(layout, grobs, 1, 1, 3, 3, z = -Inf)
+ expect_equal(layout$layout$z, -7:0)
+
+ # Mixing Inf and non-Inf z values
+ zval <- c(Inf, Inf, 6, 0, -Inf, Inf, -2, -Inf)
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), rep(cm, 3)), rep(cm, 3))
+ layout <- gtable_add_grob(layout, grobs, 1, 1, 3, 3, z = zval)
+ expect_equal(layout$layout$z, c(7, 8, 6, 0, -4, 9, -2, -3))
+
+ # Error if inputs are not length 1 or same length as grobs
+ layout <- gtable_add_cols(gtable_add_rows(gtable(), rep(cm, 3)), rep(cm, 3))
+ expect_error(gtable_add_grob(layout, grobs, c(1:3), 1, 3, 3))
+ expect_error(gtable_add_grob(layout, grobs, tval, 1:2, 3, 3))
+ expect_error(gtable_add_grob(layout, grobs, tval, 1, 3, 3, z = 1:4))
+
+})
diff --git a/tests/testthat/test-subsetting.r b/tests/testthat/test-subsetting.r
new file mode 100644
index 0000000..3faf110
--- /dev/null
+++ b/tests/testthat/test-subsetting.r
@@ -0,0 +1,183 @@
+context("Subsetting")
+
+base <- gtable(unit(rep(1, 3), "null"), unit(rep(1, 3), "null"))
+rownames(base) <- LETTERS[1:3]
+colnames(base) <- letters[1:3]
+
+test_that("dimensions correct after subsetting", {
+ expect_equal(dim(base[, ]), c(3, 3))
+ expect_equal(dim(base[1:3, 1:3]), c(3, 3))
+ expect_equal(dim(base[T, T]), c(3, 3))
+ expect_equal(dim(base[c("A", "B", "C"), c("a", "b", "c")]), c(3, 3))
+
+ expect_equal(dim(base[1, 1]), c(1, 1))
+ expect_equal(dim(base[c(T, F, F), c(T, F, F)]), c(1, 1))
+ expect_equal(dim(base[-(2:3), -(2:3)]), c(1, 1))
+ expect_equal(dim(base["A", "b"]), c(1, 1))
+
+ expect_equal(dim(base[1:2, 2:3]), c(2, 2))
+})
+
+rect <- rectGrob()
+mid <- gtable_add_grob(base, rect, 2, 2)
+row <- gtable_add_grob(base, rect, 2, l = 1, r = 3)
+col <- gtable_add_grob(base, rect, 2, t = 1, b = 3)
+
+tlbr <- function(x) unname(unlist(x$layout[c("t", "l", "b", "r")]))
+
+test_that("grobs moved to correct location", {
+
+ expect_equal(tlbr(mid[2, 2]), c(1, 1, 1, 1))
+ expect_equal(tlbr(mid[2:3, 2:3]), c(1, 1, 1, 1))
+
+ expect_equal(tlbr(mid[1:2, 1:2]), c(2, 2, 2, 2))
+ expect_equal(tlbr(mid[1:3, 1:3]), c(2, 2, 2, 2))
+})
+
+test_that("spanning grobs kept if ends kept", {
+
+ expect_equal(length(row[, -2]), 1)
+ expect_equal(tlbr(row[, -2]), c(2, 1, 2, 2))
+
+ expect_equal(length(col[-2, ]), 1)
+ expect_equal(tlbr(col[-2, ]), c(1, 2, 2, 2))
+
+ expect_equal(length(row[, 1]), 0)
+ expect_equal(length(col[1, ]), 0)
+
+})
+
+
+# Detailed tests for indexing with [.gtable ----------------------------------
+
+# Some of these tests can be confusing; if you need to see
+# what's going on, run grid.draw(gt)
+
+# Make a bunch of grobs
+g1 <- rectGrob()
+g2 <- circleGrob()
+g3 <- polygonGrob()
+g4 <- linesGrob()
+g5 <- circleGrob()
+g6 <- rectGrob()
+
+unrowname <- function(x) {
+ rownames(x) <- NULL
+ x
+}
+
+# Check that two gtable objects are the same.
+# This allows for differences in how units are stored and other subtle
+# changes that don't affect appearance.
+equal_gtable <- function(a, b) {
+ identical(a$grobs, b$grobs) &&
+ # Normalized z values are the same (ensuring same render order)
+ # Also ignore row names
+ all.equal(unrowname(z_normalise(a)$layout),
+ unrowname(z_normalise(b)$layout)) &&
+ # Test widths/heights for equality.
+ # This is the best way I could think of, but it's not very nice
+ all(convertUnit(a$widths - b$widths, "cm", valueOnly = TRUE) == 0) &&
+ all(convertUnit(a$heights - b$heights, "cm", valueOnly = TRUE) == 0) &&
+ all.equal(a$respect, b$respect) &&
+ all.equal(a$rownames, b$rownames) &&
+ all.equal(a$colnames, b$colnames)
+}
+
+
+# This will create a new gtable made with gtable_matrix
+# using the specified cols and rows from grobmat.
+# The sizes of the rows/cols are the same as the index values (but in cm)
+make_gt <- function(grobmat, rows, cols) {
+ gtable_matrix("test", grobmat[rows, cols, drop = FALSE],
+ heights=unit(rows, "cm"), widths=unit(cols, "cm") )
+}
+
+
+test_that("Indexing with single-cell grobs", {
+ # Make a 2x3 gtable where each cell has one grob
+ grobmat <- matrix(list(g1, g2, g3, g4, g5, g6), nrow=2)
+ gt <- make_gt(grobmat, 1:2, 1:3)
+
+ # Indexing in ways that don't change gt
+ expect_true(equal_gtable(gt, gt[1:2, 1:3]))
+ expect_true(equal_gtable(gt, gt[]))
+ expect_true(equal_gtable(gt, gt[1:2, ]))
+ expect_true(equal_gtable(gt, gt[, 1:3]))
+
+ # New table from contiguous cells
+ expect_true(equal_gtable(gt[1, 1], make_gt(grobmat, 1, 1)))
+ expect_true(equal_gtable(gt[2, 2], make_gt(grobmat, 2, 2)))
+ expect_true(equal_gtable(gt[1:2, 1], make_gt(grobmat, 1:2, 1)))
+ expect_true(equal_gtable(gt[1:2, 2], make_gt(grobmat, 1:2, 2)))
+ expect_true(equal_gtable(gt[1, 1:3], make_gt(grobmat, 1, 1:3)))
+ expect_true(equal_gtable(gt[1, 1:2], make_gt(grobmat, 1, 1:2)))
+ expect_true(equal_gtable(gt[1:2, 1:2], make_gt(grobmat, 1:2, 1:2)))
+ expect_true(equal_gtable(gt[1:2, 2:3], make_gt(grobmat, 1:2, 2:3)))
+
+ # New table from non-contiguous cells
+ expect_true(equal_gtable(gt[1, c(1, 3)], make_gt(grobmat, 1, c(1, 3))))
+ expect_true(equal_gtable(gt[1:2, c(1, 3)], make_gt(grobmat, 1:2, c(1, 3))))
+})
+
+
+test_that("Indexing with names", {
+ # Make a 2x3 gtable where each cell has one grob
+ grobmat <- matrix(list(g1, g2, g3, g4, g5, g6), nrow=2)
+ gt <- make_gt(grobmat, 1:2, 1:3)
+ dimnames(gt) <- list(c("a","b"), c("x","y","z"))
+
+ expect_true(equal_gtable(gt, gt[c("a","b"), c("x","y","z")]))
+ expect_true(equal_gtable(gt[1, ], gt["a", ]))
+ expect_true(equal_gtable(gt[, 2], gt[, "y"]))
+ expect_true(equal_gtable(gt[, 2:3], gt[, c("y","z")]))
+ expect_true(equal_gtable(gt[1, 1:2], gt["a", c("x","y")]))
+ expect_true(equal_gtable(gt[1, 1:2], gt["a", 1:2]))
+})
+
+
+
+# Make a gtable with grobs that span cells
+make_span_gt <- function(rows, cols) {
+ # Make gtable with one grob at (1:1, 1:3) and another at (1:2, 1:2)
+ gt <- gtable(name = "test",
+ heights=unit(rows, "cm"), widths=unit(cols, "cm") )
+
+ if (all(1 %in% rows) && all(c(1,3) %in% cols)) {
+ gt <- gtable_add_grob(gt, g3, 1, 1, 1, length(cols))
+ }
+ if (all(1:2 %in% rows) && all(c(1,2) %in% cols)) {
+ gt <- gtable_add_grob(gt, g4, 1, 1, 2, 2)
+ }
+ gt
+}
+
+test_that("Indexing with grobs that span cells", {
+
+ # Make a gtable with two grobs that span cells
+ gt <- make_span_gt(1:2, 1:3)
+
+ # Indexing in ways that don't change gt
+ expect_true(equal_gtable(gt, gt[1:2, 1:3]))
+
+ # If a cell at the end of a grob is dropped, drop the grob
+ # These should drop all grobs
+ expect_true(equal_gtable(gt[1, 2], make_span_gt(1, 2)))
+ expect_equal(length(gt[1, 2]$grobs), 0)
+ expect_true(equal_gtable(gt[1:2, 2], make_span_gt(1:2, 2)))
+ expect_equal(length(gt[1:2, 2]$grobs), 0)
+
+ # These should preserve one of the grobs
+ expect_true(equal_gtable(gt[1:2, 1:2], make_span_gt(1:2, 1:2)))
+ expect_equal(length(gt[1:2, 1:2]$grobs), 1)
+ expect_true(equal_gtable(gt[1, 1:3], make_span_gt(1, 1:3)))
+ expect_equal(length(gt[1, 1:3]$grobs), 1)
+
+ # If a cell in the middle of a grob is dropped, don't drop the grob
+ expect_true(equal_gtable(gt[1, c(1,3)], make_span_gt(1, c(1,3))))
+ expect_equal(length(gt[1, c(1,3)]$grobs), 1)
+
+ # Currently undefined behavior:
+ # What happens when you do repeat rows/cols, like gt[1, c(1,1,1,3)] ?
+ # What happens when order is non-monotonic, like gt[1, c(3,1,2)] ?
+})
diff --git a/tests/testthat/test-z-order.r b/tests/testthat/test-z-order.r
new file mode 100644
index 0000000..f8aba58
--- /dev/null
+++ b/tests/testthat/test-z-order.r
@@ -0,0 +1,82 @@
+context("z-order")
+
+# z tests for gtable_add_grob are in test-layout.r, mixed with other tests
+
+
+test_that("z order for row, column, and matrix layouts", {
+ zorder <- c(3, 1, 2, 4)
+
+ # ==== column ====
+ gt <- gtable_col("test", list(grob1, grob2, grob3, grob4))
+ # z for positions 1 2 3 4 (left to right) should equal 1:4
+ expect_equal(gt$layout$z[gt$layout$t], 1:4)
+
+ gt <- gtable_col("test", list(grob1, grob2, grob3, grob4), z = zorder)
+ # z for position 1 2 3 4 (left to right) should equal zorder
+ expect_equal(gt$layout$z[gt$layout$t], zorder)
+
+ # ==== row ====
+ gt <- gtable_row("test", list(grob1, grob2, grob3, grob4))
+ # z for positions 1 2 3 4 (top to bottom) should equal 1:4
+ expect_equal(gt$layout$z[gt$layout$l], 1:4)
+
+ gt <- gtable_row("test", list(grob1, grob2, grob3, grob4), z = zorder)
+ # z for position 1 2 3 4 (top to bottom) should equal zorder
+ expect_equal(gt$layout$z[gt$layout$l], zorder)
+
+ # ==== matrix ====
+ gt <- gtable_matrix("test", matrix(list(grob1, grob2, grob3, grob4),
+ nrow = 2), unit(c(1, 1), "null"), unit(c(1, 1), "null"))
+ # Get the position. Should be: 1 3
+ # 2 4
+ loc <- 2 * (gt$layout$l - 1) + gt$layout$t
+ # z for positions 1:4 should equal 1:4
+ expect_equal(gt$layout$z[loc], 1:4)
+
+ gt <- gtable_matrix("test", matrix(list(grob1, grob2, grob3, grob4),
+ nrow = 2), unit(c(1, 1), "null"), unit(c(1, 1), "null"),
+ z = matrix(zorder, nrow = 2))
+ # Get the position. Should be: 1 3
+ # 2 4
+ loc <- 2 * (gt$layout$l - 1) + gt$layout$t
+ # z for positions 1:4 should equal zorder
+ expect_equal(gt$layout$z[loc], zorder)
+
+})
+
+
+test_that("z_normalise works properly", {
+ # Non-integer starting zorder, in funny order
+ zorder <- c(0.001, -4, 0, 1e6)
+ gt <- gtable_col("test", list(grob1, grob2, grob3, grob4), z = zorder)
+ expect_equal(gt$layout$z, zorder)
+ gt1 <- z_normalise(gt)
+ expect_equal(sort(gt1$layout$z), 1:4)
+
+ # OK with empty layout (zero rows in data frame)
+ gt <- gtable(unit(1:3, c("cm")), unit(c(2,4), "cm"))
+ gt1 <- z_normalise(gt)
+ expect_equal(nrow(gt1$layout), 0)
+})
+
+
+
+test_that("z_arrange_gtables properly sets z values", {
+ gt <- list(
+ gtable_col("test1", list(grob1, grob2, grob3), z = c(.9, .3, .6)),
+ gtable_col("test2", list(grob4, grob1, grob2), z = c(1, 3, 2)),
+ gtable_col("test3", list(grob3, grob4, grob1), z = c(2, 3, 1))
+ )
+
+ # Arrange the z values of each gtable
+ gt1 <- z_arrange_gtables(gt, c(3, 2, 1))
+ expect_equal(gt1[[1]]$layout$z, c(9, 7, 8))
+ expect_equal(gt1[[2]]$layout$z, c(4, 6, 5))
+ expect_equal(gt1[[3]]$layout$z, c(2, 3, 1))
+
+ # Check that it works with cbind and rbind (which call z_arrange_gtables)
+ gt1 <- cbind(gt[[1]], gt[[2]], gt[[3]], z = c(3, 2, 1))
+ expect_equal(gt1$layout$z, c(9, 7, 8, 4, 6, 5, 2, 3, 1))
+ gt1 <- rbind(gt[[1]], gt[[2]], gt[[3]], z = c(3, 2, 1))
+ expect_equal(gt1$layout$z, c(9, 7, 8, 4, 6, 5, 2, 3, 1))
+})
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-gtable.git
More information about the debian-med-commit
mailing list