Skip to content

Commit

Permalink
codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
CoryMcCartan committed Feb 23, 2021
1 parent fe459f8 commit 18b51c0
Show file tree
Hide file tree
Showing 24 changed files with 536 additions and 27 deletions.
4 changes: 4 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Description: Color palettes taken from the landscapes and cities of Washington
confusion than others.
Depends:
R (>= 3.0)
Imports:
graphics,
grDevices,
ggplot2
Suggests:
knitr,
rmarkdown,
Expand Down
15 changes: 15 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@

S3method(plot,palette)
S3method(print,palette)
export(pal_functions)
export(pal_vector)
export(palette_html)
export(scale_color_wa_b)
export(scale_color_wa_c)
export(scale_color_wa_d)
export(scale_colour_wa_b)
export(scale_colour_wa_c)
export(scale_colour_wa_d)
export(scale_fill_wa_b)
export(scale_fill_wa_c)
export(scale_fill_wa_d)
export(wa_pal)
export(wacolors)
importFrom(ggplot2,discrete_scale)
importFrom(ggplot2,scale_color_gradientn)
importFrom(grDevices,colorRampPalette)
importFrom(grDevices,rgb)
importFrom(graphics,image)
importFrom(graphics,par)
importFrom(graphics,rect)
Expand Down
88 changes: 88 additions & 0 deletions R/codegen.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#' Output a character vector containing code for a palette
#'
#' Call this function to get the code for a character vector containing a
#' palette. If using RStudio, the code will be loaded at the console prompt;
#' otherwise, it will be printed at the terminal.
#'
#' @inheritParams wa_pal
#'
#' @examples
#' pal_vector("rainier", 4)
#'
#' @export
pal_vector = function(palette, n, which=NULL,
type=c("discrete", "continuous"), reverse=FALSE) {
type = match.arg(type)
pal = wa_pal(palette, n, which, type, reverse)
varname = paste0("PAL_", toupper(attr(pal, "name")))
code = paste0(varname, " = c(", paste0('"', pal, '"', collapse=", "), ")")
code = paste0(strwrap(code, 76, indent=0, exdent=nchar(varname) + 5),
collapse="\n")
code_output(code)
}

#' Output a character vector containing code for a `ggplot2` scale
#'
#' Call this function to get the code for the `scale_*` functions for a palette.
#' If using RStudio, the code will be loaded at the console prompt;
#' otherwise, it will be printed at the terminal. Assumes that `ggplot2` has
#' been loaded into the namespace, or will be by the time the scales are used.
#'
#' @param palette a `[wacolors]` palette or palette name.
#' @param which if not `NULL`, the indices or names of a subset of colors to use.
#' @param ... Other arguments passed on to [ggplot2::discrete_scale()],
#' [ggplot2::continuous_scale()], or [ggplot2::binned_scale()] to control
#' name, limits, breaks, labels and so forth.
#' @param reverse `TRUE` if the colors should be reversed.
#'
#' @examples
#' pal_functions("rainier")
#'
#' @export
pal_functions = function(palette, which=NULL, type=c("discrete", "continuous"),
reverse=FALSE) {
pal = match_pal(palette)
if (!is.null(which)) pal$pal = pal$pal[which]
if (reverse) pal$pal = rev(pal$pal)
names(pal$pal) = NULL

make_discr = function(aesthetic) {
pal_col_code = paste0(" pal_cols = c(", paste0('"', pal$pal, '"', collapse=", "), ")")
pal_col_code = paste0(strwrap(pal_col_code, 76, indent=2, exdent=15), collapse="\n")
pal_col_code = paste0(pal_col_code, "\n", " n_col = length(pal_cols)\n")
pal_fun_code = " ramp = grDevices::colorRampPalette(pal_cols)\n"
if (!(pal$name %in% cont_pal)) {
pal_fun_code = paste0(pal_fun_code, " pal_fun = function(n) ",
"if (n <= n_col) pal_cols[1:n] else ramp(n)\n")
} else {
pal_fun_code = paste0(pal_fun_code, " pal_fun = ramp\n")
}
pal_gen_code = paste0(' discrete_scale("', aesthetic, '", "',
pal$name, '", palette=pal_fun, ...)\n')

fname = paste0("scale_", aesthetic, "_", pal$name, "_d")
paste0(fname, " = function(...) {\n", pal_col_code,
pal_fun_code, pal_gen_code, "}\n")
}

make_cont = function(aesthetic) {
pal_cols = paste0("c(", paste0('"', pal$pal, '"', collapse=", "), ")")
pal_gen_code = paste0("scale_", aesthetic, "_gradientn(..., colours=",
pal_cols, ")")
pal_gen_code = paste0(strwrap(pal_gen_code, 76, indent=2,
exdent=23 + (aesthetic=="color")), collapse="\n")
fname = paste0("scale_", aesthetic, "_", pal$name, "_c")
paste0(fname, " = function(...) {\n", pal_gen_code, "\n}\n")
}

code = ""
if ("discrete" %in% type) {
code = paste0(code, make_discr("color"), make_discr("fill"), "\n")
}
if ("continuous" %in% type) {
code = paste0(code, make_cont("color"), make_cont("fill"), "\n")
}

code_output(code)
}

