Skip to content

Commit 73b4119

Browse files
authored
Setting panel sizes in theme() (#6094)
* add panel.widths/panel.heights theme elements * add `set_panel_size()` method to layout * add test * document * add aspect ratio warning * turn off respect * `set_panel_size()` is a Facet method
1 parent c00a154 commit 73b4119

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
(@teunbrand, #4722, #6069).
239239
* `geom_abline()` clips to the panel range in the vertical direction too
240240
(@teunbrand, #6086).
241+
* Added `panel.widths` and `panel.heights` to `theme()` (#5338, @teunbrand).
241242

242243
# ggplot2 3.5.1
243244

R/facet-.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,53 @@ Facet <- ggproto("Facet", NULL,
263263
},
264264
format_strip_labels = function(layout, params) {
265265
return()
266+
},
267+
set_panel_size = function(table, theme) {
268+
269+
new_widths <- calc_element("panel.widths", theme)
270+
new_heights <- calc_element("panel.heights", theme)
271+
272+
if (is.null(new_widths) && is.null(new_heights)) {
273+
return(table)
274+
}
275+
276+
if (isTRUE(table$respect)) {
277+
args <- !c(is.null(new_widths), is.null(new_heights))
278+
args <- c("panel.widths", "panel.heights")[args]
279+
cli::cli_warn(
280+
"Aspect ratios are overruled by {.arg {args}} theme element{?s}."
281+
)
282+
table$respect <- FALSE
283+
}
284+
285+
rows <- panel_rows(table)
286+
cols <- panel_cols(table)
287+
288+
if (length(new_widths) == 1L && nrow(cols) > 1L) {
289+
# Get total size of non-panel widths in between panels
290+
extra <- setdiff(seq(min(cols$l), max(cols$r)), union(cols$l, cols$r))
291+
extra <- unit(sum(width_cm(table$widths[extra])), "cm")
292+
# Distribute width proportionally
293+
relative <- as.numeric(table$widths[cols$l]) # assumed to be simple units
294+
new_widths <- (new_widths - extra) * (relative / sum(relative))
295+
}
296+
if (!is.null(new_widths)) {
297+
table$widths[cols$l] <- rep(new_widths, length.out = nrow(cols))
298+
}
299+
300+
if (length(new_heights) == 1L && nrow(rows) > 1L) {
301+
# Get total size of non-panel heights in between panels
302+
extra <- setdiff(seq(min(rows$t), max(rows$t)), union(rows$t, rows$b))
303+
extra <- unit(sum(height_cm(table$heights[extra])), "cm")
304+
# Distribute height proportionally
305+
relative <- as.numeric(table$heights[rows$t]) # assumed to be simple units
306+
new_heights <- (new_heights - extra) * (relative / sum(relative))
307+
}
308+
if (!is.null(new_heights)) {
309+
table$heights[rows$t] <- rep(new_heights, length.out = nrow(rows))
310+
}
311+
312+
table
266313
}
267314
)
268315

R/layout.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ Layout <- ggproto("Layout", NULL,
9494
theme,
9595
self$facet_params
9696
)
97+
plot_table <- self$facet$set_panel_size(plot_table, theme)
9798

9899
# Draw individual labels, then add to gtable
99100
labels <- self$coord$labels(
@@ -300,7 +301,6 @@ Layout <- ggproto("Layout", NULL,
300301
}
301302
)
302303

303-
304304
# Helpers -----------------------------------------------------------------
305305

306306
# Function for applying scale method to multiple variables in a given

R/theme-elements.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,8 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) {
639639
panel.grid.minor.x = el_def("element_line", "panel.grid.minor"),
640640
panel.grid.minor.y = el_def("element_line", "panel.grid.minor"),
641641
panel.ontop = el_def("logical"),
642+
panel.widths = el_def("unit"),
643+
panel.heights = el_def("unit"),
642644

643645
strip.background = el_def("element_rect", "rect"),
644646
strip.background.x = el_def("element_rect", "strip.background"),

R/theme.R

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@
143143
#' and x axis grid lines are vertical. `panel.grid.*.*` inherits from
144144
#' `panel.grid.*` which inherits from `panel.grid`, which in turn inherits
145145
#' from `line`
146+
#' @param panel.widths,panel.heights Sizes for panels (`units`). Can be a
147+
#' single unit to set the total size for the panel area, or a unit vector to
148+
#' set the size of individual panels.
146149
#' @param panel.ontop option to place the panel (background, gridlines) over
147150
#' the data layers (`logical`). Usually used with a transparent or blank
148151
#' `panel.background`.
@@ -427,6 +430,8 @@ theme <- function(...,
427430
panel.grid.minor.x,
428431
panel.grid.minor.y,
429432
panel.ontop,
433+
panel.widths,
434+
panel.heights,
430435
plot.background,
431436
plot.title,
432437
plot.title.position,

man/theme.Rd

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-theme.R

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,47 @@ test_that("complete_theme completes a theme", {
617617
reset_theme_settings()
618618
})
619619

620+
test_that("panel.widths and panel.heights works with free-space panels", {
621+
622+
df <- data.frame(x = c(1, 1, 2, 1, 3), g = c("A", "B", "B", "C", "C"))
623+
624+
p <- ggplotGrob(
625+
ggplot(df, aes(x, x)) +
626+
geom_point() +
627+
scale_x_continuous(expand = expansion(add = 1)) +
628+
facet_grid(~ g, scales = "free_x", space = "free_x") +
629+
theme(
630+
panel.widths = unit(11, "cm"),
631+
panel.spacing.x = unit(1, "cm")
632+
)
633+
)
634+
635+
idx <- range(panel_cols(p)$l)
636+
expect_equal(as.numeric(p$widths[seq(idx[1], idx[2])]), c(2, 1, 3, 1, 4))
637+
638+
p <- ggplotGrob(
639+
ggplot(df, aes(x, x)) +
640+
geom_point() +
641+
scale_y_continuous(expand = expansion(add = 1)) +
642+
facet_grid(g ~ ., scales = "free_y", space = "free_y") +
643+
theme(
644+
panel.heights = unit(11, "cm"),
645+
panel.spacing.y = unit(1, "cm")
646+
)
647+
)
648+
649+
idx <- range(panel_rows(p)$t)
650+
expect_equal(as.numeric(p$heights[seq(idx[1], idx[2])]), c(2, 1, 3, 1, 4))
651+
652+
})
653+
654+
test_that("panel.widths and panel.heights appropriately warn about aspect override", {
655+
p <- ggplot(mpg, aes(displ, hwy)) +
656+
geom_point() +
657+
theme(aspect.ratio = 1, panel.widths = unit(4, "cm"))
658+
expect_warning(ggplotGrob(p), "Aspect ratios are overruled")
659+
})
660+
620661
test_that("margin_part() mechanics work as expected", {
621662

622663
t <- theme_gray() +
@@ -630,7 +671,6 @@ test_that("margin_part() mechanics work as expected", {
630671

631672
test <- calc_element("plot.margin", t)
632673
expect_equal(as.numeric(test), c(5.5, 5.5, 11, 5.5))
633-
634674
})
635675

636676
# Visual tests ------------------------------------------------------------

0 commit comments

Comments
 (0)