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).

链接地址: http://www.djcxy.com/p/79466.html

上一篇: 我怎样才能使这个100%的高度+列溢出布局在Firefox和IE中工作?

下一篇: grid.layout不喜欢尊重和复合单位