# File src/library/grDevices/R/colorstuff.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1995-2023 The R Core Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # A copy of the GNU General Public License is available at # https://www.R-project.org/Licenses/ colours <- colors <- function(distinct = FALSE) { c <- .Call(C_colors) if(distinct) c[!duplicated(t(col2rgb(c)))] else c } col2rgb <- function(col, alpha = FALSE) { ## R-level trap for now. if(any(as.character(col) %in% "0")) stop("numerical color values must be positive", domain = NA) if (is.factor(col)) col <- as.character(col) .Call(C_col2rgb, col, alpha) } gray <- function(level, alpha) .Call(C_gray, level, if (missing(alpha)) NULL else alpha) grey <- gray rgb <- function(red, green, blue, alpha, names = NULL, maxColorValue = 1) { ## Only 'red' given if(missing(green) && missing(blue)) { if(is.matrix(red) || is.data.frame(red)) { red <- data.matrix(red) if(ncol(red) < 3L) stop("at least 3 columns needed") green <- red[,2L]; blue <- red[,3L]; red <- red[,1L] } } .Call(C_rgb, red, green, blue, if (missing(alpha)) NULL else alpha, maxColorValue, names) } hsv <- function(h = 1, s = 1, v = 1, alpha) .Call(C_hsv, h, s, v, if(missing(alpha)) NULL else alpha) hcl <- function (h = 0, c = 35, l = 85, alpha, fixup = TRUE) .Call(C_hcl, h, c, l, if(missing(alpha)) NULL else alpha, fixup) rgb2hsv <- function(r, g = NULL, b = NULL, maxColorValue = 255) { rgb <- if(is.null(g) && is.null(b)) as.matrix(r) else rbind(r, g, b) if(!is.numeric(rgb)) stop("rgb matrix must be numeric") d <- dim(rgb) if(d[1L] != 3L) stop("rgb matrix must have 3 rows") n <- d[2L] if(n == 0L) return(cbind(c(h = 1, s = 1, v = 1))[, 0L]) ## else: rgb <- rgb/maxColorValue if(any(0 > rgb) || any(rgb > 1)) stop("rgb values must be in [0, maxColorValue]") .Call(C_RGB2hsv, rgb) } ## A quick little ''rainbow'' function -- improved by MM ## doc in ../man/palettes.Rd rainbow <- function (n, s = 1, v = 1, start = 0, end = max(1,n - 1)/n, alpha, rev = FALSE) { if ((n <- as.integer(n[1L])) > 0) { if(start == end || any(c(start,end) < 0)|| any(c(start,end) > 1)) stop("'start' and 'end' must be distinct and in [0, 1].") alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- hsv(h = seq.int(start, (start > end)*1 + end, length.out = n) %% 1, s, v, alpha) if(rev) rev(cols) else cols } else character() } topo.colors <- function (n, alpha, rev = FALSE) { if ((n <- as.integer(n[1L])) > 0) { j <- n %/% 3 k <- n %/% 3 i <- n - j - k alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- c(if(i > 0) hsv(h = seq.int(from = 43/60, to = 31/60, length.out = i), alpha = alpha[seq_len(i)]), if(j > 0) hsv(h = seq.int(from = 23/60, to = 11/60, length.out = j), alpha = alpha[i + seq_len(j)]), if(k > 0) hsv(h = seq.int(from = 10/60, to = 6/60, length.out = k), alpha = alpha[i + j + seq_len(k)], s = seq.int(from = 1, to = 0.3, length.out = k), v = 1)) if(rev) rev(cols) else cols } else character() } terrain.colors <- function (n, alpha, rev = FALSE) { if ((n <- as.integer(n[1L])) > 0) { k <- n%/%2 h <- c(4/12, 2/12, 0/12) s <- c(1, 1, 0) v <- c(0.65, 0.9, 0.95) alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- c(hsv(h = seq.int(h[1L], h[2L], length.out = k), s = seq.int(s[1L], s[2L], length.out = k), v = seq.int(v[1L], v[2L], length.out = k), alpha = alpha[seq_len(k)]), hsv(h = seq.int(h[2L], h[3L], length.out = n - k + 1)[-1L], s = seq.int(s[2L], s[3L], length.out = n - k + 1)[-1L], v = seq.int(v[2L], v[3L], length.out = n - k + 1)[-1L], alpha = alpha[k + seq_len(n - k)])) if(rev) rev(cols) else cols } else character() } heat.colors <- function (n, alpha, rev = FALSE) { if ((n <- as.integer(n[1L])) > 0) { j <- n %/% 4 i <- n - j alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- c(rainbow(i, start = 0, end = 1/6, alpha = alpha[seq_len(i)]), if (j > 0) hsv(h = 1/6, s = seq.int(from = 1-1/(2*j), to = 1/(2*j), length.out = j), v = 1, alpha = alpha[i + seq_len(n - i)])) if(rev) rev(cols) else cols } else character() } cm.colors <- function (n, alpha, rev = FALSE) { if ((n <- as.integer(n[1L])) > 0L) { even.n <- n %% 2L == 0L k <- n %/% 2L l1 <- k + 1L - even.n l2 <- n - k + even.n alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- c(if(l1 > 0L) hsv(h = 6/12, s = seq.int(.5, if(even.n) .5/k else 0, length.out = l1), v = 1, alpha = alpha[seq_len(l1)]), if(l2 > 1) hsv(h = 10/12, s = seq.int(0, 0.5, length.out = l2)[-1L], v = 1, alpha = alpha[seq_len(n)[-seq_len(l1)]])) if(rev) rev(cols) else cols } else character() } gray.colors <- function(n, start = 0.3, end = 0.9, gamma = 2.2, alpha, rev = FALSE) { alpha <- if(missing(alpha) || is.null(alpha)) NULL else rep_len(alpha, n) cols <- gray(seq.int(from = start^gamma, to = end^gamma, length.out = n)^(1/gamma), alpha) if(rev) rev(cols) else cols } grey.colors <- gray.colors ##' match palette name (not exported) palette.match <- function(pal) { fx <- function(x) tolower(gsub("[-, _, \\,, (, ), \\ , \\.]", "", x)) charmatch(fx(pal), fx(names(.palette_colors_hex))) } palette <- function (value) { ## if value missing return current palette (visibly) if (missing(value)) return(.Call(C_palette, character())) ## in case value is just a single string, select the corresponding set ## colors with "default" handled at C level if (length(value) == 1L && value != "default") { n <- palette.match(value) if (!is.na(n)) value <- .palette_colors_hex[[n]] } ## set new palette value, return old one invisibly ## if a .Device is open, record the .Call.graphics if (.Device == "null device") { invisible(.Call(C_palette, value)) } else { invisible(.Call.graphics(C_palette, value)) } } ## palette.colors() is a function for accessing the colors behind palette() ## directly. palette.pals() shows the available names (a la hcl.pals()). palette.pals <- function() names(.palette_colors_hex) palette.colors <- function(n = NULL, palette = "Okabe-Ito", alpha, recycle = FALSE, names = FALSE) { ## number of colors if (!is.null(n)) { n <- as.integer(n[1L]) if (n < 1L) return(character()) } p <- palette.match(palette) if (is.na(p)) stop("'palette' does not match any given palette") if (p < 1L) stop("'palette' is ambiguous") ## select n colors from palette nc <- length(cols <- .palette_colors_hex[[p]]) if (is.null(n)) n <- nc else if(n > nc) { if(recycle) { cols <- cols[rep_len(seq_len(nc), n)] } else { warning(sprintf("'n' set to %s, the maximum available for %s palette", nc, palette)) n <- nc } } else if(n < nc) cols <- cols[seq_len(n)] ## add alpha if specified as number: if (!(missing(alpha) || is.null(alpha))) { alpha <- rep_len(alpha, n) alpha <- format(as.hexmode(round(alpha * 255 + 0.0001)), width = 2L, upper.case = TRUE) cols <- paste0(cols, alpha) } if (names) cols else unname(cols) } ## underlying hex codes for palette color sets .palette_colors_hex <- list( ## default in R <= 3.6.x "R3" = c("#000000", "#FF0000", "#00CD00", "#0000FF", "#00FFFF", "#FF00FF", "#FFFF00", "#BEBEBE"), ## rgb( ## r = c(0, 255, 0, 0, 0, 255, 255, 190), ## g = c(0, 0, 205, 0, 255, 0, 255, 190), ## b = c(0, 0, 0, 255, 255, 255, 0, 190), ## maxColorValue = 255 ## ), ## new default in R >= 4.0.0 "R4" = c("#000000", "#DF536B", "#61D04F", "#2297E6", "#28E2E5", "#CD0BBC", "#F5C710", "#9E9E9E"), ## hcl(h = c(0, 5, 125, 245, 195, 315, 65, 0), ## c = c(0, 100, 90, 85, 63, 105, 94, 0), ## l = c(0, 55, 75, 60, 82, 48, 82, 65) ## ), ## scales::hue_pal (Hadley Wickham) ## re-ordered for RGBCMY plus black/gray "ggplot2" = c("#000000", "#F8766D", "#00BA38", "#619CFF", "#00BFC4", "#F564E3", "#B79F00", "#9E9E9E"), ## hcl( ## h = c(0, 15, 135, 255, 195, 315, 75, 0), ## c = c(0, 100, 100, 100, 100, 100, 100, 0), ## l = c(0, 65, 65, 65, 65, 65, 65, 65) ## ), ## Masataka Okabe & Kei Ito ## http://jfly.iam.u-tokyo.ac.jp/color/ "Okabe-Ito" = c(black = "#000000", orange = "#E69F00", skyblue = "#56B4E9", bluishgreen = "#009E73", yellow = "#F0E442", blue = "#0072B2", vermillion = "#D55E00", reddishpurple = "#CC79A7", gray = "#999999"), ## ColorBrewer.org (Mark A. Harrower & Cynthia A. Brewer) ## http://ColorBrewer2.org/ "Accent" = c("#7FC97F", "#BEAED4", "#FDC086", "#FFFF99", "#386CB0", "#F0027F", "#BF5B17", "#666666"), "Dark 2" = c("#1B9E77", "#D95F02", "#7570B3", "#E7298A", "#66A61E", "#E6AB02", "#A6761D", "#666666"), "Paired" = c("#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928"), "Pastel 1" = c("#FBB4AE", "#B3CDE3", "#CCEBC5", "#DECBE4", "#FED9A6", "#FFFFCC", "#E5D8BD", "#FDDAEC", "#F2F2F2"), "Pastel 2" = c("#B3E2CD", "#FDCDAC", "#CBD5E8", "#F4CAE4", "#E6F5C9", "#FFF2AE", "#F1E2CC", "#CCCCCC"), "Set 1" = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"), "Set 2" = c("#66C2A5", "#FC8D62", "#8DA0CB", "#E78AC3", "#A6D854", "#FFD92F", "#E5C494", "#B3B3B3"), "Set 3" = c("#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F"), ## Tableau 10 (Maureen Stone & Cristy Miller) ## https://www.tableau.com/about/blog/2016/7/colors-upgrade-tableau-10-56782 "Tableau 10" = c(blue = "#4E79A7", orange = "#F28E2B", red = "#E15759", lightteal = "#76B7B2", green = "#59A14F", yellow = "#EDC948", purple = "#B07AA1", pink = "#FF9DA7", brown = "#9C755F", lightgray = "#BAB0AC"), "Classic Tableau" = c("#1F77B4", "#FF7F0E", "#2CA02C", "#D62728", "#9467BD", "#8C564B", "#E377C2", "#7F7F7F", "#BCBD22", "#17BECF"), ## Polychrome ## (Kevin R. Coombes, Guy Brock, Zachary B. Abrams, Lynne V. Abruzzo) ## https://doi.org/10.18637/jss.v090.c01 "Polychrome 36" = c(darkpurplishgray = "#5A5156", purplishwhite = "#E4E1E3", vividred = "#F6222E", vividpurple = "#FE00FA", vividyellowishgreen = "#16FF32", strongpurplishblue = "#3283FE", vividorangeyellow = "#FEAF16", vividpurplishred = "#B00068", brilliantgreen = "#1CFFCE", vividyellowgreen = "#90AD1C", vividblue = "#2ED9FF", brilliantpurple = "#DEA0FD", vividviolet = "#AA0DFE", strongpink = "#F8A19F", strongblue = "#325A9B", strongreddishorange = "#C4451C", vividgreen = "#1C8356", lightolivebrown = "#85660D", vividreddishpurple = "#B10DA1", vividgreenishyellow = "#FBE426", vividyellowishgreen = "#1CBE4F", vividred = "#FA0087", vividpurplishred = "#FC1CBF", paleyellow = "#F7E1A0", strongreddishpurple = "#C075A6", vividviolet = "#782AB6", vividyellowgreen = "#AAF400", verylightblue = "#BDCDFF", strongreddishbrown = "#822E1C", verylightyellowishgreen = "#B5EFB5", verylightbluishgreen = "#7ED7D1", deepgreenishblue = "#1C7F93", vividpurple = "#D85FF7", deeppurple = "#683B79", brilliantblue = "#66B0FF", vividviolet = "#3B00FB"), "Alphabet" = c(amethyst = "#AA0DFE", blue = "#3283FE", caramel = "#85660D", damson = "#782AB6", ebony = "#565656", forest = "#1C8356", green = "#16FF32", honey = "#F7E1A0", iron = "#E2E2E2", jade = "#1CBE4F", kingcrab = "#C4451C", lavender = "#DEA0FD", magenta = "#FE00FA", navy = "#325A9B", orange = "#FEAF16", pink = "#F8A19F", quagmire = "#90AD1C", red = "#F6222E", sea = "#1CFFCE", turquoise = "#2ED9FF", ultraviolet = "#B10DA1", violet = "#C075A6", wine = "#FC1CBF", xanthin = "#B00068", yellow = "#FBE426", zinnia = "#FA0087") ) ## An unexported version that works with internal representation as 'rcolor' ## We could avoid this if we knew at R level whether the display list was ## enabled or inhibited: but we do need to record a call to C_palette2. recordPalette <- function() .Call.graphics(C_palette2, .Call(C_palette2, NULL))