27 changes: 17 additions & 10 deletions R/colors.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
#' @export
wacolors = list(
rainier = c(ground="#6D9537", lake="#364B6F", rock="#A1A2B7", trees="#2A4E45",
lodge="#7F4E28", fall_bush="#E59A20", glacier="#DEE5F0"),
lodge="#7F4E28", fall_bush="#E59A20", glacier="#D2D8E8"),
rainier_meadow = c(ground="#6D9337", ragwort="#EECE12", paintbrush="#DE327D",
penstemon="#7754ED", western_anemone="#DCD5A8",
trees="#2A4B41", bog_orchid="#C1B062"),
larch = c(larch="#DEA141", shrub="#313D23", rock="#8698A4",
moss="#7F6A3D", dirt="#C9AB85", forest="#718C7D"),
forest = c(shadows="#183314", trees="#719133", stream="#0D2633", dirt="#553727"),
forest = c(shadows="#183314", trees="#719133", stream="#0D2633", dirt="#5a3f2f"),
understory = c(shadows="#183314", trees="#425426",
moss="#60652B", tips="#719133"),
seattle_night = c(columbia="#1A243C", dock_lights="#CA893D", ferry="#7393C1",
seafirst="#0B0912", pike_clock="#F1383C", sky="#0C3E84",
smith="#C3B497", puddle="#372937"),
seattle_fall = c("#756211", "#C7AA1F", "#C0664A", "#69798B", "#709FC3"),
seattle_fall = c("#5C5F0A", "#C7AA1F", "#C0664A", "#69798B", "#709FC3"),
pike_place = c(neon="#F85051", lemons="#F3B866",
broccoli="#698423", seafood_sign="#2480F0", floor="#725334"),
fort_worden = c(sea="#617493", shrub="#313E18", lighthouse="#A7B5C9",
Expand Down Expand Up @@ -65,17 +65,21 @@ wacolors = list(
red="#800000", medblue="#014098")
)

cont_pal = c("sound_sunset", "understory", "winter_mountain",
"ferries", "volcano", "locks", "plane_view")

