Skip to content

Commit f28d8c1

Browse files
teunbrandthomasp85
andauthored
Improve palette fallback (#6674)
* scales have fallback palettes * replace fallback mechanism * populate fallback palettes * document news * add news bullet * Apply suggestion from code review Co-authored-by: Thomas Lin Pedersen <[email protected]> --------- Co-authored-by: Thomas Lin Pedersen <[email protected]>
1 parent 7df8c69 commit f28d8c1

20 files changed

+132
-69
lines changed

NEWS.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
(@mitchelloharawild, #6609).
1010
* Fixed regression where `scale_{x,y}_*()` threw an error when an expression
1111
object is set to `labels` argument (@yutannihilation, #6617).
12-
13-
12+
* Improved palette fallback mechanism in scales (@teunbrand, #6669).
1413
* Allow `stat` in `geom_hline`, `geom_vline`, and `geom_abline`. (@sierrajohnson, #6559)
1514
* `stat_boxplot()` treats `width` as an optional aesthetic (@Yunuuuu, #6575)
1615

R/scale-.R

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@
9292
#' to generate the values for the `expand` argument. The defaults are to
9393
#' expand the scale by 5% on each side for continuous variables, and by
9494
#' 0.6 units on each side for discrete variables.
95+
#' @param fallback.palette Function to use when `palette = NULL` and the
96+
#' palette is not represented in the theme.
9597
#' @param position For position scales, The position of the axis.
9698
#' `left` or `right` for y axes, `top` or `bottom` for x axes.
9799
#' @param call The `call` used to construct the scale for reporting messages.
@@ -107,6 +109,7 @@ continuous_scale <- function(aesthetics, scale_name = deprecated(), palette, nam
107109
oob = censor, expand = waiver(), na.value = NA,
108110
transform = "identity", trans = deprecated(),
109111
guide = "legend", position = "left",
112+
fallback.palette = NULL,
110113
call = caller_call(),
111114
super = ScaleContinuous) {
112115
call <- call %||% current_call()
@@ -121,6 +124,7 @@ continuous_scale <- function(aesthetics, scale_name = deprecated(), palette, nam
121124
aesthetics <- standardise_aes_names(aesthetics)
122125

123126
check_breaks_labels(breaks, labels, call = call)
127+
check_fallback_palette(palette, fallback.palette, call = call)
124128

125129
position <- arg_match0(position, c("left", "right", "top", "bottom"))
126130

@@ -152,6 +156,7 @@ continuous_scale <- function(aesthetics, scale_name = deprecated(), palette, nam
152156

153157
aesthetics = aesthetics,
154158
palette = palette,
159+
fallback_palette = fallback.palette,
155160

156161
range = ContinuousRange$new(),
157162
limits = limits,
@@ -211,6 +216,7 @@ discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name
211216
labels = waiver(), limits = NULL, expand = waiver(),
212217
na.translate = TRUE, na.value = NA, drop = TRUE,
213218
guide = "legend", position = "left",
219+
fallback.palette = NULL,
214220
call = caller_call(),
215221
super = ScaleDiscrete) {
216222
call <- call %||% current_call()
@@ -221,6 +227,7 @@ discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name
221227
aesthetics <- standardise_aes_names(aesthetics)
222228

223229
check_breaks_labels(breaks, labels, call = call)
230+
check_fallback_palette(palette, fallback.palette, call = call)
224231

225232
# Convert formula input to function if appropriate
226233
limits <- allow_lambda(limits)
@@ -251,6 +258,7 @@ discrete_scale <- function(aesthetics, scale_name = deprecated(), palette, name
251258

252259
aesthetics = aesthetics,
253260
palette = palette,
261+
fallback_palette = fallback.palette,
254262

255263
range = DiscreteRange$new(),
256264
limits = limits,
@@ -303,6 +311,7 @@ binned_scale <- function(aesthetics, scale_name = deprecated(), palette, name =
303311
right = TRUE, transform = "identity",
304312
trans = deprecated(), show.limits = FALSE,
305313
guide = "bins", position = "left",
314+
fallback.palette = NULL,
306315
call = caller_call(),
307316
super = ScaleBinned) {
308317
if (lifecycle::is_present(scale_name)) {
@@ -318,6 +327,7 @@ binned_scale <- function(aesthetics, scale_name = deprecated(), palette, name =
318327
aesthetics <- standardise_aes_names(aesthetics)
319328

320329
check_breaks_labels(breaks, labels, call = call)
330+
check_fallback_palette(palette, fallback.palette, call = call)
321331

322332
position <- arg_match0(position, c("left", "right", "top", "bottom"))
323333

@@ -346,6 +356,7 @@ binned_scale <- function(aesthetics, scale_name = deprecated(), palette, name =
346356

347357
aesthetics = aesthetics,
348358
palette = palette,
359+
fallback_palette = fallback.palette,
349360

350361
range = ContinuousRange$new(),
351362
limits = limits,
@@ -589,7 +600,7 @@ Scale <- ggproto("Scale", NULL,
589600
if (empty(df)) {
590601
return()
591602
}
592-
self$palette <- self$palette %||% fallback_palette(self)
603+
self$palette <- self$palette %||% fetch_ggproto(self, "fallback_palette")
593604

594605
aesthetics <- intersect(self$aesthetics, names(df))
595606
names(aesthetics) <- aesthetics
@@ -1775,6 +1786,15 @@ check_continuous_limits <- function(limits, ...,
17751786
check_length(limits, 2L, arg = arg, call = call)
17761787
}
17771788

1789+
check_fallback_palette <- function(pal, fallback, call = caller_env()) {
1790+
if (!is.null(pal) || is.function(fallback)) {
1791+
return(invisible())
1792+
}
1793+
cli::cli_abort(
1794+
"When {.code palette = NULL}, the {.arg fallback.palette} must be defined."
1795+
)
1796+
}
1797+
17781798
allow_lambda <- function(x) {
17791799
# we check the 'call' class to prevent interpreting `bquote()` calls as a function
17801800
if (is_formula(x, lhs = FALSE) && !inherits(x, "call")) as_function(x) else x

R/scale-alpha.R

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@
3333
#' p + scale_alpha("cylinders")
3434
scale_alpha <- function(name = waiver(), ..., range = NULL, aesthetics = "alpha") {
3535
palette <- if (!is.null(range)) pal_rescale(range) else NULL
36-
continuous_scale(aesthetics, name = name, palette = palette, ...)
36+
continuous_scale(
37+
aesthetics, name = name, palette = palette,
38+
fallback.palette = pal_rescale(c(0.1, 1)),
39+
...
40+
)
3741
}
3842

3943
#' @rdname scale_alpha
@@ -44,7 +48,11 @@ scale_alpha_continuous <- scale_alpha
4448
#' @export
4549
scale_alpha_binned <- function(name = waiver(), ..., range = NULL, aesthetics = "alpha") {
4650
palette <- if (!is.null(range)) pal_rescale(range) else NULL
47-
binned_scale(aesthetics, name = name, palette = palette, ...)
51+
binned_scale(
52+
aesthetics, name = name, palette = palette,
53+
fallback.palette = pal_rescale(c(0.1, 1)),
54+
...
55+
)
4856
}
4957

5058
#' @rdname scale_alpha
@@ -64,7 +72,11 @@ scale_alpha_ordinal <- function(name = waiver(), ..., range = NULL, aesthetics =
6472
} else {
6573
NULL
6674
}
67-
discrete_scale(aesthetics, name = name, palette = palette, ...)
75+
discrete_scale(
76+
aesthetics, name = name, palette = palette,
77+
fallback.palette = function(n) seq(0.1, 1, length.out = n),
78+
...
79+
)
6880
}
6981

7082
#' @rdname scale_alpha
@@ -74,7 +86,8 @@ scale_alpha_datetime <- function(name = waiver(), ..., range = NULL, aesthetics
7486
palette <- if (!is.null(range)) pal_rescale(range) else NULL
7587
datetime_scale(
7688
aesthetics = aesthetics, transform = "time", name = name,
77-
palette = palette, ...
89+
palette = palette, fallback.palette = pal_rescale(c(0.1, 1)),
90+
...
7891
)
7992
}
8093

@@ -85,6 +98,7 @@ scale_alpha_date <- function(name = waiver(), ..., range = NULL, aesthetics = "a
8598
palette <- if (!is.null(range)) pal_rescale(range) else NULL
8699
datetime_scale(
87100
aesthetics = aesthetics, transform = "date", name = name,
88-
palette = palette, ...
101+
palette = palette, fallback.palette = pal_rescale(c(0.1, 1)),
102+
...
89103
)
90104
}

R/scale-colour.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ scale_colour_continuous <- function(..., palette = NULL, aesthetics = "colour",
9393
palette <- if (!is.null(palette)) as_continuous_pal(palette)
9494
continuous_scale(
9595
aesthetics, palette = palette, guide = guide, na.value = na.value,
96+
fallback.palette = pal_seq_gradient("#132B43", "#56B1F7"),
9697
...
9798
)
9899
}
@@ -115,6 +116,7 @@ scale_fill_continuous <- function(..., palette = NULL, aesthetics = "fill", guid
115116
palette <- if (!is.null(palette)) as_continuous_pal(palette)
116117
continuous_scale(
117118
aesthetics, palette = palette, guide = guide, na.value = na.value,
119+
fallback.palette = pal_seq_gradient("#132B43", "#56B1F7"),
118120
...
119121
)
120122
}
@@ -137,6 +139,7 @@ scale_colour_binned <- function(..., palette = NULL, aesthetics = "colour", guid
137139
palette <- if (!is.null(palette)) pal_binned(as_discrete_pal(palette))
138140
binned_scale(
139141
aesthetics, palette = palette, guide = guide, na.value = na.value,
142+
fallback.palette = pal_seq_gradient("#132B43", "#56B1F7"),
140143
...
141144
)
142145
}
@@ -158,6 +161,7 @@ scale_fill_binned <- function(..., palette = NULL, aesthetics = "fill", guide =
158161
palette <- if (!is.null(palette)) pal_binned(as_discrete_pal(palette))
159162
binned_scale(
160163
aesthetics, palette = palette, guide = guide, na.value = na.value,
164+
fallback.palette = pal_seq_gradient("#132B43", "#56B1F7"),
161165
...
162166
)
163167
}
@@ -227,6 +231,7 @@ scale_colour_discrete <- function(..., palette = NULL, aesthetics = "colour", na
227231
palette <- if (!is.null(palette)) as_discrete_pal(palette)
228232
discrete_scale(
229233
aesthetics, palette = palette, na.value = na.value,
234+
fallback.palette = pal_hue(),
230235
...
231236
)
232237
}
@@ -248,6 +253,7 @@ scale_fill_discrete <- function(..., palette = NULL, aesthetics = "fill", na.val
248253
palette <- if (!is.null(palette)) as_discrete_pal(palette)
249254
discrete_scale(
250255
aesthetics, palette = palette, na.value = na.value,
256+
fallback.palette = pal_hue(),
251257
...
252258
)
253259
}

R/scale-discrete-.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#' level, and increasing by one for each level (i.e. the labels are placed
1111
#' at integer positions). This is what allows jittering to work.
1212
#'
13-
#' @inheritDotParams discrete_scale -scale_name
13+
#' @inheritDotParams discrete_scale -scale_name -fallback.palette
1414
#' @inheritParams discrete_scale
1515
#' @param palette A palette function that when called with a single integer
1616
#' argument (the number of levels in the scale) returns the numerical values

R/scale-gradient.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#' @param low,high Colours for low and high ends of the gradient.
1717
#' @param guide Type of legend. Use `"colourbar"` for continuous
1818
#' colour bar, or `"legend"` for discrete colour legend.
19-
#' @inheritDotParams continuous_scale -na.value -guide -aesthetics -expand -position -palette
19+
#' @inheritDotParams continuous_scale -na.value -guide -aesthetics -expand -position -palette -fallback.palette
2020
#' @seealso [scales::pal_seq_gradient()] for details on underlying
2121
#' palette, [scale_colour_steps()] for binned variants of these scales.
2222
#'

R/scale-grey.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#'
66
#' @inheritParams scales::pal_grey
77
#' @inheritParams scale_colour_hue
8-
#' @inheritDotParams discrete_scale -expand -position -scale_name -palette
8+
#' @inheritDotParams discrete_scale -expand -position -scale_name -palette -fallback.palette
99
#' @family colour scales
1010
#' @seealso
1111
#' The documentation on [colour aesthetics][aes_colour_fill_alpha].

R/scale-hue.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#' It does not generate colour-blind safe palettes.
55
#'
66
#' @param na.value Colour to use for missing values
7-
#' @inheritDotParams discrete_scale -aesthetics -expand -position -scale_name -palette
7+
#' @inheritDotParams discrete_scale -aesthetics -expand -position -scale_name -palette -fallback.palette
88
#' @param aesthetics Character string or vector of character strings listing the
99
#' name(s) of the aesthetic(s) that this scale works with. This can be useful, for
1010
#' example, to apply colour settings to the `colour` and `fill` aesthetics at the

R/scale-linetype.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#' no inherent order, this use is not advised.
77
#'
88
#' @inheritParams discrete_scale
9-
#' @inheritDotParams discrete_scale -expand -position -na.value -scale_name -palette
9+
#' @inheritDotParams discrete_scale -expand -position -na.value -scale_name -palette -fallback.palette
1010
#' @rdname scale_linetype
1111
#' @details
1212
#' Lines can be referred to by number, name or hex code. Contrary to base R
@@ -45,7 +45,7 @@
4545
scale_linetype <- function(name = waiver(), ..., aesthetics = "linetype") {
4646
discrete_scale(
4747
aesthetics, name = name,
48-
palette = NULL,
48+
palette = NULL, fallback.palette = pal_linetype(),
4949
...
5050
)
5151
}
@@ -55,7 +55,7 @@ scale_linetype <- function(name = waiver(), ..., aesthetics = "linetype") {
5555
scale_linetype_binned <- function(name = waiver(), ..., aesthetics = "linetype") {
5656
binned_scale(
5757
aesthetics, name = name,
58-
palette = NULL,
58+
palette = NULL, fallback.palette = pal_binned(pal_linetype()),
5959
...
6060
)
6161
}

R/scale-linewidth.R

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ scale_linewidth_continuous <- function(name = waiver(), breaks = waiver(),
3636
guide = "legend",
3737
aesthetics = "linewidth") {
3838
palette <- if (!is.null(range)) pal_rescale(range) else NULL
39-
continuous_scale(aesthetics, palette = palette, name = name,
40-
breaks = breaks, labels = labels, limits = limits,
41-
transform = transform, trans = trans, guide = guide)
39+
continuous_scale(
40+
aesthetics, palette = palette, name = name,
41+
breaks = breaks, labels = labels, limits = limits,
42+
transform = transform, trans = trans, guide = guide,
43+
fallback.palette = pal_rescale(c(1, 6))
44+
)
4245
}
4346

4447
#' @rdname scale_linewidth
@@ -52,10 +55,13 @@ scale_linewidth_binned <- function(name = waiver(), breaks = waiver(), labels =
5255
nice.breaks = TRUE, transform = "identity",
5356
trans = deprecated(), guide = "bins", aesthetics = "linewidth") {
5457
palette <- if (!is.null(range)) pal_rescale(range) else NULL
55-
binned_scale(aesthetics, palette = palette, name = name,
56-
breaks = breaks, labels = labels, limits = limits,
57-
transform = transform, trans = trans, n.breaks = n.breaks,
58-
nice.breaks = nice.breaks, guide = guide)
58+
binned_scale(
59+
aesthetics, palette = palette, name = name,
60+
breaks = breaks, labels = labels, limits = limits,
61+
transform = transform, trans = trans, n.breaks = n.breaks,
62+
nice.breaks = nice.breaks, guide = guide,
63+
fallback.palette = pal_rescale(c(1, 6))
64+
)
5965
}
6066

6167
#' @rdname scale_linewidth
@@ -77,7 +83,11 @@ scale_linewidth_ordinal <- function(name = waiver(), ..., range = NULL, aestheti
7783
} else {
7884
NULL
7985
}
80-
discrete_scale(aesthetics, name = name, palette = palette, ...)
86+
discrete_scale(
87+
aesthetics, name = name, palette = palette,
88+
fallback.palette = function(n) seq(2, 6, length.out = n),
89+
...
90+
)
8191
}
8292

8393
#' @rdname scale_linewidth
@@ -87,7 +97,8 @@ scale_linewidth_datetime <- function(name = waiver(), ..., range = NULL, aesthet
8797
palette <- if (!is.null(range)) pal_rescale(range) else NULL
8898
datetime_scale(
8999
aesthetics, transform = "time", name = name,
90-
palette = palette, ...
100+
palette = palette, fallback.palette = pal_rescale(c(1, 6)),
101+
...
91102
)
92103
}
93104

@@ -98,6 +109,7 @@ scale_linewidth_date <- function(name = waiver(), ..., range = NULL, aesthetics
98109
palette <- if (!is.null(range)) pal_rescale(range) else NULL
99110
datetime_scale(
100111
aesthetics, transform = "date", name = name,
101-
palette = palette, ...
112+
palette = palette, fallback.palette = pal_rescale(c(1, 6)),
113+
...
102114
)
103115
}

0 commit comments

Comments
 (0)