From 79041b3267d835cf53b7c71b332a1f7aa494fc7d Mon Sep 17 00:00:00 2001 From: yun Date: Fri, 2 Aug 2024 23:36:04 +0800 Subject: [PATCH 01/10] add option `align_axis_title` --- R/collect_axes.R | 95 ++++++++++++++++++++++++++++++++++++++++++++++ R/plot_layout.R | 19 +++++++++- R/plot_patchwork.R | 9 +++++ man/plot_layout.Rd | 5 +++ 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index 1bdba58..6eef6b7 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -133,6 +133,101 @@ collect_axes <- function(gt, dir = "x") { new } +collapse_axes_and_titles <- function(gt, n, collapsed_positions) { + for (i in seq_len(n)) { + for (position in collapsed_positions) { + lab_pattern <- switch(position, + t = , + b = paste("xlab", position, i, sep = "-"), + l = , + r = paste("ylab", position, i, sep = "-") + ) + axis_pattern <- switch(position, + t = , + b = paste("axis", position, i, sep = "-"), + l = , + r = paste("axis", position, i, sep = "-") + ) + # this grob contain both axis labels and axis title + axis_and_title <- gtable::gtable_filter( + gt, paste(lab_pattern, axis_pattern, sep = "|") + ) + no_grobs <- vapply(.subset2(axis_and_title, "grobs"), inherits, + logical(1L), what = "zeroGrob" + ) + if (all(no_grobs)) next + + # integrate axis and lab grobs ------------------------------ + layout <- .subset2(gt, "layout") + lab_index <- which(grepl(lab_pattern, .subset2(layout, "name"))) + axis_index <- which(grepl(axis_pattern, .subset2(layout, "name"))) + + ## we reset the axis labels grob size ----------------------- + grob_axis_index <- which( + grepl(axis_pattern, .subset2(axis_and_title$layout, "name")) + ) + + if (position == "t") { + axis_and_title$heights[grob_axis_index] <- grid::grobHeight( + axis_and_title$grobs[[grob_axis_index]] + ) + axis_and_title$vp <- grid::viewport( + y = 0, just = "bottom", + height = sum(axis_and_title$heights) + ) + } + if (position == "b") { + axis_and_title$heights[grob_axis_index] <- grid::grobHeight( + axis_and_title$grobs[[grob_axis_index]] + ) + axis_and_title$vp <- grid::viewport( + y = 1, just = "top", + height = sum(axis_and_title$heights) + ) + } + if (position == "l") { + axis_and_title$widths[grob_axis_index] <- grid::grobWidth( + axis_and_title$grobs[[grob_axis_index]] + ) + axis_and_title$vp <- grid::viewport( + x = 1, just = "right", + width = sum(axis_and_title$widths) + ) + } + if (position == "r") { + axis_and_title$widths[grob_axis_index] <- grid::grobWidth( + axis_and_title$grobs[[grob_axis_index]] + ) + axis_and_title$vp <- grid::viewport( + x = 0, just = "left", + width = sum(axis_and_title$widths) + ) + } + + # remove the original grobs ----------------------------- + gt$grobs[[axis_index]] <- ggplot2::zeroGrob() + gt$grobs[[lab_index]] <- ggplot2::zeroGrob() + + # insert the collapsed axis title and labs -------------- + new_area <- layout[axis_index, , drop = FALSE] + gt <- gtable::gtable_add_grob( + gt, grobs = axis_and_title, + .subset2(new_area, "t"), + .subset2(new_area, "l"), + .subset2(new_area, "b"), + .subset2(new_area, "r"), + name = switch(position, + t = , + b = paste("xlab", "axis", position, i, sep = "-"), + l = , + r = paste("ylab", "axis", position, i, sep = "-") + ) + ) + } + } + gt +} + # For every given row, check if all non-zero grobs occupying that row have a # name that has a pattern. If all these grobs in that row do, measure the # grob heights and put that into the gtable's heights. diff --git a/R/plot_layout.R b/R/plot_layout.R index 02cc56b..106b2f9 100644 --- a/R/plot_layout.R +++ b/R/plot_layout.R @@ -27,6 +27,9 @@ #' @param design Specification of the location of areas in the layout. Can either #' be specified as a text string or by concatenating calls to [area()] together. #' See the examples for further information on use. +#' @param align_axis_title A boolean value or a character of the axis position +#' ("t", "l", "b", "l") indicates how to align the axis title. By default, all +#' axis title will be aligned. #' @param axes A string specifying how axes should be treated. `'keep'` will #' retain all axes in individual plots. `'collect'` will remove duplicated #' axes when placed in the same run of rows or columns of the layout. @@ -109,7 +112,8 @@ plot_layout <- function(ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, heights = NULL, guides = NULL, tag_level = NULL, - design = NULL, axes = NULL, axis_titles = axes) { + design = NULL, align_axis_title = NULL, + axes = NULL, axis_titles = axes) { if (!is.null(guides)) guides <- match.arg(guides, c('auto', 'collect', 'keep')) if (!is.null(tag_level)) tag_level <- match.arg(tag_level, c('keep', 'new')) if (!is.null(axes)) axes <- match.arg( @@ -118,6 +122,16 @@ plot_layout <- function(ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, if (!is.null(axis_titles)) collect_titles <- match.arg( axis_titles, c('keep', 'collect', 'collect_x', 'collect_y') ) + # By default, we always align the axis titles + if (isTRUE(align_axis_title) || is.null(align_axis_title)) { + align_axis_title <- c("t", "l", "b", "r") + } else if (isFALSE(align_axis_title)) { + align_axis_title <- character() + } else if (!all(align_axis_title %in% c("t", "l", "b", "r"))) { + cli_abort( + "only 't', 'l', 'b', and 'r' are allowed in {.arg align_axis_title}" + ) + } structure(list( ncol = ncol, nrow = nrow, @@ -126,6 +140,7 @@ plot_layout <- function(ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, heights = heights, guides = guides, tag_level = tag_level, + align_axis_title = unique(align_axis_title), axes = axes, axis_titles = axis_titles, design = as_areas(design) @@ -305,7 +320,7 @@ c.patch_area <- function(..., recursive = FALSE) { } default_layout <- plot_layout( byrow = TRUE, widths = NA, heights = NA, guides = 'auto', tag_level = 'keep', - axes = 'keep', axis_titles = 'keep' + align_axis_title = c("t", "l", "b", "r"), axes = 'keep', axis_titles = 'keep' ) #' @importFrom utils modifyList #' @export diff --git a/R/plot_patchwork.R b/R/plot_patchwork.R index 87ede53..6fe6259 100644 --- a/R/plot_patchwork.R +++ b/R/plot_patchwork.R @@ -241,6 +241,15 @@ build_patchwork <- function(x, guides = 'auto') { gt_new <- collect_axis_titles(gt_new, "y", merge = TRUE) } + # the default behaviour is aligning all axis, if we don't want to align an + # axis we collapsed it + collapsed_positions <- setdiff( + c("t", "l", "b", "r"), + x$layout$align_axis_title + ) + if (length(collapsed_positions)) { + gt_new <- collapse_axes_and_titles(gt_new, length(gt), collapsed_positions) + } gt_new <- gtable_add_grob( gt_new, zeroGrob(), t = PANEL_ROW, diff --git a/man/plot_layout.Rd b/man/plot_layout.Rd index 894e479..e09e6f0 100644 --- a/man/plot_layout.Rd +++ b/man/plot_layout.Rd @@ -13,6 +13,7 @@ plot_layout( guides = NULL, tag_level = NULL, design = NULL, + align_axis_title = NULL, axes = NULL, axis_titles = axes ) @@ -47,6 +48,10 @@ auto-tagging should behave. See \code{\link[=plot_annotation]{plot_annotation()} be specified as a text string or by concatenating calls to \code{\link[=area]{area()}} together. See the examples for further information on use.} +\item{align_axis_title}{A boolean value or a character of the axis position +("t", "l", "b", "l") indicates how to align the axis title. By default, all +axis title will be aligned.} + \item{axes}{A string specifying how axes should be treated. \code{'keep'} will retain all axes in individual plots. \code{'collect'} will remove duplicated axes when placed in the same run of rows or columns of the layout. From 0d86b316727e6751251328f252e0bcb9a34a45b2 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 00:28:46 +0800 Subject: [PATCH 02/10] reset all size of axis title and labels when collapse --- R/collect_axes.R | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index 6eef6b7..e1e0a42 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -152,9 +152,8 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { axis_and_title <- gtable::gtable_filter( gt, paste(lab_pattern, axis_pattern, sep = "|") ) - no_grobs <- vapply(.subset2(axis_and_title, "grobs"), inherits, - logical(1L), what = "zeroGrob" - ) + grobs <- .subset2(axis_and_title, "grobs") + no_grobs <- vapply(grobs, inherits, logical(1L), what = "zeroGrob") if (all(no_grobs)) next # integrate axis and lab grobs ------------------------------ @@ -163,43 +162,39 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { axis_index <- which(grepl(axis_pattern, .subset2(layout, "name"))) ## we reset the axis labels grob size ----------------------- - grob_axis_index <- which( - grepl(axis_pattern, .subset2(axis_and_title$layout, "name")) - ) - if (position == "t") { - axis_and_title$heights[grob_axis_index] <- grid::grobHeight( - axis_and_title$grobs[[grob_axis_index]] + axis_and_title$heights <- do.call( + unit.c, lapply(grobs, grid::grobHeight) ) axis_and_title$vp <- grid::viewport( - y = 0, just = "bottom", + y = unit(0, "npc"), just = "bottom", height = sum(axis_and_title$heights) ) } if (position == "b") { - axis_and_title$heights[grob_axis_index] <- grid::grobHeight( - axis_and_title$grobs[[grob_axis_index]] + axis_and_title$heights <- do.call( + unit.c, lapply(grobs, grid::grobHeight) ) axis_and_title$vp <- grid::viewport( - y = 1, just = "top", + y = unit(1, "npc"), just = "top", height = sum(axis_and_title$heights) ) } if (position == "l") { - axis_and_title$widths[grob_axis_index] <- grid::grobWidth( - axis_and_title$grobs[[grob_axis_index]] + axis_and_title$widths <- do.call( + unit.c, lapply(grobs, grid::grobWidth) ) axis_and_title$vp <- grid::viewport( - x = 1, just = "right", + x = unit(1, "npc"), just = "right", width = sum(axis_and_title$widths) ) } if (position == "r") { - axis_and_title$widths[grob_axis_index] <- grid::grobWidth( - axis_and_title$grobs[[grob_axis_index]] + axis_and_title$widths <- do.call( + unit.c, lapply(grobs, grid::grobWidth) ) axis_and_title$vp <- grid::viewport( - x = 0, just = "left", + x = unit(0, "npc"), just = "left", width = sum(axis_and_title$widths) ) } @@ -211,7 +206,8 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { # insert the collapsed axis title and labs -------------- new_area <- layout[axis_index, , drop = FALSE] gt <- gtable::gtable_add_grob( - gt, grobs = axis_and_title, + gt, + grobs = axis_and_title, .subset2(new_area, "t"), .subset2(new_area, "l"), .subset2(new_area, "b"), @@ -221,7 +217,8 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { b = paste("xlab", "axis", position, i, sep = "-"), l = , r = paste("ylab", "axis", position, i, sep = "-") - ) + ), + clip = "off" ) } } From 190a8d59ffd8415cccb53f552c9a6ca32856a176 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 02:17:51 +0800 Subject: [PATCH 03/10] fix error for plot with multiple panels --- R/collect_axes.R | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index e1e0a42..483012c 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -142,19 +142,31 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { l = , r = paste("ylab", position, i, sep = "-") ) - axis_pattern <- switch(position, - t = , - b = paste("axis", position, i, sep = "-"), - l = , - r = paste("axis", position, i, sep = "-") + lab_pattern <- paste0("^", lab_pattern, "$") + axis_pattern <- paste0( + "^axis-", position, "(-\\d+){0,2}", + "(, axis-", position, "(-\\d+){0,2})*", # recycle multiple panels + "-", i, "$" ) + # this grob contain both axis labels and axis title axis_and_title <- gtable::gtable_filter( gt, paste(lab_pattern, axis_pattern, sep = "|") ) + + # both axis labels and title must exist + if (length(axis_and_title) != 2L) next + grobs <- .subset2(axis_and_title, "grobs") - no_grobs <- vapply(grobs, inherits, logical(1L), what = "zeroGrob") - if (all(no_grobs)) next + skip <- any(vapply(grobs, function(grob) { + # if no valid grobs, we skip it + inherits(grob, "zeroGrob") || + # it seems for plot with multiple facet panels, + # the axis title won't be aligned by default + # here we always skip it if there are multiple panels + gtable::is.gtable(grob) + }, logical(1L))) + if (skip) next # integrate axis and lab grobs ------------------------------ layout <- .subset2(gt, "layout") From 10f6856d0c8e5cec2f4465fa9aff2651a8139891 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 02:28:17 +0800 Subject: [PATCH 04/10] align title for plot with multiple panels --- R/collect_axes.R | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index 483012c..1e80d02 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -161,10 +161,13 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { skip <- any(vapply(grobs, function(grob) { # if no valid grobs, we skip it inherits(grob, "zeroGrob") || - # it seems for plot with multiple facet panels, - # the axis title won't be aligned by default - # here we always skip it if there are multiple panels - gtable::is.gtable(grob) + # if it is a gtable, this grob should contain multiple axis for multiple + # panels, we test if all of them are invalid + gtable::is.gtable(grob) && + all(vapply(.subset2(grob, "grobs"), inherits, + logical(1L), + what = "zeroGrob" + )) }, logical(1L))) if (skip) next From 982ccf1986a1ecace6978f1c5e02f7baf27d2a12 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 04:01:28 +0800 Subject: [PATCH 05/10] reset layout width and heights after merging axis tilte and labels --- NAMESPACE | 2 ++ R/collect_axes.R | 87 +++++++++++++++++++++++++++++++++++++++--------- R/plot_layout.R | 2 +- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 41f3d3c..a6556cc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -124,6 +124,7 @@ importFrom(grid,gTree) importFrom(grid,grid.draw) importFrom(grid,grid.newpage) importFrom(grid,grobHeight) +importFrom(grid,grobWidth) importFrom(grid,heightDetails) importFrom(grid,is.grob) importFrom(grid,is.unit) @@ -141,6 +142,7 @@ importFrom(gtable,gtable) importFrom(gtable,gtable_add_cols) importFrom(gtable,gtable_add_grob) importFrom(gtable,gtable_add_rows) +importFrom(gtable,gtable_filter) importFrom(gtable,gtable_height) importFrom(gtable,gtable_width) importFrom(gtable,is.gtable) diff --git a/R/collect_axes.R b/R/collect_axes.R index 1e80d02..00a1680 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -133,6 +133,9 @@ collect_axes <- function(gt, dir = "x") { new } +#' @importFrom ggplot2 zeroGrob +#' @importFrom gtable is.gtable gtable_height gtable_width gtable_filter gtable_add_grob +#' @importFrom grid grobHeight grobWidth collapse_axes_and_titles <- function(gt, n, collapsed_positions) { for (i in seq_len(n)) { for (position in collapsed_positions) { @@ -150,7 +153,7 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { ) # this grob contain both axis labels and axis title - axis_and_title <- gtable::gtable_filter( + axis_and_title <- gtable_filter( gt, paste(lab_pattern, axis_pattern, sep = "|") ) @@ -161,9 +164,9 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { skip <- any(vapply(grobs, function(grob) { # if no valid grobs, we skip it inherits(grob, "zeroGrob") || - # if it is a gtable, this grob should contain multiple axis for multiple - # panels, we test if all of them are invalid - gtable::is.gtable(grob) && + # if it is a gtable, this grob should contain multiple axis for multiple + # panels, we test if all of them are invalid + is.gtable(grob) && all(vapply(.subset2(grob, "grobs"), inherits, logical(1L), what = "zeroGrob" @@ -179,48 +182,55 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { ## we reset the axis labels grob size ----------------------- if (position == "t") { axis_and_title$heights <- do.call( - unit.c, lapply(grobs, grid::grobHeight) + unit.c, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) + }) ) - axis_and_title$vp <- grid::viewport( + axis_and_title$vp <- viewport( y = unit(0, "npc"), just = "bottom", height = sum(axis_and_title$heights) ) } if (position == "b") { axis_and_title$heights <- do.call( - unit.c, lapply(grobs, grid::grobHeight) + unit.c, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) + }) ) - axis_and_title$vp <- grid::viewport( + axis_and_title$vp <- viewport( y = unit(1, "npc"), just = "top", height = sum(axis_and_title$heights) ) } if (position == "l") { axis_and_title$widths <- do.call( - unit.c, lapply(grobs, grid::grobWidth) + unit.c, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) + }) ) - axis_and_title$vp <- grid::viewport( + axis_and_title$vp <- viewport( x = unit(1, "npc"), just = "right", width = sum(axis_and_title$widths) ) } if (position == "r") { axis_and_title$widths <- do.call( - unit.c, lapply(grobs, grid::grobWidth) + unit.c, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) + }) ) - axis_and_title$vp <- grid::viewport( + axis_and_title$vp <- viewport( x = unit(0, "npc"), just = "left", width = sum(axis_and_title$widths) ) } - # remove the original grobs ----------------------------- - gt$grobs[[axis_index]] <- ggplot2::zeroGrob() - gt$grobs[[lab_index]] <- ggplot2::zeroGrob() + gt$grobs[[axis_index]] <- zeroGrob() + gt$grobs[[lab_index]] <- zeroGrob() # insert the collapsed axis title and labs -------------- new_area <- layout[axis_index, , drop = FALSE] - gt <- gtable::gtable_add_grob( + gt <- gtable_add_grob( gt, grobs = axis_and_title, .subset2(new_area, "t"), @@ -235,6 +245,51 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { ), clip = "off" ) + + # in the final, we reset the widths and heights of the axis or label + # columns / rows + layout <- .subset2(gt, "layout") + if (any(position == c("l", "r"))) { + lab_boader <- .subset2(layout, position)[lab_index] + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "l") == lab_boader & + .subset2(layout, "r") == lab_boader + ) + gt$widths[lab_boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) + })) + + axis_boader <- .subset2(layout, position)[axis_index] + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "l") == axis_boader & + .subset2(layout, "r") == axis_boader + ) + gt$widths[axis_boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) + })) + } else { + lab_boader <- .subset2(layout, position)[lab_index] + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "t") == lab_boader & + .subset2(layout, "b") == lab_boader + ) + gt$heights[lab_boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) + })) + + axis_boader <- .subset2(layout, position)[axis_index] + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "t") == axis_boader & + .subset2(layout, "b") == axis_boader + ) + gt$heights[axis_boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) + })) + } } } gt diff --git a/R/plot_layout.R b/R/plot_layout.R index 106b2f9..0e61fd5 100644 --- a/R/plot_layout.R +++ b/R/plot_layout.R @@ -124,7 +124,7 @@ plot_layout <- function(ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, ) # By default, we always align the axis titles if (isTRUE(align_axis_title) || is.null(align_axis_title)) { - align_axis_title <- c("t", "l", "b", "r") + align_axis_title <- NULL } else if (isFALSE(align_axis_title)) { align_axis_title <- character() } else if (!all(align_axis_title %in% c("t", "l", "b", "r"))) { From d623bffcfc4b93b8c978651e1a49fec8942b1ad5 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 04:21:31 +0800 Subject: [PATCH 06/10] simplify code --- R/collect_axes.R | 58 +++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index 00a1680..dfb39ef 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -249,46 +249,28 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { # in the final, we reset the widths and heights of the axis or label # columns / rows layout <- .subset2(gt, "layout") + lab_boader <- .subset2(layout, position)[lab_index] + axis_boader <- .subset2(layout, position)[axis_index] if (any(position == c("l", "r"))) { - lab_boader <- .subset2(layout, position)[lab_index] - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "l") == lab_boader & - .subset2(layout, "r") == lab_boader - ) - gt$widths[lab_boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) - })) - - axis_boader <- .subset2(layout, position)[axis_index] - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "l") == axis_boader & - .subset2(layout, "r") == axis_boader - ) - gt$widths[axis_boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) - })) + for (boader in c(lab_boader, axis_boader)) { + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "l") == boader & .subset2(layout, "r") == boader + ) + gt$widths[boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) + })) + } } else { - lab_boader <- .subset2(layout, position)[lab_index] - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "t") == lab_boader & - .subset2(layout, "b") == lab_boader - ) - gt$heights[lab_boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) - })) - - axis_boader <- .subset2(layout, position)[axis_index] - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "t") == axis_boader & - .subset2(layout, "b") == axis_boader - ) - gt$heights[axis_boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) - })) + for (boader in c(lab_boader, axis_boader)) { + grobs <- .subset( + .subset2(gt, "grobs"), + .subset2(layout, "t") == boader & .subset2(layout, "b") == boader + ) + gt$heights[boader] <- do.call(max, lapply(grobs, function(grob) { + if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) + })) + } } } } From 9787a29d8cab1538cf011573ac13273f45a55ef2 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 04:38:50 +0800 Subject: [PATCH 07/10] remove unnecessary function usage --- R/plot_layout.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/plot_layout.R b/R/plot_layout.R index 0e61fd5..270f94d 100644 --- a/R/plot_layout.R +++ b/R/plot_layout.R @@ -140,7 +140,7 @@ plot_layout <- function(ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, heights = heights, guides = guides, tag_level = tag_level, - align_axis_title = unique(align_axis_title), + align_axis_title = align_axis_title, axes = axes, axis_titles = axis_titles, design = as_areas(design) From 8b0c38dedd9de5339f4f45a8429e845903b623f5 Mon Sep 17 00:00:00 2001 From: yun Date: Sat, 3 Aug 2024 14:04:05 +0800 Subject: [PATCH 08/10] wrap_plots: add `align_axis_title` arugment --- R/wrap_plots.R | 7 ++++--- man/wrap_plots.Rd | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/R/wrap_plots.R b/R/wrap_plots.R index 8401e20..d143277 100644 --- a/R/wrap_plots.R +++ b/R/wrap_plots.R @@ -46,7 +46,8 @@ #' wrap_plots <- function(..., ncol = NULL, nrow = NULL, byrow = NULL, widths = NULL, heights = NULL, guides = NULL, - tag_level = NULL, design = NULL, axes = NULL, + tag_level = NULL, design = NULL, + align_axis_title = NULL, axes = NULL, axis_titles = axes) { if (is_valid_plot(..1)) { plots <- list(...) @@ -69,8 +70,8 @@ wrap_plots <- function(..., ncol = NULL, nrow = NULL, byrow = NULL, } Reduce(`+`, plots, init = plot_filler()) + plot_layout( ncol = ncol, nrow = nrow, byrow = byrow, widths = widths, heights = heights, - guides = guides, tag_level = tag_level, design = design, axes = axes, - axis_titles = axis_titles + guides = guides, tag_level = tag_level, design = design, + align_axis_title = align_axis_title, axes = axes, axis_titles = axis_titles ) } diff --git a/man/wrap_plots.Rd b/man/wrap_plots.Rd index 3b011e3..1a674ea 100644 --- a/man/wrap_plots.Rd +++ b/man/wrap_plots.Rd @@ -14,6 +14,7 @@ wrap_plots( guides = NULL, tag_level = NULL, design = NULL, + align_axis_title = NULL, axes = NULL, axis_titles = axes ) @@ -50,6 +51,10 @@ auto-tagging should behave. See \code{\link[=plot_annotation]{plot_annotation()} be specified as a text string or by concatenating calls to \code{\link[=area]{area()}} together. See the examples for further information on use.} +\item{align_axis_title}{A boolean value or a character of the axis position +("t", "l", "b", "l") indicates how to align the axis title. By default, all +axis title will be aligned.} + \item{axes}{A string specifying how axes should be treated. \code{'keep'} will retain all axes in individual plots. \code{'collect'} will remove duplicated axes when placed in the same run of rows or columns of the layout. From f163807407be25d3a8244b2c9e240fe99054a6cf Mon Sep 17 00:00:00 2001 From: yun Date: Sun, 4 Aug 2024 16:04:39 +0800 Subject: [PATCH 09/10] fix Docs --- R/plot_layout.R | 2 +- man/plot_layout.Rd | 2 +- man/wrap_plots.Rd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/plot_layout.R b/R/plot_layout.R index 270f94d..07b842a 100644 --- a/R/plot_layout.R +++ b/R/plot_layout.R @@ -28,7 +28,7 @@ #' be specified as a text string or by concatenating calls to [area()] together. #' See the examples for further information on use. #' @param align_axis_title A boolean value or a character of the axis position -#' ("t", "l", "b", "l") indicates how to align the axis title. By default, all +#' ("t", "l", "b", "r") indicates how to align the axis title. By default, all #' axis title will be aligned. #' @param axes A string specifying how axes should be treated. `'keep'` will #' retain all axes in individual plots. `'collect'` will remove duplicated diff --git a/man/plot_layout.Rd b/man/plot_layout.Rd index e09e6f0..e278f80 100644 --- a/man/plot_layout.Rd +++ b/man/plot_layout.Rd @@ -49,7 +49,7 @@ be specified as a text string or by concatenating calls to \code{\link[=area]{ar See the examples for further information on use.} \item{align_axis_title}{A boolean value or a character of the axis position -("t", "l", "b", "l") indicates how to align the axis title. By default, all +("t", "l", "b", "r") indicates how to align the axis title. By default, all axis title will be aligned.} \item{axes}{A string specifying how axes should be treated. \code{'keep'} will diff --git a/man/wrap_plots.Rd b/man/wrap_plots.Rd index 1a674ea..05eaa47 100644 --- a/man/wrap_plots.Rd +++ b/man/wrap_plots.Rd @@ -52,7 +52,7 @@ be specified as a text string or by concatenating calls to \code{\link[=area]{ar See the examples for further information on use.} \item{align_axis_title}{A boolean value or a character of the axis position -("t", "l", "b", "l") indicates how to align the axis title. By default, all +("t", "l", "b", "r") indicates how to align the axis title. By default, all axis title will be aligned.} \item{axes}{A string specifying how axes should be treated. \code{'keep'} will From 11b7fc973c72012ae47306ca52c4ca66bfb674a9 Mon Sep 17 00:00:00 2001 From: yun Date: Tue, 6 Aug 2024 01:10:35 +0800 Subject: [PATCH 10/10] fix axis-collapse induce nested plot --- R/collect_axes.R | 42 +----------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/R/collect_axes.R b/R/collect_axes.R index dfb39ef..010b609 100644 --- a/R/collect_axes.R +++ b/R/collect_axes.R @@ -160,21 +160,8 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { # both axis labels and title must exist if (length(axis_and_title) != 2L) next - grobs <- .subset2(axis_and_title, "grobs") - skip <- any(vapply(grobs, function(grob) { - # if no valid grobs, we skip it - inherits(grob, "zeroGrob") || - # if it is a gtable, this grob should contain multiple axis for multiple - # panels, we test if all of them are invalid - is.gtable(grob) && - all(vapply(.subset2(grob, "grobs"), inherits, - logical(1L), - what = "zeroGrob" - )) - }, logical(1L))) - if (skip) next - # integrate axis and lab grobs ------------------------------ + grobs <- .subset2(axis_and_title, "grobs") layout <- .subset2(gt, "layout") lab_index <- which(grepl(lab_pattern, .subset2(layout, "name"))) axis_index <- which(grepl(axis_pattern, .subset2(layout, "name"))) @@ -245,33 +232,6 @@ collapse_axes_and_titles <- function(gt, n, collapsed_positions) { ), clip = "off" ) - - # in the final, we reset the widths and heights of the axis or label - # columns / rows - layout <- .subset2(gt, "layout") - lab_boader <- .subset2(layout, position)[lab_index] - axis_boader <- .subset2(layout, position)[axis_index] - if (any(position == c("l", "r"))) { - for (boader in c(lab_boader, axis_boader)) { - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "l") == boader & .subset2(layout, "r") == boader - ) - gt$widths[boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_width(grob) else grobWidth(grob) - })) - } - } else { - for (boader in c(lab_boader, axis_boader)) { - grobs <- .subset( - .subset2(gt, "grobs"), - .subset2(layout, "t") == boader & .subset2(layout, "b") == boader - ) - gt$heights[boader] <- do.call(max, lapply(grobs, function(grob) { - if (is.gtable(grob)) gtable_height(grob) else grobHeight(grob) - })) - } - } } } gt