Skip to content

Commit a9983a8

Browse files
authored
Custom key glyph sizes (#5465)
* Mechanism for setting key sizes * Mechanism for getting key sizes * Feed key grobs to `measure_label_sizes()` * Add test * Add news bullet
1 parent 5e29f33 commit a9983a8

File tree

5 files changed

+160
-24
lines changed

5 files changed

+160
-24
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# ggplot2 (development version)
22

3+
* Glyphs drawing functions of the `draw_key_*()` family can now set `"width"`
4+
and `"height"` attributes (in centimetres) to the produced keys to control
5+
their displayed size in the legend.
6+
37
* `coord_radial()` is a successor to `coord_polar()` with more customisation
48
options. `coord_radial()` can:
59

R/guide-bins.R

+3-2
Original file line numberDiff line numberDiff line change
@@ -368,14 +368,15 @@ GuideBins <- ggproto(
368368

369369
dim <- if (params$direction == "vertical") c(nkeys, 1) else c(1, nkeys)
370370

371+
decor <- GuideLegend$build_decor(decor, grobs, elements, params)
372+
371373
sizes <- measure_legend_keys(
372-
params$decor, nkeys, dim, byrow = FALSE,
374+
decor, nkeys, dim, byrow = FALSE,
373375
default_width = elements$key.width,
374376
default_height = elements$key.height
375377
)
376378
sizes <- lapply(sizes, function(x) rep_len(max(x), length(x)))
377379

378-
decor <- GuideLegend$build_decor(decor, grobs, elements, params)
379380
n_layers <- length(decor) / nkeys
380381
key_id <- rep(seq_len(nkeys), each = n_layers)
381382
key_nm <- paste("key", key_id, c("bg", seq_len(n_layers - 1)))

R/guide-legend.R

+37-22
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ GuideLegend <- ggproto(
513513
keys <- lapply(decor, function(g) {
514514
data <- vec_slice(g$data, i)
515515
if (data$.draw %||% TRUE) {
516-
g$draw_key(data, g$params, key_size)
516+
key <- g$draw_key(data, g$params, key_size)
517+
set_key_size(key, data$linewidth, data$size, key_size / 10)
517518
} else {
518519
zeroGrob()
519520
}
@@ -550,7 +551,7 @@ GuideLegend <- ggproto(
550551
# A guide may have already specified the size of the decoration, only
551552
# measure when it hasn't already.
552553
sizes <- params$sizes %||% measure_legend_keys(
553-
params$decor, n = n_breaks, dim = dim, byrow = byrow,
554+
grobs$decor, n = n_breaks, dim = dim, byrow = byrow,
554555
default_width = elements$key.width,
555556
default_height = elements$key.height
556557
)
@@ -776,41 +777,55 @@ GuideLegend <- ggproto(
776777
label_hjust_defaults <- c(top = 0.5, bottom = 0.5, left = 1, right = 0)
777778
label_vjust_defaults <- c(top = 0, bottom = 1, left = 0.5, right = 0.5)
778779

779-
measure_legend_keys <- function(decor, n, dim, byrow = FALSE,
780+
measure_legend_keys <- function(keys, n, dim, byrow = FALSE,
780781
default_width = 1, default_height = 1) {
781-
if (is.null(decor)) {
782+
if (is.null(keys)) {
782783
ans <- list(widths = NULL, heights = NULL)
783784
return(ans)
784785
}
785786

786787
# Vector padding in case rows * cols > keys
787-
zeroes <- rep(0, prod(dim) - n)
788+
padding_zeroes <- rep(0, prod(dim) - n)
788789

789790
# For every layer, extract the size in cm
790-
size <- lapply(decor, function(g) {
791-
lwd <- g$data$linewidth %||% 0
792-
lwd[is.na(lwd)] <- 0
793-
size <- g$data$size %||% 0
794-
size[is.na(size)] <- 0
795-
vec_recycle((size + lwd) / 10, size = nrow(g$data))
796-
})
797-
size <- inject(cbind(!!!size))
798-
799-
# Binned legends may have `n + 1` breaks, but we need to display `n` keys.
800-
size <- vec_slice(size, seq_len(n))
801-
802-
# For every key, find maximum across all layers
803-
size <- apply(size, 1, max)
791+
widths <- c(get_key_size(keys, "width", n), padding_zeroes)
792+
heights <- c(get_key_size(keys, "height", n), padding_zeroes)
804793

805794
# Apply legend layout
806-
size <- matrix(c(size, zeroes), nrow = dim[1], ncol = dim[2], byrow = byrow)
795+
widths <- matrix(widths, nrow = dim[1], ncol = dim[2], byrow = byrow)
796+
heights <- matrix(heights, nrow = dim[1], ncol = dim[2], byrow = byrow)
807797

808798
list(
809-
widths = pmax(default_width, apply(size, 2, max)),
810-
heights = pmax(default_height, apply(size, 1, max))
799+
widths = pmax(default_width, apply(widths, 2, max)),
800+
heights = pmax(default_height, apply(heights, 1, max))
811801
)
812802
}
813803

804+
get_key_size <- function(keys, which = "width", n) {
805+
size <- lapply(keys, attr, which = which)
806+
size[lengths(size) != 1] <- 0
807+
size <- matrix(unlist(size), ncol = n)
808+
apply(size, 2, max)
809+
}
810+
811+
set_key_size <- function(key, linewidth = NULL, size = NULL, default = NULL) {
812+
if (!is.null(attr(key, "width")) && !is.null(attr(key, 'height'))) {
813+
return(key)
814+
}
815+
if (!is.null(size) || !is.null(linewidth)) {
816+
size <- size %||% 0
817+
linewidth <- linewidth %||% 0
818+
size <- if (is.na(size)[1]) 0 else size[1]
819+
linewidth <- if (is.na(linewidth)[1]) 0 else linewidth[1]
820+
size <- (size + linewidth) / 10 # From mm to cm
821+
} else {
822+
size <- NULL
823+
}
824+
attr(key, "width") <- attr(key, "width", TRUE) %||% size %||% default[1]
825+
attr(key, "height") <- attr(key, "height", TRUE) %||% size %||% default[2]
826+
key
827+
}
828+
814829
# For legend keys, check if the guide key's `.value` also occurs in the layer
815830
# data when `show.legend = NA` and data is discrete. Note that `show.legend`
816831
# besides TRUE (always show), FALSE (never show) and NA (show in relevant legend),
Loading

tests/testthat/test-draw-key.R

+16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ test_that("alternative key glyphs work", {
2020
)
2121
})
2222

23+
test_that("keys can communicate their size", {
24+
25+
draw_key_dummy <- function(data, params, size) {
26+
grob <- circleGrob(r = unit(1, "cm"))
27+
attr(grob, "width") <- 2
28+
attr(grob, "height") <- 2
29+
grob
30+
}
31+
32+
expect_doppelganger(
33+
"circle glyphs of 2cm size",
34+
ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
35+
geom_point(key_glyph = draw_key_dummy)
36+
)
37+
})
38+
2339
# Orientation-aware key glyphs --------------------------------------------
2440

2541
test_that("horizontal key glyphs work", {

0 commit comments

Comments
 (0)