From b71d67d1d08574ade56c94ba76225f67d4346ea8 Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 10:02:16 -0500 Subject: [PATCH 1/6] Add ability to set default scale_[fill/colour]_discrete() via type=getOption('ggplot2.discrete.[fill/colour]') argument, closes #3826 --- R/scale-hue.r | 121 ++++++++++++++++++++++++++++++++++- R/zxx.r | 12 +--- man/scale_colour_discrete.Rd | 63 ++++++++++++++++++ man/scale_hue.Rd | 7 +- 4 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 man/scale_colour_discrete.Rd diff --git a/R/scale-hue.r b/R/scale-hue.r index b1d4903389..5ef431eee1 100644 --- a/R/scale-hue.r +++ b/R/scale-hue.r @@ -1,8 +1,7 @@ #' Evenly spaced colours for discrete data #' -#' This is the default colour scale for categorical variables. It maps each -#' level to an evenly spaced hue on the colour wheel. It does not generate -#' colour-blind safe palettes. +#' Maps each level to an evenly spaced hue on the colour wheel. +#' It does not generate colour-blind safe palettes. #' #' @param na.value Colour to use for missing values #' @inheritDotParams discrete_scale -aesthetics @@ -63,3 +62,119 @@ scale_fill_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0 discrete_scale(aesthetics, "hue", hue_pal(h, c, l, h.start, direction), na.value = na.value, ...) } + + +#' Discrete colour scales +#' +#' Colour scales for discrete data default to the values of the `ggplot2.discrete.fill` +#' and `ggplot2.discrete.colour` options. By default these scales attempt to use +#' a colour-blind safe (or a custom) palette, but if the number of levels is +#' large, they fallback to [scale_fill_hue()]/[scale_colour_hue()]. +#' +#' @param ... Additional parameters passed on to the scale type, +#' @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 +#' (if there are more levels than codes, [scale_colour_hue()]/[scale_fill_hue()] +#' are used to construct the default scale). +#' * A list of character vectors of color codes. The minimum length vector that exceeds the +#' number of data levels is chosen for the color scaling. This is useful if you +#' want to change the color palette based on the number of levels. +#' * A function that returns a discrete colour/fill scale (e.g., [scale_fill_hue()], +#' [scale_fill_brewer()], etc). +#' @export +#' @rdname +#' @examples +#' # Template function for creating densities grouped by a variable +#' cty_by_var <- function(var) { +#' ggplot(mpg, aes(cty, colour = factor({{var}}), fill = factor({{var}}))) + +#' geom_density(alpha = 0.2) +#' } +#' # The default color scale for three levels +#' cty_by_var(class) +#' +#' # Define custom palettes for when there are 1-2, 3, or 4-6 levels +#' opts <- options( +#' ggplot2.discrete.fill = list( +#' c("skyblue", "orange"), +#' RColorBrewer::brewer.pal(3, "Set2"), +#' RColorBrewer::brewer.pal(6, "Accent") +#' ) +#' ) +#' cty_by_var(year) +#' cty_by_var(drv) +#' cty_by_var(fl) +#' cty_by_var(class) +#' options(opts) +#' +scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour", getOption("ggplot2.discrete.fill"))) { + type <- type %||% okabeIto + if (is.function(type)) { + type(...) + } else { + scale_colour_qualitative(..., codes = type) + } +} + +#' @rdname scale_colour_discrete +#' @export +scale_fill_discrete <- function(..., type = getOption("ggplot2.discrete.fill", getOption("ggplot2.discrete.colour"))) { + type <- type %||% okabeIto + if (is.function(type)) { + type(...) + } else { + scale_fill_qualitative(..., codes = type) + } +} + +scale_colour_qualitative <- function(..., codes = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, + direction = 1, na.value = "grey50", aesthetics = "colour") { + discrete_scale( + aesthetics, "qualitative", qualitative_pal(codes, h, c, l, h.start, direction), + na.value = na.value, ... + ) +} + +scale_fill_qualitative <- function(..., codes = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, + direction = 1, na.value = "grey50", aesthetics = "fill") { + discrete_scale( + aesthetics, "qualitative", qualitative_pal(codes, h, c, l, h.start, direction), + na.value = na.value, ... + ) +} + +qualitative_pal <- function(codes, h, c, l, h.start, direction) { + function(n) { + if (!length(codes)) { + return(scales::hue_pal(h, c, l, h.start, direction)(n)) + } + codes_list <- if (!is.list(codes)) list(codes) else codes + if (!all(vapply(codes_list, is.character, logical(1)))) { + stop("codes must be a character vector or a list of character vectors", call. = FALSE) + } + codes_lengths <- vapply(codes_list, length, integer(1)) + # If there are more levels than color codes default to hue_pal() + if (max(codes_lengths) < n) { + return(scales::hue_pal(h, c, l, h.start, direction)(n)) + } + # Use the minimum length vector that exceeds the number of levels (n) + codes_list <- codes_list[order(codes_lengths)] + i <- 1 + while (length(codes_list[[i]]) < n) { + i <- i + 1 + } + codes_list[[i]][seq_len(n)] + } +} + +# prismatic::check_color_blindness(okabeIto) +okabeIto <- c( + "#E69F00", + "#56B4E9", + "#009E73", + "#F0E442", + "#0072B2", + "#D55E00", + "#CC79A7", + "black" +) diff --git a/R/zxx.r b/R/zxx.r index b90bd9804c..0d77544be3 100644 --- a/R/zxx.r +++ b/R/zxx.r @@ -1,10 +1,5 @@ # Default scales ------------------------------------------------------------- -#' @export -#' @rdname scale_hue -#' @usage NULL -scale_colour_discrete <- scale_colour_hue - #' @export #' @rdname scale_viridis #' @usage NULL @@ -64,11 +59,6 @@ scale_colour_date <- function(..., #' @usage NULL scale_color_date <- scale_colour_date -#' @export -#' @rdname scale_hue -#' @usage NULL -scale_fill_discrete <- scale_fill_hue - #' @export #' @rdname scale_viridis #' @usage NULL @@ -143,7 +133,7 @@ scale_color_binned <- scale_colour_binned #' @export #' @rdname scale_hue #' @usage NULL -scale_color_discrete <- scale_colour_hue +scale_color_discrete <- scale_colour_discrete #' @export #' @rdname scale_gradient diff --git a/man/scale_colour_discrete.Rd b/man/scale_colour_discrete.Rd new file mode 100644 index 0000000000..4e353ceb92 --- /dev/null +++ b/man/scale_colour_discrete.Rd @@ -0,0 +1,63 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/scale-hue.r +\name{scale_colour_discrete} +\alias{scale_colour_discrete} +\alias{scale_fill_discrete} +\title{Discrete colour scales} +\usage{ +scale_colour_discrete( + ..., + type = getOption("ggplot2.discrete.colour", getOption("ggplot2.discrete.fill")) +) + +scale_fill_discrete( + ..., + type = getOption("ggplot2.discrete.fill", getOption("ggplot2.discrete.colour")) +) +} +\arguments{ +\item{...}{Additional parameters passed on to the scale type,} + +\item{type}{One of the following: +\itemize{ +\item 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 +(if there are more levels than codes, \code{\link[=scale_colour_hue]{scale_colour_hue()}}/\code{\link[=scale_fill_hue]{scale_fill_hue()}} +are used to construct the default scale). +\item A list of character vectors of color codes. The minimum length vector that exceeds the +number of data levels is chosen for the color scaling. This is useful if you +want to change the color palette based on the number of levels. +\item A function that returns a discrete colour/fill scale (e.g., \code{\link[=scale_fill_hue]{scale_fill_hue()}}, +\code{\link[=scale_fill_brewer]{scale_fill_brewer()}}, etc). +}} +} +\description{ +Colour scales for discrete data default to the values of the \code{ggplot2.discrete.fill} +and \code{ggplot2.discrete.colour} options. By default these scales attempt to use +a colour-blind safe (or a custom) palette, but if the number of levels is +large, they fallback to \code{\link[=scale_fill_hue]{scale_fill_hue()}}/\code{\link[=scale_colour_hue]{scale_colour_hue()}}. +} +\examples{ +# Template function for creating densities grouped by a variable +cty_by_var <- function(var) { + ggplot(mpg, aes(cty, colour = factor({{var}}), fill = factor({{var}}))) + + geom_density(alpha = 0.2) +} +# The default color scale for three levels +cty_by_var(class) + +# Define custom palettes for when there are 1-2, 3, or 4-6 levels +opts <- options( + ggplot2.discrete.fill = list( + c("skyblue", "orange"), + RColorBrewer::brewer.pal(3, "Set2"), + RColorBrewer::brewer.pal(6, "Accent") + ) +) +cty_by_var(year) +cty_by_var(drv) +cty_by_var(fl) +cty_by_var(class) +options(opts) + +} diff --git a/man/scale_hue.Rd b/man/scale_hue.Rd index 42020cc375..c7a22e2865 100644 --- a/man/scale_hue.Rd +++ b/man/scale_hue.Rd @@ -3,8 +3,6 @@ \name{scale_colour_hue} \alias{scale_colour_hue} \alias{scale_fill_hue} -\alias{scale_colour_discrete} -\alias{scale_fill_discrete} \alias{scale_color_discrete} \alias{scale_color_hue} \title{Evenly spaced colours for discrete data} @@ -102,9 +100,8 @@ example, to apply colour settings to the \code{colour} and \code{fill} aesthetic same time, via \code{aesthetics = c("colour", "fill")}.} } \description{ -This is the default colour scale for categorical variables. It maps each -level to an evenly spaced hue on the colour wheel. It does not generate -colour-blind safe palettes. +Maps each level to an evenly spaced hue on the colour wheel. +It does not generate colour-blind safe palettes. } \examples{ \donttest{ From 0bc861e146abead0e92b5bda737581c712ddb804 Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 10:04:42 -0500 Subject: [PATCH 2/6] Don't change the default behavior (i.e., don't default to Okabe-Ito) --- R/scale-hue.r | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/R/scale-hue.r b/R/scale-hue.r index 5ef431eee1..44d82c924e 100644 --- a/R/scale-hue.r +++ b/R/scale-hue.r @@ -108,7 +108,6 @@ scale_fill_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0 #' options(opts) #' scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour", getOption("ggplot2.discrete.fill"))) { - type <- type %||% okabeIto if (is.function(type)) { type(...) } else { @@ -119,7 +118,6 @@ scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour #' @rdname scale_colour_discrete #' @export scale_fill_discrete <- function(..., type = getOption("ggplot2.discrete.fill", getOption("ggplot2.discrete.colour"))) { - type <- type %||% okabeIto if (is.function(type)) { type(...) } else { @@ -166,15 +164,3 @@ qualitative_pal <- function(codes, h, c, l, h.start, direction) { codes_list[[i]][seq_len(n)] } } - -# prismatic::check_color_blindness(okabeIto) -okabeIto <- c( - "#E69F00", - "#56B4E9", - "#009E73", - "#F0E442", - "#0072B2", - "#D55E00", - "#CC79A7", - "black" -) From 4dd9c846c95e66338834a803f9e8a20ce7ab66d3 Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 10:23:33 -0500 Subject: [PATCH 3/6] suggest RColorBrewer --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index 65d9ef0945..afff2e5097 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -56,6 +56,7 @@ Suggests: nlme, profvis, quantreg, + RColorBrewer, rgeos, rmarkdown, rpart, From 6bbcb99cae554bbb9b43c215305d5d733973d6fc Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 11:06:23 -0500 Subject: [PATCH 4/6] avoid the new code path if options aren't set and use the name type (instead of codes) all the way down --- R/scale-hue.r | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/R/scale-hue.r b/R/scale-hue.r index 44d82c924e..953bc45c6f 100644 --- a/R/scale-hue.r +++ b/R/scale-hue.r @@ -108,59 +108,63 @@ scale_fill_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0 #' options(opts) #' scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour", 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_colour_hue if (is.function(type)) { type(...) } else { - scale_colour_qualitative(..., codes = type) + scale_colour_qualitative(..., type = type) } } #' @rdname scale_colour_discrete #' @export scale_fill_discrete <- function(..., type = getOption("ggplot2.discrete.fill", 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_fill_hue if (is.function(type)) { type(...) } else { - scale_fill_qualitative(..., codes = type) + scale_fill_qualitative(..., type = type) } } -scale_colour_qualitative <- function(..., codes = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, +scale_colour_qualitative <- function(..., type = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "colour") { discrete_scale( - aesthetics, "qualitative", qualitative_pal(codes, h, c, l, h.start, direction), + aesthetics, "qualitative", qualitative_pal(type, h, c, l, h.start, direction), na.value = na.value, ... ) } -scale_fill_qualitative <- function(..., codes = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, +scale_fill_qualitative <- function(..., type = NULL, h = c(0, 360) + 15, c = 100, l = 65, h.start = 0, direction = 1, na.value = "grey50", aesthetics = "fill") { discrete_scale( - aesthetics, "qualitative", qualitative_pal(codes, h, c, l, h.start, direction), + aesthetics, "qualitative", qualitative_pal(type, h, c, l, h.start, direction), na.value = na.value, ... ) } -qualitative_pal <- function(codes, h, c, l, h.start, direction) { +#' Given set(s) of colour codes (i.e., type), find the smallest set that can support n levels +#' @param type a character vector or a list of character vectors +#' @noRd +qualitative_pal <- function(type, h, c, l, h.start, direction) { function(n) { - if (!length(codes)) { - return(scales::hue_pal(h, c, l, h.start, direction)(n)) - } - codes_list <- if (!is.list(codes)) list(codes) else codes - if (!all(vapply(codes_list, is.character, logical(1)))) { - stop("codes must be a character vector or a list of character vectors", call. = FALSE) + type_list <- if (!is.list(type)) list(type) else type + if (!all(vapply(type_list, is.character, logical(1)))) { + abort("`type` must be a character vector or a list of character vectors", call. = FALSE) } - codes_lengths <- vapply(codes_list, length, integer(1)) + type_lengths <- vapply(type_list, length, integer(1)) # If there are more levels than color codes default to hue_pal() - if (max(codes_lengths) < n) { + if (max(type_lengths) < n) { return(scales::hue_pal(h, c, l, h.start, direction)(n)) } # Use the minimum length vector that exceeds the number of levels (n) - codes_list <- codes_list[order(codes_lengths)] + type_list <- type_list[order(type_lengths)] i <- 1 - while (length(codes_list[[i]]) < n) { + while (length(type_list[[i]]) < n) { i <- i + 1 } - codes_list[[i]][seq_len(n)] + type_list[[i]][seq_len(n)] } } From 3a030f3a85ed173817257f3ef53c22d08442466f Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 11:54:09 -0500 Subject: [PATCH 5/6] Tidy up documentation --- R/scale-hue.r | 43 ++++++++++++++++++++++-------------- man/scale_colour_discrete.Rd | 43 ++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/R/scale-hue.r b/R/scale-hue.r index 953bc45c6f..15228521f8 100644 --- a/R/scale-hue.r +++ b/R/scale-hue.r @@ -66,10 +66,9 @@ scale_fill_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0 #' Discrete colour scales #' -#' Colour scales for discrete data default to the values of the `ggplot2.discrete.fill` -#' and `ggplot2.discrete.colour` options. By default these scales attempt to use -#' a colour-blind safe (or a custom) palette, but if the number of levels is -#' large, they fallback to [scale_fill_hue()]/[scale_colour_hue()]. +#' The default discrete colour scale. Defaults to [scale_fill_hue()]/[scale_fill_brewer()] +#' unless `type` (which defaults to the `ggplot2.discrete.fill`/`ggplot2.discrete.colour` options) +#' is specified. #' #' @param ... Additional parameters passed on to the scale type, #' @param type One of the following: @@ -90,22 +89,32 @@ scale_fill_hue <- function(..., h = c(0, 360) + 15, c = 100, l = 65, h.start = 0 #' ggplot(mpg, aes(cty, colour = factor({{var}}), fill = factor({{var}}))) + #' geom_density(alpha = 0.2) #' } -#' # The default color scale for three levels +#' +#' # The default, scale_fill_hue(), is not colour-blind safe #' cty_by_var(class) #' -#' # Define custom palettes for when there are 1-2, 3, or 4-6 levels -#' opts <- options( -#' ggplot2.discrete.fill = list( -#' c("skyblue", "orange"), -#' RColorBrewer::brewer.pal(3, "Set2"), -#' RColorBrewer::brewer.pal(6, "Accent") -#' ) +#' # (Temporarily) set the default to Okabe-Ito (which is colour-blind safe) +#' okabe <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") +#' withr::with_options( +#' list(ggplot2.discrete.fill = okabe), +#' print(cty_by_var(class)) #' ) -#' cty_by_var(year) -#' cty_by_var(drv) -#' cty_by_var(fl) -#' cty_by_var(class) -#' options(opts) +#' +#' # Define a collection of palettes to alter the default based on number of levels to encode +#' discrete_palettes <- list( +#' c("skyblue", "orange"), +#' RColorBrewer::brewer.pal(3, "Set2"), +#' RColorBrewer::brewer.pal(6, "Accent") +#' ) +#' withr::with_options( +#' list(ggplot2.discrete.fill = discrete_palettes), { +#' # 1st palette is used when there 1-2 levels (e.g., year) +#' print(cty_by_var(year)) +#' # 2nd palette is used when there are 3 levels +#' print(cty_by_var(drv)) +#' # 3rd palette is used when there are 4-6 levels +#' print(cty_by_var(fl)) +#' }) #' scale_colour_discrete <- function(..., type = getOption("ggplot2.discrete.colour", getOption("ggplot2.discrete.fill"))) { # TODO: eventually `type` should default to a set of colour-blind safe color codes (e.g. Okabe-Ito) diff --git a/man/scale_colour_discrete.Rd b/man/scale_colour_discrete.Rd index 4e353ceb92..abcc712239 100644 --- a/man/scale_colour_discrete.Rd +++ b/man/scale_colour_discrete.Rd @@ -32,10 +32,9 @@ want to change the color palette based on the number of levels. }} } \description{ -Colour scales for discrete data default to the values of the \code{ggplot2.discrete.fill} -and \code{ggplot2.discrete.colour} options. By default these scales attempt to use -a colour-blind safe (or a custom) palette, but if the number of levels is -large, they fallback to \code{\link[=scale_fill_hue]{scale_fill_hue()}}/\code{\link[=scale_colour_hue]{scale_colour_hue()}}. +The default discrete colour scale. Defaults to \code{\link[=scale_fill_hue]{scale_fill_hue()}}/\code{\link[=scale_fill_brewer]{scale_fill_brewer()}} +unless \code{type} (which defaults to the \code{ggplot2.discrete.fill}/\code{ggplot2.discrete.colour} options) +is specified. } \examples{ # Template function for creating densities grouped by a variable @@ -43,21 +42,31 @@ cty_by_var <- function(var) { ggplot(mpg, aes(cty, colour = factor({{var}}), fill = factor({{var}}))) + geom_density(alpha = 0.2) } -# The default color scale for three levels + +# The default, scale_fill_hue(), is not colour-blind safe cty_by_var(class) -# Define custom palettes for when there are 1-2, 3, or 4-6 levels -opts <- options( - ggplot2.discrete.fill = list( - c("skyblue", "orange"), - RColorBrewer::brewer.pal(3, "Set2"), - RColorBrewer::brewer.pal(6, "Accent") - ) +# (Temporarily) set the default to Okabe-Ito (which is colour-blind safe) +okabe <- c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7") +withr::with_options( + list(ggplot2.discrete.fill = okabe), + print(cty_by_var(class)) ) -cty_by_var(year) -cty_by_var(drv) -cty_by_var(fl) -cty_by_var(class) -options(opts) + +# Define a collection of palettes to alter the default based on number of levels to encode +discrete_palettes <- list( + c("skyblue", "orange"), + RColorBrewer::brewer.pal(3, "Set2"), + RColorBrewer::brewer.pal(6, "Accent") +) +withr::with_options( + list(ggplot2.discrete.fill = discrete_palettes), { + # 1st palette is used when there 1-2 levels (e.g., year) + print(cty_by_var(year)) + # 2nd palette is used when there are 3 levels + print(cty_by_var(drv)) + # 3rd palette is used when there are 4-6 levels + print(cty_by_var(fl)) +}) } From fa89c5dd39328b337ee7d53a97bec23e8d7c269f Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 18 May 2020 13:14:47 -0500 Subject: [PATCH 6/6] add unit tests --- tests/testthat/test-scale-discrete.R | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/testthat/test-scale-discrete.R b/tests/testthat/test-scale-discrete.R index 35e0c17b64..0fed8f795c 100644 --- a/tests/testthat/test-scale-discrete.R +++ b/tests/testthat/test-scale-discrete.R @@ -87,3 +87,45 @@ test_that("discrete non-position scales can accept functional limits", { scale$train(c("a", "b", "c")) expect_identical(scale$get_limits(), c("c", "b", "a")) }) + + +test_that("discrete scale defaults can be set globally", { + df <- data_frame( + x = 1:4, y = 1:4, + two = c("a", "b", "a", "b"), + four = c("a", "b", "c", "d") + ) + + withr::with_options( + list(ggplot2.discrete.fill = c("#FFFFFF", "#000000")), { + # nlevels == ncodes + two <- ggplot(df, aes(x, y, colour = two, fill = two)) + geom_point() + expect_equal(layer_data(two)$colour, rep(c("#FFFFFF", "#000000"), 2)) + expect_equal(layer_data(two)$fill, rep(c("#FFFFFF", "#000000"), 2)) + + # nlevels > ncodes (so should fallback to scale_fill_hue()) + four_default <- ggplot(df, aes(x, y, colour = four, fill = four)) + + geom_point() + four_hue <- four_default + scale_fill_hue() + expect_equal(layer_data(four_default)$colour, layer_data(four_hue)$colour) + }) + + withr::with_options( + list( + ggplot2.discrete.fill = list( + c("#FFFFFF", "#000000"), + c("#FF0000", "#00FF00", "#0000FF", "#FF00FF") + ) + ), { + # nlevels == 2 + two <- ggplot(df, aes(x, y, colour = two, fill = two)) + geom_point() + expect_equal(layer_data(two)$colour, rep(c("#FFFFFF", "#000000"), 2)) + expect_equal(layer_data(two)$fill, rep(c("#FFFFFF", "#000000"), 2)) + + # nlevels == 4 + four <- ggplot(df, aes(x, y, colour = four, fill = four)) + geom_point() + expect_equal(layer_data(four)$colour, c("#FF0000", "#00FF00", "#0000FF", "#FF00FF")) + expect_equal(layer_data(four)$fill, c("#FF0000", "#00FF00", "#0000FF", "#FF00FF")) + }) + +})