From e1ea0febf20efea8999074e618b4861d445347a5 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 18 Jun 2024 09:27:39 +0200 Subject: [PATCH 01/21] add palette theme elements --- R/theme-elements.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/theme-elements.R b/R/theme-elements.R index 6aa27dd5f8..ed69c82a57 100644 --- a/R/theme-elements.R +++ b/R/theme-elements.R @@ -608,6 +608,11 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) { plot.tag.location = el_def("character"), plot.margin = el_def(c("margin", "rel"), "margins"), + palette.colour.discrete = el_def(c("character", "function")), + palette.fill.discrete = el_def(c("character", "function"), "palette.colour.discrete"), + palette.colour.continuous = el_def(c("character", "function")), + palette.fill.continuous = el_def(c("character", "function"), "palette.colour.continuous"), + aspect.ratio = el_def(c("numeric", "integer")) ) From aec8cb154878f58c08066253763684fba0dbcfbc Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Tue, 18 Jun 2024 09:29:37 +0200 Subject: [PATCH 02/21] set palettes --- R/plot-build.R | 2 ++ R/scales-.R | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/R/plot-build.R b/R/plot-build.R index 7b90d9cf26..d7482a955e 100644 --- a/R/plot-build.R +++ b/R/plot-build.R @@ -102,6 +102,8 @@ ggplot_build.ggplot <- function(plot) { # Train and map non-position scales and guides npscales <- scales$non_position_scales() if (npscales$n() > 0) { + #TODO: if #5854 gets merged, we shouldn't need `plot_theme()` here + npscales$set_palettes(plot_theme(plot)) lapply(data, npscales$train_df) plot$guides <- plot$guides$build(npscales, plot$layers, plot$labels, data) data <- lapply(data, npscales$map_df) diff --git a/R/scales-.R b/R/scales-.R index e62eb0e8cb..3422995a0e 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -168,6 +168,40 @@ ScalesList <- ggproto("ScalesList", NULL, scale_name <- paste("scale", aes, "continuous", sep = "_") self$add(find_global(scale_name, env, mode = "function")()) } + }, + + set_palettes = function(self, theme) { + for (scale in self$scales) { + if (!is.null(scale$palette)) { + next + } + elem <- calc_element(paste0( + "palette.", scale$aesthetics[1], ".", + if (scale$is_discrete()) "discrete" else "continuous" + ), theme) + if (is.character(elem)) { + if (length(elem) == 1) { + # Assume we have a name for a palette, so search for palette function + elem <- get0(paste0("pal_", elem), mode = "function") + if (is.function(elem)) { + elem <- elem() + } + } else { + # We might have a vector of colours + if (scale$is_discrete()) { + elem <- manual_pal(elem) + } else { + elem <- gradient_n_pal(elem) + } + } + } + if (!is.function(elem)) { + cli::cli_warn( + "Failed to find palette for {.field {scale$aesthetics[1]}} scale." + ) + } + scale$palette <- elem + } } ) From e2d02371cc95bcbeff4f80e5979f5905d0c13f80 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 11:20:16 +0200 Subject: [PATCH 03/21] expand options for numeric palettes --- R/scale-.R | 3 +++ R/scales-.R | 32 +++++++++++++++++++------------- R/utilities.R | 24 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/R/scale-.R b/R/scale-.R index 9eaa153590..72d44df0b3 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -518,6 +518,9 @@ Scale <- ggproto("Scale", NULL, if (empty(df)) { return() } + if (is.null(self$palette)) { + self$palette <- fallback_palette(self$aesthetics[1], self$is_discrete()) + } aesthetics <- intersect(self$aesthetics, names(df)) names(aesthetics) <- aesthetics diff --git a/R/scales-.R b/R/scales-.R index 3422995a0e..886ed82307 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -179,22 +179,28 @@ ScalesList <- ggproto("ScalesList", NULL, "palette.", scale$aesthetics[1], ".", if (scale$is_discrete()) "discrete" else "continuous" ), theme) - if (is.character(elem)) { - if (length(elem) == 1) { - # Assume we have a name for a palette, so search for palette function - elem <- get0(paste0("pal_", elem), mode = "function") - if (is.function(elem)) { - elem <- elem() - } + + # TODO: ideally {scales} would have some sort of `as_palette()` function + # String might be a name for a palette function + if (is_bare_string(elem)) { + elem <- get0(paste0("pal_", elem), mode = "function") + if (is.function(elem)) { + elem <- elem() + } + } + + if (is.atomic(elem) && !is.null(elem)) { + if (scale$is_discrete()) { + elem <- pal_manual(elem) + } else if (is.character(elem)) { + elem <- pal_gradient_n(elem) } else { - # We might have a vector of colours - if (scale$is_discrete()) { - elem <- manual_pal(elem) - } else { - elem <- gradient_n_pal(elem) - } + elem <- pal_rescale(range = rep(elem, length.out = 2)) } } + + elem <- elem %||% fallback_palette(scale$aesthetics[1], scale$is_discrete()) + if (!is.function(elem)) { cli::cli_warn( "Failed to find palette for {.field {scale$aesthetics[1]}} scale." diff --git a/R/utilities.R b/R/utilities.R index 1a9181be69..7b46f52d34 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -818,3 +818,27 @@ as_unordered_factor <- function(x) { class(x) <- setdiff(class(x), "ordered") x } + +fallback_palette <- function(aes, discrete) { + if (discrete) { + pal <- switch( + aes, + colour = , fill = pal_hue(), + alpha = function(n) seq(0.1, 1, length.out = n), + linewidth = function(n) seq(2, 6, length.out = n), + linetype = pal_linetype(), + shape = pal_shape(), + size = function(n) sqrt(seq(4, 36, length.out = n)), + NULL + ) + return(pal) + } + switch( + aes, + colour = , fill = pal_seq_gradient("#132B43", "#56B1F7"), + alpha = pal_rescale(c(0.1, 1)), + linewidth = pal_rescale(c(1, 6)), + size = pal_area(), + NULL + ) +} From e0a9449fc9742c63969529b77c93d0b372720cb6 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 15:22:24 +0200 Subject: [PATCH 04/21] backward compatibility for `options()` default scales --- R/scale-colour.R | 64 +++++++++++++++++++ .../_snaps/scale-colour-continuous.md | 2 - 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/R/scale-colour.R b/R/scale-colour.R index 71255e2033..4f86d01f82 100644 --- a/R/scale-colour.R +++ b/R/scale-colour.R @@ -222,3 +222,67 @@ check_scale_type <- function(scale, name, aesthetic, scale_is_discrete = FALSE, scale } + +# helper function for backwards compatibility through setting defaults +# scales through `options()` instead of `theme()`. +scale_backward_compatibility <- function(..., scale, aesthetic, type) { + aesthetic <- standardise_aes_names(aesthetic[1]) + + args <- list2(...) + args$call <- args$call %||% caller_call() %||% current_call() + + if (type == "binned") { + fallback <- getOption( + paste("ggplot2", type, aesthetic, sep = "."), + default = "gradient" + ) + if (is.function(fallback)) { + fallback <- "gradient" + } + scale <- scale %||% fallback + } + + if (is_bare_string(scale)) { + if (scale == "continuous") { + scale <- "gradient" + } + if (scale == "discrete") { + scale <- "hue" + } + candidates <- paste("scale", aesthetic, scale, sep = "_") + for (candi in candidates) { + f <- find_global(candi, env = caller_env(), mode = "function") + if (!is.null(f)) { + scale <- f + break + } + } + } + + if (!is.function(scale) && type == "discrete") { + args$type <- scale + scale <- switch( + aesthetic, + colour = scale_colour_qualitative, + fill = scale_fill_qualitative + ) + } + + if (is.function(scale)) { + if (!any(c("...", "call") %in% fn_fmls_names(scale))) { + args$call <- NULL + } + if (!"..." %in% fn_fmls_names(scale)) { + args <- args[intersect(names(args), fn_fmls_names(scale))] + } + scale <- check_scale_type( + exec(scale, !!!args), + paste("scale", aesthetic, type, sep = "_"), + aesthetic, + scale_is_discrete = type == "discrete" + ) + return(scale) + } + + cli::cli_abort("Unknown scale type: {.val {scale}}") +} diff --git a/tests/testthat/_snaps/scale-colour-continuous.md b/tests/testthat/_snaps/scale-colour-continuous.md index a5410a8799..14d6e6a95d 100644 --- a/tests/testthat/_snaps/scale-colour-continuous.md +++ b/tests/testthat/_snaps/scale-colour-continuous.md @@ -21,10 +21,8 @@ --- Unknown scale type: "abc" - i Use either "gradient" or "viridis". --- Unknown scale type: "abc" - i Use either "gradient" or "viridis". From d57a2cf1fc08f0ea909f6f904f3b88fa671bcc96 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 15:23:04 +0200 Subject: [PATCH 05/21] use explicit scales in tests --- tests/testthat/test-guides.R | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/testthat/test-guides.R b/tests/testthat/test-guides.R index a2e5ae918d..b2811bf0c2 100644 --- a/tests/testthat/test-guides.R +++ b/tests/testthat/test-guides.R @@ -41,7 +41,7 @@ test_that("adding guides doesn't change plot state", { test_that("colourbar trains without labels", { g <- guide_colorbar() - sc <- scale_colour_continuous(limits = c(0, 4), labels = NULL) + sc <- scale_colour_gradient(limits = c(0, 4), labels = NULL) out <- g$train(scale = sc) expect_equal(names(out$key), c("colour", ".value")) @@ -178,34 +178,34 @@ test_that("guide merging for guide_legend() works as expected", { } different_limits <- merge_test_guides( - scale_colour_discrete(limits = c("a", "b", "c", "d")), + scale_colour_hue(limits = c("a", "b", "c", "d")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(different_limits, 2) same_limits <- merge_test_guides( - scale_colour_discrete(limits = c("a", "b", "c")), + scale_colour_hue(limits = c("a", "b", "c")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(same_limits, 1) expect_equal(same_limits[[1]]$key$.label, c("a", "b", "c")) same_labels_different_limits <- merge_test_guides( - scale_colour_discrete(limits = c("a", "b", "c")), + scale_colour_hue(limits = c("a", "b", "c")), scale_linetype_discrete(limits = c("one", "two", "three"), labels = c("a", "b", "c")) ) expect_length(same_labels_different_limits, 1) expect_equal(same_labels_different_limits[[1]]$key$.label, c("a", "b", "c")) same_labels_different_scale <- merge_test_guides( - scale_colour_continuous(limits = c(0, 4), breaks = 1:3, labels = c("a", "b", "c")), + scale_colour_gradient(limits = c(0, 4), breaks = 1:3, labels = c("a", "b", "c")), scale_linetype_discrete(limits = c("a", "b", "c")) ) expect_length(same_labels_different_scale, 1) expect_equal(same_labels_different_scale[[1]]$key$.label, c("a", "b", "c")) repeated_identical_labels <- merge_test_guides( - scale_colour_discrete(limits = c("one", "two", "three"), labels = c("label1", "label1", "label2")), + scale_colour_hue(limits = c("one", "two", "three"), labels = c("label1", "label1", "label2")), scale_linetype_discrete(limits = c("1", "2", "3"), labels = c("label1", "label1", "label2")) ) expect_length(repeated_identical_labels, 1) @@ -270,7 +270,7 @@ test_that("colorsteps and bins checks the breaks format", { test_that("legend reverse argument reverses the key", { - scale <- scale_colour_discrete() + scale <- scale_colour_hue() scale$train(LETTERS[1:4]) guides <- guides_list(NULL) From 52d7fe69688dd398a50d711059cadb11ab0e7059 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 15:23:55 +0200 Subject: [PATCH 06/21] standard colour scales have `palette = NULL` --- R/scale-colour.R | 145 +++++++++++++++++------------------------------ R/scale-hue.R | 56 +++++++----------- 2 files changed, 74 insertions(+), 127 deletions(-) diff --git a/R/scale-colour.R b/R/scale-colour.R index 4f86d01f82..0b31c2f2fa 100644 --- a/R/scale-colour.R +++ b/R/scale-colour.R @@ -77,122 +77,81 @@ #' v #' options(ggplot2.continuous.fill = tmp) # restore previous setting #' @export -scale_colour_continuous <- function(..., +scale_colour_continuous <- function(..., aesthetics = "colour", + guide = "colourbar", na.value = "grey50", type = getOption("ggplot2.continuous.colour")) { - type <- type %||% "gradient" - args <- list2(...) - args$call <- args$call %||% current_call() - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type(exec(type, !!!args), "scale_colour_continuous", "colour") - } else if (identical(type, "gradient")) { - exec(scale_colour_gradient, !!!args) - } else if (identical(type, "viridis")) { - exec(scale_colour_viridis_c, !!!args) - } else { - cli::cli_abort(c( - "Unknown scale type: {.val {type}}", - "i" = "Use either {.val gradient} or {.val viridis}." - )) + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., guide = guide, na.value = na.value, scale = type, + aesthetic = "colour", type = "continuous" + ) + return(scale) } + + continuous_scale( + aesthetics, palette = NULL, guide = guide, na.value = na.value, + ... + ) } #' @rdname scale_colour_continuous #' @export -scale_fill_continuous <- function(..., +scale_fill_continuous <- function(..., aesthetics = "fill", guide = "colourbar", + na.value = "grey50", type = getOption("ggplot2.continuous.fill")) { - type <- type %||% "gradient" - args <- list2(...) - args$call <- args$call %||% current_call() - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type(exec(type, !!!args), "scale_fill_continuous", "fill") - } else if (identical(type, "gradient")) { - exec(scale_fill_gradient, !!!args) - } else if (identical(type, "viridis")) { - exec(scale_fill_viridis_c, !!!args) - } else { - cli::cli_abort(c( - "Unknown scale type: {.val {type}}", - "i" = "Use either {.val gradient} or {.val viridis}." - )) + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., guide = guide, na.value = na.value, scale = type, + aesthetic = "fill", type = "continuous" + ) + return(scale) } + + continuous_scale( + aesthetics, palette = NULL, guide = guide, na.value = na.value, + ... + ) } #' @export #' @rdname scale_colour_continuous -scale_colour_binned <- function(..., +scale_colour_binned <- function(..., aesthetics = "colour", guide = "coloursteps", + na.value = "grey50", type = getOption("ggplot2.binned.colour")) { - args <- list2(...) - args$call <- args$call %||% current_call() - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type(exec(type, !!!args), "scale_colour_binned", "colour") - } else { - type_fallback <- getOption("ggplot2.continuous.colour", default = "gradient") - # don't use fallback from scale_colour_continuous() if it is - # a function, since that would change the type of the color - # scale from binned to continuous - if (is.function(type_fallback)) { - type_fallback <- "gradient" - } - type <- type %||% type_fallback - - if (identical(type, "gradient")) { - exec(scale_colour_steps, !!!args) - } else if (identical(type, "viridis")) { - exec(scale_colour_viridis_b, !!!args) - } else { - cli::cli_abort(c( - "Unknown scale type: {.val {type}}", - "i" = "Use either {.val gradient} or {.val viridis}." - )) - } + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., guide = guide, na.value = na.value, scale = type, + aesthetic = "colour", type = "binned" + ) + return(scale) } + + binned_scale( + aesthetics, palette = NULL, guide = guide, na.value = na.value, + ... + ) } #' @export #' @rdname scale_colour_continuous -scale_fill_binned <- function(..., +scale_fill_binned <- function(..., aesthetics = "fill", guide = "coloursteps", + na.value = "grey50", type = getOption("ggplot2.binned.fill")) { - args <- list2(...) - args$call <- args$call %||% current_call() - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type(exec(type, !!!args), "scale_fill_binned", "fill") - } else { - type_fallback <- getOption("ggplot2.continuous.fill", default = "gradient") - # don't use fallback from scale_colour_continuous() if it is - # a function, since that would change the type of the color - # scale from binned to continuous - if (is.function(type_fallback)) { - type_fallback <- "gradient" - } - type <- type %||% type_fallback - - if (identical(type, "gradient")) { - exec(scale_fill_steps, !!!args) - } else if (identical(type, "viridis")) { - exec(scale_fill_viridis_b, !!!args) - } else { - cli::cli_abort(c( - "Unknown scale type: {.val {type}}", - "i" = "Use either {.val gradient} or {.val viridis}." - )) - } + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., guide = guide, na.value = na.value, scale = type, + aesthetic = "fill", type = "binned" + ) + return(scale) } -} + binned_scale( + aesthetics, palette = NULL, guide = guide, na.value = na.value, + ... + ) +} # helper function to make sure that the provided scale is of the correct # type (i.e., is continuous and works with the provided aesthetic) diff --git a/R/scale-hue.R b/R/scale-hue.R index ba50f81dc2..120de34857 100644 --- a/R/scale-hue.R +++ b/R/scale-hue.R @@ -134,48 +134,36 @@ scale_fill_hue <- function(name = waiver(), ..., h = c(0, 360) + 15, c = 100, #' print(cty_by_var(fl)) #' }) #' -scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour")) { - # TODO: eventually `type` should default to a set of colour-blind safe color codes (e.g. Okabe-Ito) - type <- type %||% scale_colour_hue - args <- list2(...) - args$call <- args$call %||% current_call() - - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type( - exec(type, !!!args), - "scale_colour_discrete", - "colour", - scale_is_discrete = TRUE +scale_colour_discrete <- function(..., aesthetics = "colour", na.value = "grey50", + type = getOption("ggplot2.discrete.colour")) { + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., na.value = na.value, scale = type, + aesthetic = "colour", type = "discrete" ) - } else { - exec(scale_colour_qualitative, !!!args, type = type) + return(scale) } + discrete_scale( + aesthetics, palette = NULL, na.value = na.value, + ... + ) } #' @rdname scale_colour_discrete #' @export -scale_fill_discrete <- function(..., type = getOption("ggplot2.discrete.fill")) { - # TODO: eventually `type` should default to a set of colour-blind safe color codes (e.g. Okabe-Ito) - type <- type %||% scale_fill_hue - args <- list2(...) - args$call <- args$call %||% current_call() - - if (is.function(type)) { - if (!any(c("...", "call") %in% fn_fmls_names(type))) { - args$call <- NULL - } - check_scale_type( - exec(type, !!!args), - "scale_fill_discrete", - "fill", - scale_is_discrete = TRUE +scale_fill_discrete <- function(..., aesthetics = "fill", na.value = "grey50", + type = getOption("ggplot2.discrete.fill")) { + if (!is.null(type)) { + scale <- scale_backward_compatibility( + ..., na.value = na.value, scale = type, + aesthetic = "fill", type = "discrete" ) - } else { - exec(scale_fill_qualitative, !!!args, type = type) + return(scale) } + discrete_scale( + aesthetics, palette = NULL, na.value = na.value, + ... + ) } scale_colour_qualitative <- function(name = waiver(), ..., type = NULL, From 78775dbe06e729ac482aeda36064dc388b5d7a60 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 15:32:13 +0200 Subject: [PATCH 07/21] document --- man/scale_colour_continuous.Rd | 38 +++++++++++++++++++++++++++------- man/scale_colour_discrete.Rd | 14 +++++++++++-- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/man/scale_colour_continuous.Rd b/man/scale_colour_continuous.Rd index 36f3427746..2691e31f32 100644 --- a/man/scale_colour_continuous.Rd +++ b/man/scale_colour_continuous.Rd @@ -9,13 +9,37 @@ \alias{scale_color_binned} \title{Continuous and binned colour scales} \usage{ -scale_colour_continuous(..., type = getOption("ggplot2.continuous.colour")) - -scale_fill_continuous(..., type = getOption("ggplot2.continuous.fill")) - -scale_colour_binned(..., type = getOption("ggplot2.binned.colour")) - -scale_fill_binned(..., type = getOption("ggplot2.binned.fill")) +scale_colour_continuous( + ..., + aesthetics = "colour", + guide = "colourbar", + na.value = "grey50", + type = getOption("ggplot2.continuous.colour") +) + +scale_fill_continuous( + ..., + aesthetics = "fill", + guide = "colourbar", + na.value = "grey50", + type = getOption("ggplot2.continuous.fill") +) + +scale_colour_binned( + ..., + aesthetics = "colour", + guide = "coloursteps", + na.value = "grey50", + type = getOption("ggplot2.binned.colour") +) + +scale_fill_binned( + ..., + aesthetics = "fill", + guide = "coloursteps", + na.value = "grey50", + type = getOption("ggplot2.binned.fill") +) } \arguments{ \item{...}{Additional parameters passed on to the scale type} diff --git a/man/scale_colour_discrete.Rd b/man/scale_colour_discrete.Rd index 0c7883fb6e..ce5de60730 100644 --- a/man/scale_colour_discrete.Rd +++ b/man/scale_colour_discrete.Rd @@ -6,9 +6,19 @@ \alias{scale_color_discrete} \title{Discrete colour scales} \usage{ -scale_colour_discrete(..., type = getOption("ggplot2.discrete.colour")) +scale_colour_discrete( + ..., + aesthetics = "colour", + na.value = "grey50", + type = getOption("ggplot2.discrete.colour") +) -scale_fill_discrete(..., type = getOption("ggplot2.discrete.fill")) +scale_fill_discrete( + ..., + aesthetics = "fill", + na.value = "grey50", + type = getOption("ggplot2.discrete.fill") +) } \arguments{ \item{...}{Additional parameters passed on to the scale type,} From 9a202c7d8a1150c2c92259990d017c282837a4ae Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 15:46:49 +0200 Subject: [PATCH 08/21] properly document this time --- R/scale-colour.R | 1 + R/scale-hue.R | 1 + man/scale_colour_continuous.Rd | 7 +++++++ man/scale_colour_discrete.Rd | 6 ++++++ 4 files changed, 15 insertions(+) diff --git a/R/scale-colour.R b/R/scale-colour.R index 0b31c2f2fa..1953fdd538 100644 --- a/R/scale-colour.R +++ b/R/scale-colour.R @@ -27,6 +27,7 @@ #' you want to manually set the colors of a scale, consider using #' [scale_colour_gradient()] or [scale_colour_steps()]. #' +#' @inheritParams continuous_scale #' @param ... Additional parameters passed on to the scale type #' @param type One of the following: #' * "gradient" (the default) diff --git a/R/scale-hue.R b/R/scale-hue.R index 120de34857..43766221c5 100644 --- a/R/scale-hue.R +++ b/R/scale-hue.R @@ -86,6 +86,7 @@ scale_fill_hue <- function(name = waiver(), ..., h = c(0, 360) + 15, c = 100, #' is specified. #' #' @param ... Additional parameters passed on to the scale type, +#' @inheritParams discrete_scale #' @param type One of the following: #' * A character vector of color codes. The codes are used for a 'manual' color #' scale as long as the number of codes exceeds the number of data levels diff --git a/man/scale_colour_continuous.Rd b/man/scale_colour_continuous.Rd index 2691e31f32..d88a74f399 100644 --- a/man/scale_colour_continuous.Rd +++ b/man/scale_colour_continuous.Rd @@ -44,6 +44,13 @@ scale_fill_binned( \arguments{ \item{...}{Additional parameters passed on to the scale type} +\item{aesthetics}{The names of the aesthetics that this scale works with.} + +\item{guide}{A function used to create a guide or its name. See +\code{\link[=guides]{guides()}} for more information.} + +\item{na.value}{Missing values will be replaced with this value.} + \item{type}{One of the following: \itemize{ \item "gradient" (the default) diff --git a/man/scale_colour_discrete.Rd b/man/scale_colour_discrete.Rd index ce5de60730..ff8fe3f9e7 100644 --- a/man/scale_colour_discrete.Rd +++ b/man/scale_colour_discrete.Rd @@ -23,6 +23,12 @@ scale_fill_discrete( \arguments{ \item{...}{Additional parameters passed on to the scale type,} +\item{aesthetics}{The names of the aesthetics that this scale works with.} + +\item{na.value}{If \code{na.translate = TRUE}, what aesthetic value should the +missing values be displayed as? Does not apply to position scales +where \code{NA} is always placed at the far right.} + \item{type}{One of the following: \itemize{ \item A character vector of color codes. The codes are used for a 'manual' color From aa42bc9c72805f18449c64fd72556585f484e852 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Wed, 19 Jun 2024 16:12:09 +0200 Subject: [PATCH 09/21] misc fixes --- R/geom-text.R | 2 +- R/scale-colour.R | 6 ++++++ man/geom_text.Rd | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/geom-text.R b/R/geom-text.R index d0f33a12ff..4f81f43741 100644 --- a/R/geom-text.R +++ b/R/geom-text.R @@ -95,7 +95,7 @@ #' # Add aesthetic mappings #' p + geom_text(aes(colour = factor(cyl))) #' p + geom_text(aes(colour = factor(cyl))) + -#' scale_colour_discrete(l = 40) +#' scale_colour_hue(l = 40) #' p + geom_label(aes(fill = factor(cyl)), colour = "white", fontface = "bold") #' #' p + geom_text(aes(size = wt)) diff --git a/R/scale-colour.R b/R/scale-colour.R index 1953fdd538..144cedef72 100644 --- a/R/scale-colour.R +++ b/R/scale-colour.R @@ -209,6 +209,12 @@ scale_backward_compatibility <- function(..., scale, aesthetic, type) { if (scale == "discrete") { scale <- "hue" } + if (scale == "viridis") { + scale <- switch( + type, discrete = "viridis_d", binned = "viridis_b", "viridis_c" + ) + } + candidates <- paste("scale", aesthetic, scale, sep = "_") for (candi in candidates) { f <- find_global(candi, env = caller_env(), mode = "function") diff --git a/man/geom_text.Rd b/man/geom_text.Rd index 9c64a258d5..9a5b451108 100644 --- a/man/geom_text.Rd +++ b/man/geom_text.Rd @@ -244,7 +244,7 @@ p + # Add aesthetic mappings p + geom_text(aes(colour = factor(cyl))) p + geom_text(aes(colour = factor(cyl))) + - scale_colour_discrete(l = 40) + scale_colour_hue(l = 40) p + geom_label(aes(fill = factor(cyl)), colour = "white", fontface = "bold") p + geom_text(aes(size = wt)) From 47adf690ef207169137c9ac6f34ba18940a29d78 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 14:03:33 +0200 Subject: [PATCH 10/21] more palettes in theme --- R/theme-elements.R | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/R/theme-elements.R b/R/theme-elements.R index ed69c82a57..930e17eb74 100644 --- a/R/theme-elements.R +++ b/R/theme-elements.R @@ -608,10 +608,20 @@ el_def <- function(class = NULL, inherit = NULL, description = NULL) { plot.tag.location = el_def("character"), plot.margin = el_def(c("margin", "rel"), "margins"), - palette.colour.discrete = el_def(c("character", "function")), - palette.fill.discrete = el_def(c("character", "function"), "palette.colour.discrete"), + palette.colour.discrete = el_def(c("character", "function")), palette.colour.continuous = el_def(c("character", "function")), + palette.fill.discrete = el_def(c("character", "function"), "palette.colour.discrete"), palette.fill.continuous = el_def(c("character", "function"), "palette.colour.continuous"), + palette.alpha.discrete = el_def(c("character", "numeric", "integer", "function")), + palette.alpha.continuous = el_def(c("character", "numeric", "integer", "function")), + palette.linewidth.discrete = el_def(c("character", "numeric", "integer", "function")), + palette.linewidth.continuous = el_def(c("character", "numeric", "integer", "function")), + palette.size.discrete = el_def(c("character", "numeric", "integer", "function")), + palette.size.continuous = el_def(c("character", "numeric", "integer", "function")), + palette.shape.discrete = el_def(c("character", "numeric", "integer", "function")), + palette.shape.continuous = el_def(c("character", "numeric", "integer", "function")), + palette.linetype.discrete = el_def(c("character", "numeric", "integer", "function")), + palette.linetype.continuous = el_def(c("character", "numeric", "integer", "function")), aspect.ratio = el_def(c("numeric", "integer")) ) From e2b552137b8b822c27c5293b4c249481674eb398 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 15:08:58 +0200 Subject: [PATCH 11/21] use binned versions of discrete palettes for continuous linetype/shape --- R/utilities.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/utilities.R b/R/utilities.R index 7b46f52d34..ee4662e4ba 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -838,6 +838,8 @@ fallback_palette <- function(aes, discrete) { colour = , fill = pal_seq_gradient("#132B43", "#56B1F7"), alpha = pal_rescale(c(0.1, 1)), linewidth = pal_rescale(c(1, 6)), + linetype = pal_binned(pal_linetype()), + shape = pal_binned(pal_shape()), size = pal_area(), NULL ) From ddef5d3a001f33da270af298c5aa42dba6dc9ebf Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 15:28:42 +0200 Subject: [PATCH 12/21] Set default palettes to `NULL` --- R/scale-alpha.R | 35 +++++++++++++++++++---------------- R/scale-linetype.R | 4 ++-- R/scale-linewidth.R | 35 +++++++++++++++++++---------------- R/scale-shape.R | 8 +++++--- R/scale-size.R | 38 +++++++++++++++++++------------------- 5 files changed, 64 insertions(+), 56 deletions(-) diff --git a/R/scale-alpha.R b/R/scale-alpha.R index 53344f23be..5e22937e88 100644 --- a/R/scale-alpha.R +++ b/R/scale-alpha.R @@ -31,8 +31,9 @@ #' #' # Changing the title #' p + scale_alpha("cylinders") -scale_alpha <- function(name = waiver(), ..., range = c(0.1, 1)) { - continuous_scale("alpha", name = name, palette = pal_rescale(range), ...) +scale_alpha <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_rescale(range) else NULL + continuous_scale("alpha", name = name, palette = palette, ...) } #' @rdname scale_alpha @@ -41,8 +42,9 @@ scale_alpha_continuous <- scale_alpha #' @rdname scale_alpha #' @export -scale_alpha_binned <- function(name = waiver(), ..., range = c(0.1, 1)) { - binned_scale("alpha", name = name, palette = pal_rescale(range), ...) +scale_alpha_binned <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_rescale(range) else NULL + binned_scale("alpha", name = name, palette = palette, ...) } #' @rdname scale_alpha @@ -56,32 +58,33 @@ scale_alpha_discrete <- function(...) { #' @rdname scale_alpha #' @export -scale_alpha_ordinal <- function(name = waiver(), ..., range = c(0.1, 1)) { - discrete_scale( - "alpha", name = name, - palette = function(n) seq(range[1], range[2], length.out = n), - ... - ) +scale_alpha_ordinal <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) { + function(n) seq(range[1], range[2], length.out = n) + } else { + NULL + } + discrete_scale("alpha", name = name, palette = palette, ...) } #' @rdname scale_alpha #' @export #' @usage NULL -scale_alpha_datetime <- function(name = waiver(), ..., range = c(0.1, 1)) { +scale_alpha_datetime <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_rescale(range) else NULL datetime_scale( aesthetics = "alpha", transform = "time", name = name, - palette = pal_rescale(range), - ... + palette = palette, ... ) } #' @rdname scale_alpha #' @export #' @usage NULL -scale_alpha_date <- function(name = waiver(), ..., range = c(0.1, 1)){ +scale_alpha_date <- function(name = waiver(), ..., range = NULL){ + palette <- if (!is.null(range)) pal_rescale(range) else NULL datetime_scale( aesthetics = "alpha", transform = "date", name = name, - palette = pal_rescale(range), - ... + palette = palette, ... ) } diff --git a/R/scale-linetype.R b/R/scale-linetype.R index a1b983b23d..ec83a0124c 100644 --- a/R/scale-linetype.R +++ b/R/scale-linetype.R @@ -38,7 +38,7 @@ scale_linetype <- function(name = waiver(), ..., na.value = "blank") { discrete_scale( "linetype", name = name, - palette = pal_linetype(), + palette = NULL, na.value = na.value, ... ) @@ -49,7 +49,7 @@ scale_linetype <- function(name = waiver(), ..., na.value = "blank") { scale_linetype_binned <- function(name = waiver(), ..., na.value = "blank") { binned_scale( "linetype", name = name, - palette = pal_binned(pal_linetype()), + palette = NULL, na.value = na.value, ... ) diff --git a/R/scale-linewidth.R b/R/scale-linewidth.R index 801df22b3a..9bf05b3913 100644 --- a/R/scale-linewidth.R +++ b/R/scale-linewidth.R @@ -31,10 +31,11 @@ NULL #' @usage NULL scale_linewidth_continuous <- function(name = waiver(), breaks = waiver(), labels = waiver(), limits = NULL, - range = c(1, 6), transform = "identity", + range = NULL, transform = "identity", trans = deprecated(), guide = "legend") { - continuous_scale("linewidth", palette = pal_rescale(range), name = name, + palette <- if (!is.null(range)) pal_rescale(range) else NULL + continuous_scale("linewidth", palette = palette, name = name, breaks = breaks, labels = labels, limits = limits, transform = transform, trans = trans, guide = guide) } @@ -46,10 +47,11 @@ scale_linewidth <- scale_linewidth_continuous #' @rdname scale_linewidth #' @export scale_linewidth_binned <- function(name = waiver(), breaks = waiver(), labels = waiver(), - limits = NULL, range = c(1, 6), n.breaks = NULL, + limits = NULL, range = NULL, n.breaks = NULL, nice.breaks = TRUE, transform = "identity", trans = deprecated(), guide = "bins") { - binned_scale("linewidth", palette = pal_rescale(range), name = name, + palette <- if (!is.null(range)) pal_rescale(range) else NULL + binned_scale("linewidth", palette = palette, name = name, breaks = breaks, labels = labels, limits = limits, transform = transform, trans = trans, n.breaks = n.breaks, nice.breaks = nice.breaks, guide = guide) @@ -68,32 +70,33 @@ scale_linewidth_discrete <- function(...) { #' @rdname scale_linewidth #' @export #' @usage NULL -scale_linewidth_ordinal <- function(name = waiver(), ..., range = c(2, 6)) { - force(range) - - discrete_scale( - "linewidth", name = name, - palette = function(n) seq(range[1], range[2], length.out = n), - ... - ) +scale_linewidth_ordinal <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) { + function(n) seq(range[1], range[2], length.out = n) + } else { + NULL + } + discrete_scale("linewidth", name = name, palette = palette, ...) } #' @rdname scale_linewidth #' @export #' @usage NULL -scale_linewidth_datetime <- function(name = waiver(), ..., range = c(1, 6)) { +scale_linewidth_datetime <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_rescale(range) else NULL datetime_scale( "linewidth", transform = "time", name = name, - palette = pal_rescale(range), ... + palette = palette, ... ) } #' @rdname scale_linewidth #' @export #' @usage NULL -scale_linewidth_date <- function(name = waiver(), ..., range = c(1, 6)) { +scale_linewidth_date <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_rescale(range) else NULL datetime_scale( "linewidth", transform = "date", name = name, - palette = pal_rescale(range), ... + palette = palette, ... ) } diff --git a/R/scale-shape.R b/R/scale-shape.R index 7c4c750519..6281fc2a6d 100644 --- a/R/scale-shape.R +++ b/R/scale-shape.R @@ -42,14 +42,16 @@ #' scale_shape_identity() + #' facet_wrap(~shape) + #' theme_void() -scale_shape <- function(name = waiver(), ..., solid = TRUE) { - discrete_scale("shape", name = name, palette = pal_shape(solid), ...) +scale_shape <- function(name = waiver(), ..., solid = NULL) { + palette <- if (!is.null(solid)) pal_shape(solid) else NULL + discrete_scale("shape", name = name, palette = palette, ...) } #' @rdname scale_shape #' @export scale_shape_binned <- function(name = waiver(), ..., solid = TRUE) { - binned_scale("shape", name = name, palette = pal_binned(pal_shape(solid)), ...) + palette <- if (!is.null(solid)) pal_binned(pal_shape(solid)) else NULL + binned_scale("shape", name = name, palette = palette, ...) } #' @rdname scale_shape diff --git a/R/scale-size.R b/R/scale-size.R index 33f14d4834..525f378e15 100644 --- a/R/scale-size.R +++ b/R/scale-size.R @@ -52,11 +52,12 @@ NULL #' @export #' @usage NULL scale_size_continuous <- function(name = waiver(), breaks = waiver(), labels = waiver(), - limits = NULL, range = c(1, 6), + limits = NULL, range = NULL, transform = "identity", trans = deprecated(), guide = "legend") { - continuous_scale("size", palette = pal_area(range), name = name, + palette <- if (!is.null(range)) pal_area(range) else NULL + continuous_scale("size", palette = palette, name = name, breaks = breaks, labels = labels, limits = limits, transform = transform, trans = trans, guide = guide) } @@ -79,10 +80,11 @@ scale_radius <- function(name = waiver(), breaks = waiver(), labels = waiver(), #' @rdname scale_size #' @export scale_size_binned <- function(name = waiver(), breaks = waiver(), labels = waiver(), - limits = NULL, range = c(1, 6), n.breaks = NULL, + limits = NULL, range = NULL, n.breaks = NULL, nice.breaks = TRUE, transform = "identity", trans = deprecated(), guide = "bins") { - binned_scale("size", palette = pal_area(range), name = name, + palette <- if (!is.null(range)) pal_area(range) else NULL + binned_scale("size", palette = palette, name = name, breaks = breaks, labels = labels, limits = limits, transform = transform, trans = trans, n.breaks = n.breaks, nice.breaks = nice.breaks, guide = guide) @@ -101,17 +103,13 @@ scale_size_discrete <- function(...) { #' @rdname scale_size #' @export #' @usage NULL -scale_size_ordinal <- function(name = waiver(), ..., range = c(2, 6)) { - force(range) - - discrete_scale( - "size", name = name, - palette = function(n) { - area <- seq(range[1] ^ 2, range[2] ^ 2, length.out = n) - sqrt(area) - }, - ... - ) +scale_size_ordinal <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) { + function(n) sqrt(seq(range[1]^2, range[2]^2, length.out = n)) + } else { + NULL + } + discrete_scale("size", name = name, palette = palette, ...) } #' @inheritDotParams continuous_scale -aesthetics -scale_name -palette -rescaler -expand -position @@ -139,13 +137,15 @@ scale_size_binned_area <- function(name = waiver(), ..., max_size = 6) { #' @rdname scale_size #' @export #' @usage NULL -scale_size_datetime <- function(name = waiver(), ..., range = c(1, 6)) { - datetime_scale("size", "time", name = name, palette = pal_area(range), ...) +scale_size_datetime <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_area(range) else NULL + datetime_scale("size", "time", name = name, palette = palette, ...) } #' @rdname scale_size #' @export #' @usage NULL -scale_size_date <- function(name = waiver(), ..., range = c(1, 6)) { - datetime_scale("size", "date", name = name, palette = pal_area(range), ...) +scale_size_date <- function(name = waiver(), ..., range = NULL) { + palette <- if (!is.null(range)) pal_area(range) else NULL + datetime_scale("size", "date", name = name, palette = palette, ...) } From 71d3122ad4712f74c6673d22bd649ded72eb3635 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 15:28:55 +0200 Subject: [PATCH 13/21] tweak test to populate palettes --- tests/testthat/test-guides.R | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testthat/test-guides.R b/tests/testthat/test-guides.R index b2811bf0c2..badffc2a48 100644 --- a/tests/testthat/test-guides.R +++ b/tests/testthat/test-guides.R @@ -164,6 +164,7 @@ test_that("guide merging for guide_legend() works as expected", { scales <- scales_list() scales$add(scale1) scales$add(scale2) + scales$set_palettes(NULL) scales <- scales$scales aesthetics <- lapply(scales, `[[`, "aesthetics") From 489d50524c9bf4f9b9fccf41e22f9872c150ddf2 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 16:43:45 +0200 Subject: [PATCH 14/21] try registered theme palettes --- R/utilities.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/utilities.R b/R/utilities.R index ee4662e4ba..0d91aa94a1 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -829,7 +829,7 @@ fallback_palette <- function(aes, discrete) { linetype = pal_linetype(), shape = pal_shape(), size = function(n) sqrt(seq(4, 36, length.out = n)), - NULL + ggplot_global$theme_default[[paste0("palette.", aes, ".discrete")]] ) return(pal) } @@ -841,6 +841,6 @@ fallback_palette <- function(aes, discrete) { linetype = pal_binned(pal_linetype()), shape = pal_binned(pal_shape()), size = pal_area(), - NULL + ggplot_global$theme_default[[paste0("palette.", aes, ".continuous")]] ) } From 94a1750c39d9eaea75885711b6c5a358cacdd7c9 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Fri, 21 Jun 2024 16:43:58 +0200 Subject: [PATCH 15/21] document --- man/scale_alpha.Rd | 8 ++++---- man/scale_linewidth.Rd | 4 ++-- man/scale_shape.Rd | 2 +- man/scale_size.Rd | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/man/scale_alpha.Rd b/man/scale_alpha.Rd index 28defef0de..6833a08002 100644 --- a/man/scale_alpha.Rd +++ b/man/scale_alpha.Rd @@ -10,15 +10,15 @@ \alias{scale_alpha_date} \title{Alpha transparency scales} \usage{ -scale_alpha(name = waiver(), ..., range = c(0.1, 1)) +scale_alpha(name = waiver(), ..., range = NULL) -scale_alpha_continuous(name = waiver(), ..., range = c(0.1, 1)) +scale_alpha_continuous(name = waiver(), ..., range = NULL) -scale_alpha_binned(name = waiver(), ..., range = c(0.1, 1)) +scale_alpha_binned(name = waiver(), ..., range = NULL) scale_alpha_discrete(...) -scale_alpha_ordinal(name = waiver(), ..., range = c(0.1, 1)) +scale_alpha_ordinal(name = waiver(), ..., range = NULL) } \arguments{ \item{name}{The name of the scale. Used as the axis or legend title. If diff --git a/man/scale_linewidth.Rd b/man/scale_linewidth.Rd index 275f860582..5c9a842da9 100644 --- a/man/scale_linewidth.Rd +++ b/man/scale_linewidth.Rd @@ -15,7 +15,7 @@ scale_linewidth( breaks = waiver(), labels = waiver(), limits = NULL, - range = c(1, 6), + range = NULL, transform = "identity", trans = deprecated(), guide = "legend" @@ -26,7 +26,7 @@ scale_linewidth_binned( breaks = waiver(), labels = waiver(), limits = NULL, - range = c(1, 6), + range = NULL, n.breaks = NULL, nice.breaks = TRUE, transform = "identity", diff --git a/man/scale_shape.Rd b/man/scale_shape.Rd index ffbb381481..ca0dd5bd6c 100644 --- a/man/scale_shape.Rd +++ b/man/scale_shape.Rd @@ -8,7 +8,7 @@ \alias{scale_shape_continuous} \title{Scales for shapes, aka glyphs} \usage{ -scale_shape(name = waiver(), ..., solid = TRUE) +scale_shape(name = waiver(), ..., solid = NULL) scale_shape_binned(name = waiver(), ..., solid = TRUE) } diff --git a/man/scale_size.Rd b/man/scale_size.Rd index 753ecfa790..2ba6a1e295 100644 --- a/man/scale_size.Rd +++ b/man/scale_size.Rd @@ -18,7 +18,7 @@ scale_size( breaks = waiver(), labels = waiver(), limits = NULL, - range = c(1, 6), + range = NULL, transform = "identity", trans = deprecated(), guide = "legend" @@ -40,7 +40,7 @@ scale_size_binned( breaks = waiver(), labels = waiver(), limits = NULL, - range = c(1, 6), + range = NULL, n.breaks = NULL, nice.breaks = TRUE, transform = "identity", From 00dd12cd0522aba0e9fcb07e6c481199f6807137 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 3 Oct 2024 09:23:14 +0200 Subject: [PATCH 16/21] remove redundant call to `plot_theme()` --- R/plot-build.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/plot-build.R b/R/plot-build.R index 7e876be040..49780ea39d 100644 --- a/R/plot-build.R +++ b/R/plot-build.R @@ -106,8 +106,7 @@ ggplot_build.ggplot <- function(plot) { # Train and map non-position scales and guides npscales <- scales$non_position_scales() if (npscales$n() > 0) { - #TODO: if #5854 gets merged, we shouldn't need `plot_theme()` here - npscales$set_palettes(plot_theme(plot)) + npscales$set_palettes(plot$theme) lapply(data, npscales$train_df) plot$guides <- plot$guides$build(npscales, plot$layers, plot$labels, data, plot$theme) data <- lapply(data, npscales$map_df) From 7251f11e92d25dd24ae87620f1f144a6a4481de9 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 3 Oct 2024 09:40:54 +0200 Subject: [PATCH 17/21] simplify `fallback_palette()` args --- R/scale-.R | 4 +--- R/scales-.R | 2 +- R/utilities.R | 4 +++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/R/scale-.R b/R/scale-.R index f20ed5948b..7dc962c74f 100644 --- a/R/scale-.R +++ b/R/scale-.R @@ -526,9 +526,7 @@ Scale <- ggproto("Scale", NULL, if (empty(df)) { return() } - if (is.null(self$palette)) { - self$palette <- fallback_palette(self$aesthetics[1], self$is_discrete()) - } + self$palette <- self$palette %||% fallback_palette(self) aesthetics <- intersect(self$aesthetics, names(df)) names(aesthetics) <- aesthetics diff --git a/R/scales-.R b/R/scales-.R index 886ed82307..f08f0b7cc7 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -199,7 +199,7 @@ ScalesList <- ggproto("ScalesList", NULL, } } - elem <- elem %||% fallback_palette(scale$aesthetics[1], scale$is_discrete()) + elem <- elem %||% fallback_palette(scale) if (!is.function(elem)) { cli::cli_warn( diff --git a/R/utilities.R b/R/utilities.R index 1508d080b2..844f51199e 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -777,7 +777,9 @@ as_unordered_factor <- function(x) { x } -fallback_palette <- function(aes, discrete) { +fallback_palette <- function(scale) { + aes <- scale$aesthetics[1] + discrete <- scale$is_discrete() if (discrete) { pal <- switch( aes, From 09012570e75a11443336c9d6734bbbe5bf07720b Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 3 Oct 2024 09:56:07 +0200 Subject: [PATCH 18/21] Put in shims for scales/#427 --- R/utilities.R | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/R/utilities.R b/R/utilities.R index 844f51199e..47c8f2a465 100644 --- a/R/utilities.R +++ b/R/utilities.R @@ -817,6 +817,8 @@ warn_dots_used <- function(env = caller_env(), call = caller_env()) { ) } +# TODO: delete shims when {scales} releases >1.3.0.9000 +# and bump {scales} version requirements # Shim for scales/#424 col_mix <- function(a, b, amount = 0.5) { input <- vec_recycle_common(a = a, b = b, amount = amount) @@ -829,10 +831,40 @@ col_mix <- function(a, b, amount = 0.5) { ) } +# Shim for scales/#427 +as_discrete_pal <- function(x, ...) { + if (is.function(x)) { + return(x) + } + pal_manual(x) +} + +# Shim for scales/#427 +as_continuous_pal <- function(x, ...) { + if (is.function(x)) { + return(x) + } + is_color <- grepl("^#(([[:xdigit:]]{2}){3,4}|([[:xdigit:]]){3,4})$", x) | + x %in% grDevices::colours() + if (all(is_color)) { + colour_ramp(x) + } else { + approxfun(seq(0, 1, length.out = length(x)), x) + } +} + +# Replace shims by actual scales function when available on_load({ - if ("col_mix" %in% getNamespaceExports("scales")) { + nse <- getNamespaceExports("scales") + if ("col_mix" %in% nse) { col_mix <- scales::col_mix } + if ("as_discrete_pal" %in% nse) { + as_discrete_pal <- scales::as_discrete_pal + } + if ("as_continuous_pal" %in% nse) { + as_continuous_pal <- scales::as_continuous_pal + } }) # TODO: Replace me if rlang/#1730 gets implemented From c0b5734753f3e286cb5ef22132b178d9a61418eb Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Thu, 3 Oct 2024 10:08:39 +0200 Subject: [PATCH 19/21] Streamline `ScalesList$set_palettes()` method --- R/scales-.R | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/R/scales-.R b/R/scales-.R index f08f0b7cc7..13115e0a8c 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -175,38 +175,31 @@ ScalesList <- ggproto("ScalesList", NULL, if (!is.null(scale$palette)) { next } - elem <- calc_element(paste0( - "palette.", scale$aesthetics[1], ".", - if (scale$is_discrete()) "discrete" else "continuous" - ), theme) - - # TODO: ideally {scales} would have some sort of `as_palette()` function - # String might be a name for a palette function - if (is_bare_string(elem)) { - elem <- get0(paste0("pal_", elem), mode = "function") - if (is.function(elem)) { - elem <- elem() - } - } - if (is.atomic(elem) && !is.null(elem)) { - if (scale$is_discrete()) { - elem <- pal_manual(elem) - } else if (is.character(elem)) { - elem <- pal_gradient_n(elem) - } else { - elem <- pal_rescale(range = rep(elem, length.out = 2)) - } - } + # Resolve palette theme setting for this scale + type <- if (scale$is_discrete()) "discrete" else "continuous" + elem <- paste0("palette.", scale$aesthetics[1], ".", type) + elem <- calc_element(elem, theme) + # Resolve the palette itself elem <- elem %||% fallback_palette(scale) - - if (!is.function(elem)) { + palette <- switch( + type, + discrete = as_discrete_pal(elem), + continuous = as_continuous_pal(elem) + ) + if (!is.function(palette)) { cli::cli_warn( "Failed to find palette for {.field {scale$aesthetics[1]}} scale." ) } - scale$palette <- elem + + # Set palette to scale + # Note: while direct assignment is not ideal, we've already cloned the + # scale at the beginning of the plot build method, so it doesn't affect + # other plots + scale$palette <- palette + invisible() } } ) From d9c9147d5bd084def548bd644ae9d3feedc6ae6b Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 11 Nov 2024 11:18:18 +0100 Subject: [PATCH 20/21] try to match first non-null aesthetic --- R/scales-.R | 4 ++-- tests/testthat/test-scales.R | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/R/scales-.R b/R/scales-.R index 13115e0a8c..769613a2d8 100644 --- a/R/scales-.R +++ b/R/scales-.R @@ -178,8 +178,8 @@ ScalesList <- ggproto("ScalesList", NULL, # Resolve palette theme setting for this scale type <- if (scale$is_discrete()) "discrete" else "continuous" - elem <- paste0("palette.", scale$aesthetics[1], ".", type) - elem <- calc_element(elem, theme) + elem <- paste0("palette.", scale$aesthetics, ".", type) + elem <- compact(lapply(elem, calc_element, theme))[1][[1]] # Resolve the palette itself elem <- elem %||% fallback_palette(scale) diff --git a/tests/testthat/test-scales.R b/tests/testthat/test-scales.R index d9286b513f..514cb392a3 100644 --- a/tests/testthat/test-scales.R +++ b/tests/testthat/test-scales.R @@ -734,6 +734,30 @@ test_that("continuous scales warn about faulty `limits`", { expect_snapshot(scale_x_continuous(limits = 1:3), error = TRUE) }) +test_that("populating palettes works", { + + scl <- scales_list() + scl$add(scale_colour_discrete(aesthetics = c("colour", "fill"))) + + my_theme <- theme( + palette.colour.discrete = c("white", "black"), + palette.fill.discrete = c("red", "blue") + ) + + scl$set_palettes(my_theme) + expect_equal(scl$scales[[1]]$palette(2), c("white", "black")) + + # Scales with >1 aesthetic + scl <- scales_list() + scl$add(scale_colour_discrete(aesthetics = c("colour", "fill"))) + + my_theme$palette.colour.discrete <- NULL + + scl$set_palettes(my_theme) + expect_equal(scl$scales[[1]]$palette(2), c("red", "blue")) + +}) + test_that("discrete scales work with NAs in arbitrary positions", { # Prevents intermediate caching of palettes map <- function(x, limits) { From 75e26a8ba7215a89241605bb5ed7a9482e28fd03 Mon Sep 17 00:00:00 2001 From: Teun van den Brand Date: Mon, 2 Dec 2024 11:02:58 +0100 Subject: [PATCH 21/21] add news bullet --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index b40ea07f25..b0708ef77d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -213,6 +213,9 @@ * Added `keep.zeroes` argument to `stat_bin()` (@teunbrand, #3449) * `coord_sf()` no longer errors when dealing with empty graticules (@teunbrand, #6052) * Added `theme_transparent()` with transparent backgrounds (@topepo). +* New theme elements `palette.{aes}.discrete` and `palette.{aes}.continuous`. + Theme palettes replace palettes in scales where `palette = NULL`, which is + the new default in many scales (@teunbrand, #4696). # ggplot2 3.5.1