#' Washington State Color Palette Generator
#'
#' Generate `palette` objects from the `wacolors` list
#'
#' @param name The name of the palette (partial matching supported), or an
#' @param palette The name of the palette (partial matching supported), or an
#' actual palette from `[wacolors]`.
#' @param which if not `NULL`, the indices or names of a subset of colors to use.
#' @param n The number of colors in the palette. If this exceeds the actual
#' number and `type` is not provided, it will be set to `continuous`.
#' @param type Either `continuous` or `discrete`. Use `continuous` if you want
#' to automatically interpolate between colors.
#' @param rev `TRUE` if palette should be reversed.
#' @param reverse `TRUE` if palette should be reversed.
#'
#' @return A vector of colors of type `palette`
#'
Expand All @@ -85,28 +89,31 @@ wacolors = list(
#' wa_pal("sound_sunset", 20, "continuous")
#' wa_pal("washington_pass", reverse=TRUE)
#'
#' @importFrom grDevices colorRampPalette
#' @export
wa_pal = function(name, n, type=c("discrete", "continuous"), rev=FALSE) {
obj = match_pal(name)
wa_pal = function(palette, n, which=NULL,
type=c("discrete", "continuous"), reverse=FALSE) {
obj = match_pal(palette)
pal = obj$pal
name = obj$name
if (!is.null(which)) pal = pal[which]

if (is.null(pal))
stop("Palette `", name, "` not found.")

if (missing(n))
n = length(pal)

if (n > length(pal) && missing(type))
if (n > length(pal))
type="continuous"
type = match.arg(type)

if (type == "discrete")
out = pal[1:n]
else
out = grDevices::colorRampPalette(pal)(n)
out = colorRampPalette(pal)(n)

if (rev) out = base::rev(out)
if (reverse) out = rev(out)

structure(out, class="palette", name=name)
}
117 changes: 117 additions & 0 deletions R/scales.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#' Helper function-palette for discrete scales
#' @importFrom grDevices colorRampPalette rgb
discr_pal = function(pal, reverse=FALSE) {
n_col = length(pal)
names(pal) = NULL
ramp = colorRampPalette(pal)
function(n) {
if (n <= n_col)
pal[1:n]
else
ramp(n)
}
}

#' Color palettes for `ggplot2`
#'
#' @rdname scale_wa
#'
#' @param palette a `[wacolors]` palette or palette name.
#' @param which if not `NULL`, the indices or names of a subset of colors to use.
#' @param ... Other arguments passed on to [ggplot2::discrete_scale()],
#' [ggplot2::continuous_scale()], or [ggplot2::binned_scale()] to control
#' name, limits, breaks, labels and so forth.
#' @param reverse `TRUE` if the colors should be reversed.
#'
#' @examples
#' ggplot(mtcars, aes(mpg, wt)) +
#' geom_point(aes(color = factor(cyl), size=hp)) +
#' scale_color_wa_d()
#'
#' ggplot(mtcars, aes(mpg, wt)) +
#' geom_point(aes(color = hp)) +
#' scale_color_wa_c("seattle_night", which=c("seafirst", "pike_clock"))
#'
#' ggplot(diamonds) +
#' geom_bar(aes(x = cut, fill = clarity)) +
#' scale_fill_wa_d(wacolors$sound_sunset, reverse=TRUE)
#'
#' @importFrom ggplot2 discrete_scale scale_color_gradientn
#' @export
scale_color_wa_d = function(palette="rainier", which=NULL, ..., reverse=FALSE) {
pal = match_pal(palette)
if (!is.null(which)) pal$pal = pal$pal[which]
if (reverse) pal$pal = rev(pal$pal)

if (pal$name %in% cont_pal)
pal_fun = colorRampPalette(pal$pal)
else
pal_fun = discr_pal(pal$pal)

discrete_scale("colour", pal$name, palette=pal_fun, ...)
}

#' @rdname scale_wa
#' @export
scale_fill_wa_d = function(palette="rainier", which=NULL, ..., reverse=FALSE) {
pal = match_pal(palette)
if (!is.null(which)) pal$pal = pal$pal[which]
if (reverse) pal$pal = rev(pal$pal)

if (pal$name %in% cont_pal)
pal_fun = colorRampPalette(pal$pal)
else
pal_fun = discr_pal(pal$pal)

discrete_scale("fill", pal$name, palette=pal_fun, ...)
}

#' @rdname scale_wa
#' @export
scale_color_wa_c = function(palette="ferries", which=NULL, ..., reverse=FALSE) {
pal = match_pal(palette)
if (!is.null(which)) pal$pal = pal$pal[which]
if (reverse) pal$pal = rev(pal$pal)

scale_color_gradientn(..., colours=pal$pal)
}

#' @rdname scale_wa
#' @export
scale_fill_wa_c = function(palette="ferries", which=NULL, ..., reverse=FALSE) {
pal = match_pal(palette)
if (!is.null(which)) pal$pal = pal$pal[which]
if (reverse) pal$pal = rev(pal$pal)

scale_fill_gradientn(..., colours=pal$pal)
}

#' @rdname scale_wa
#' @export
scale_colour_wa_d = scale_color_wa_d
#' @rdname scale_wa
#' @export
scale_colour_wa_c = scale_color_wa_c
#' @rdname scale_wa
#' @export
scale_color_wa_b = scale_color_wa_d
#' @rdname scale_wa
#' @export
scale_colour_wa_b = scale_color_wa_d
#' @rdname scale_wa
#' @export
scale_fill_wa_b = scale_fill_wa_d

if (F) {
ggplot(mtcars, aes(mpg, wt)) +
geom_point(aes(color = factor(cyl), size=hp)) +
scale_color_wa_d()

ggplot(mtcars, aes(mpg, wt)) +
geom_point(aes(colour = hp)) +
scale_color_wa_c("seattle_night", which=c("seafirst", "pike_clock"))

ggplot(diamonds) +
geom_bar(aes(x = cut, fill = clarity)) +
scale_fill_wa_d(wacolors$sound_sunset, reverse=TRUE)
}
12 changes: 10 additions & 2 deletions R/helpers.R → R/util.R
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ pth = function(x) {
rgb = as.list(as.integer(strsplit(x, "\\s")[[1]]) / 256)
col = do.call(colorspace::sRGB, rgb)
str = paste0('"', colorspace::hex(col), '"')
rstudioapi::insertText(rstudioapi::getActiveDocumentContext()$selection[[1]]$range, str)
rstudioapi::sendToConsole(str, execute=F)
code_output(str)
}

# Helper for interactive code output
code_output = function(x) {
if (interactive() && require("rstudioapi", quietly=TRUE)) {
rstudioapi::sendToConsole(x, execute=F)
} else {
cat(x, "\n")
}
}
Loading

0 comments on commit 18b51c0

Please sign in to comment.