grid.layout doesn't like respect and compound units
Using unit.pmax
as the default comparison of widths/heights in gtable is proving harder than I'd hoped; after several hours of head scratching I've narrowed it down to this situation:
library(grid)
w <- list(unit(1,"null"), unit(1,"null"))
class(w) <- c("unit.list", "unit")
h <- unit(1, "in")
gl1 <- grid.layout(1, 2, widths = w, heights = h,
respect = TRUE)
grid.newpage()
grid.show.layout(gl1) # fine
w2 <- w
w2[[1]] <- unit.pmax(unit(1,"null"), unit(1,"null"))
gl2 <- grid.layout(1, 2, widths = w2, heights = h,
respect = FALSE)
grid.newpage()
grid.show.layout(gl2)# fine
gl3 <- grid.layout(1, 2, widths = w2, heights = h,
respect = TRUE)
grid.newpage()
grid.show.layout(gl3)
## Error in grid.Call.graphics(L_setviewport, vp, TRUE) :
## non-finite location and/or size for viewport
so the combination of unit.pmax(unit(1,"null"), unit(1,"null"))
and respect = TRUE
makes grid complain. In case you're wondering, that situation would come up in ggplot2 with facet_grid
and theme(aspect.ratio = ...)
.
I can vaguely picture that unit.pmax()
should simplify null units before attempting to use the respect
parameter, but I don't really know what this all means. In practice though, it prevents me from improving gtable's cbind/rbind.
Any workaround?
Edit: I'm not sure how to provide a minimal example with ggplot2
, other than installing my fork and running
ggplot(data.frame(x=1:8, y=1:8, f=gl(2,4)), aes(x, y)) +
geom_point() +
facet_grid(f~.) +
theme(aspect.ratio=3)
# Error in grid.Call.graphics(L_setviewport, vp, TRUE) :
# non-finite location and/or size for viewport
so unit.pmax()
fails in this case, while the current comparison method compare.unit(,,pmax)
fails in other situations, such as,
p1 = qplot(1, 1); p2 = qplot(1,1)
cbind(ggplotGrob(p1), ggplotGrob(p2), size="max")
# Error in mmm < each : comparison of these types is not implemented
It's not optimal, but if all else fails, you could just rewrite unit.pmax
to make it do what you wish it did.
The following function acts just like unit.pmax()
except that, whenever it's asked to find the maximimum of two or more unit objects, all in "null"
units, it returns their value of the "largest" one, rather than an expression of the form max(x,y,...)
. (See the second code block below for an example.)
unit.pmax2 <-
function (...)
{
select.i <- function(unit, i) {
unit[i, top = FALSE]
}
x <- list(...)
numargs <- length(x)
if (numargs == 0L)
stop("no arguments where at least one expected")
maxlength <- 0L
for (i in seq_len(numargs)) if (length(x[[i]]) > maxlength)
maxlength <- length(x[[i]])
## result <- max(unit.list.from.list(lapply(x, select.i, 1L)))
UL <- grid:::unit.list.from.list(lapply(x, select.i, 1L)) ##
result <- if(all(sapply(UL, attr, "unit")=="null")) { ##
UL[which.max(UL)]} else {max(UL)} ##
if (maxlength > 1L)
for (i in 2L:maxlength) {
## result <- unit.c(result, max(unit.list.from.list(lapply(x,
## select.i, i))))
UL <- grid:::unit.list.from.list(lapply(x, select.i, i)) ##
temp <- if(all(sapply(UL, attr, "unit")=="null")) { ##
UL[which.max(UL)]} else {max(UL)} ##
result <- unit.c(result, temp) ##
}
result
}
To see the difference between unit.pmax()
and unit.pmax2()
, compare:
A <- list(unit(1,"null"), unit(1,"null"), unit(1,"null"))
B <- list(unit(1,"null"), unit(4,"null"), unit(1,"null"))
C <- list(unit(1,"null"), unit(2,"null"), unit(1,"inch"))
class(A) <- class(B) <- class(C) <- c("unit.list", "unit")
unit.pmax(A, B, C)
# [1] max(1null, 1null, 1null) max(1null, 4null, 2null) max(1null, 1null, 1inch)
unit.pmax2(A, B, C)
# [1] 1null 4null max(1null, 1null, 1inch)
Testing it out shows that it works. (Note that you also need to replace w2[[1]] <- ...
with w2[1] <- ...
to avoid a complaint when respect = TRUE
.)
library(grid)
w2 <- list(unit(1,"null"), unit(1,"null"))
class(w2) <- c("unit.list", "unit")
h <- unit(1, "in")
w2[1] <- unit.pmax2(unit(1,"null"), unit(1,"null"))
## w2[[1]] <- unit.pmax(unit(1,"null"), unit(1,"null")) ## For comparison
gl3 <- grid.layout(1, 2, widths = w2, heights = h,
respect = TRUE)
grid.newpage()
grid.show.layout(gl3)
A fix by Paul Murrell in R-devel @r65845 appears to solve the problem. Unfortunately, that means the update to gtable
will have to wait at least until the next R release (and possibly much longer, as ggplot2 dev usually takes a conservative approach about supporting older releases).