diff --git a/DESCRIPTION b/DESCRIPTION index 7d95371..c09ff58 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,22 +1,22 @@ Package: painbow Type: Package -Title: Use XKCD's "Painbow" Colormap and Dataset in ggplot2 -Version: 1.0.1 +Title: Use XKCD's "Painbow" Colormap and Dataset in Base and ggplot2 Graphics +Version: 1.1.0 Author: Steve Haroz Maintainer: Steve Haroz -Description: XKCD described a supposedly "bad" colormap that it called a "Painbow" (see ). But simple tests demonstrate that under some circumstances, the colormap can perform very well, and people can find information that is difficult to detect with the ggplot2 default and even supposedly "good" colormaps like viridis. This library let's you use the Painbow in your own ggplot graphs. +Description: XKCD described a supposedly "bad" colormap that it called a "Painbow" (see ). But simple tests demonstrate that under some circumstances, the colormap can perform very well, and people can find information that is difficult to detect with the ggplot2 default and even supposedly "good" colormaps like viridis. This package lets you use the Painbow in your own base and ggplot2 graphics. License: MIT + file LICENSE Encoding: UTF-8 LazyData: true Imports: - ggplot2 + grDevices, stats, ggplot2 Suggests: knitr, patchwork, spelling, rmarkdown Depends: - R (>= 2.10) + R (>= 2.10), graphics RoxygenNote: 7.1.2 Language: en-US BugReports: https://github.com/steveharoz/painbow/issues/new diff --git a/NAMESPACE b/NAMESPACE index d75f824..734dabd 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1 +1,23 @@ -exportPattern("^[[:alpha:]]+") +importFrom("grDevices", + "col2rgb", + "rgb") + +importFrom("stats", + "approx") + +importFrom("ggplot2", + "scale_fill_gradientn", + "scale_color_gradientn") + +export( + ## color vector + "painbow_colors", + + ## base palette + "painbow.colors", + + ## ggplot2 scales + "scale_fill_painbow", + "scale_color_painbow", + "scale_colour_painbow" +) diff --git a/R/painbow.R b/R/painbow.R index f58842f..24b7601 100644 --- a/R/painbow.R +++ b/R/painbow.R @@ -7,6 +7,33 @@ painbow_colors = c( '#FFFFFF', '#FEFEFF', '#FAFAFC', '#F4F4F7', '#EBECF1', '#E4E4EC', '#DBDAE6', '#D1D1DE', '#C7C7D5', '#BBBDCB', '#AFB3C3', '#A4A7BA', '#999CB0', '#8F91A6', '#87899D', '#7E8193', '#747788', '#6A6D7C', '#626170', '#575765', '#4C4C57', '#41424B', '#37373E', '#2D2C32', '#242328', '#1B1A1E', '#131313', '#0C0C0A', '#060606', '#010102', '#000000', '#000003', '#00000C', '#000219', '#03092A', '#091239', '#111B49', '#162150', '#1C2756', '#222D5C', '#2A3463', '#333B6A', '#3A4370', '#424B77', '#4A527D', '#515A82', '#576085', '#5D6689', '#606C8B', '#60718C', '#5F758A', '#577985', '#4A7E80', '#407D7B', '#367C72', '#287C69', '#1C7C60', '#147C58', '#0C7D50', '#037D47', '#1E7941', '#247139', '#356632', '#54562B', '#714524', '#8D361D', '#AD2516', '#C91610', '#DF0F0C', '#F20605', '#FD0004', '#FE0302', '#FD0400', '#FD0200', '#FC0401', '#FC0202', '#FA0401', '#F80601', '#F80400', '#F50800', '#F30B01', '#F10A01', '#ED0F00', '#E91101', '#E71302', '#E31401', '#E01400', '#DC1900', '#D91D00', '#D41E00', '#D12001', '#CD2200', '#C52A00', '#C32A00', '#C02B01', '#B83201', '#B53301', '#AF3601', '#AA3A01', '#A63D02', '#A04002', '#9A4502', '#964702', '#914A01', '#8B4F01', '#865202', '#825503', '#7D5904', '#775C03', '#716003', '#6D6304', '#666704', '#606B06', '#5D6F07', '#587207', '#527607', '#4E7908', '#4B7C08', '#467F08', '#418309', '#3E860A', '#38890C', '#348D0C', '#2F900C', '#2E930E', '#2A960E', '#269A0E', '#239D0E', '#1FA00F', '#1CA211', '#1EA411', '#1EA712', '#1CA912', '#1BAC14', '#18AE15', '#13B015', '#0DB216', '#0EB417', '#09B715', '#14BA18', '#13BD1B', '#09C11C', '#0FC41E', '#14C721', '#13CA22', '#1CCC25', '#21CF27', '#27D129', '#2CD32A', '#31D62C', '#37D72F', '#41D932', '#47DB35', '#50DD37', '#57DF3A', '#5CE13D', '#65E23F', '#6BE341', '#76E343', '#80E547', '#85E749', '#8BE84D', '#91E950', '#98EB53', '#A1EB56', '#A7ED58', '#ABEE5B', '#B2EE5E', '#BAEE61', '#BEF064', '#C0F167', '#C5F26A', '#CAF26D', '#CFF173', '#D4F17A', '#D6F181', '#D9F186', '#DBF38B', '#DDF293', '#DEF399', '#E0F29F', '#E2F2A8', '#E3F1AF', '#E3F0B5', '#E4EFBC', '#E4EEC1', '#E5EDC8', '#E5ECCD', '#E6EBD2', '#E6EAD8', '#E7E9DD', '#E7E9E1' ) +## base graphics palette +painbow.colors = function(n, alpha = FALSE, rev = FALSE) { + ## n: number of colors + n = as.integer(n[1L]) + if (n < 1L) return(character()) + + ## alpha: logical or numeric alpha transparency (vector) in [0, 1] + if (identical(alpha, FALSE)) alpha = NULL + if (!is.null(alpha)) alpha = pmin(pmax(as.numeric(alpha), 0), 1) * 255 + + ## interpolate painbow RGB coordinates + pain = grDevices::col2rgb(painbow_colors) + x = seq.int(0, 1, length.out = length(painbow_colors)) + xout = seq.int(0, 1, length.out = n) + pain = grDevices::rgb( + red = stats::approx(x, pain[1L, ], xout = xout)$y, + green = stats::approx(x, pain[2L, ], xout = xout)$y, + blue = stats::approx(x, pain[3L, ], xout = xout)$y, + alpha = alpha, + maxColorValue = 255) + + ## rev: reverse order? + if (rev) rev(pain) else pain +} + + +## ggplot2 scales scale_fill_painbow = function(...) { ggplot2::scale_fill_gradientn( colors = painbow_colors, diff --git a/man/painbow.Rd b/man/painbow.Rd index 5ce61a4..863d087 100644 --- a/man/painbow.Rd +++ b/man/painbow.Rd @@ -1,37 +1,73 @@ -\name{scale_colour_painbow} +\name{painbow} + +\alias{painbow.colors} \alias{scale_color_painbow} \alias{scale_colour_painbow} \alias{scale_fill_painbow} -\title{Painbow colormap} +\title{XKCD Painbow Colormap} -\description{Use XKCD's "Painbow" colormap} +\description{Use XKCD's "Painbow" colormap in base and ggplot2 graphics.} \usage{ +painbow.colors(n, alpha = FALSE, rev = FALSE) + scale_color_painbow(...) scale_colour_painbow(...) scale_fill_painbow(...) } \arguments{ + \item{n}{integer. The number of colors (\eqn{\ge 1}) in the palette.} + \item{alpha}{logical or numeric. Specification of an alpha-transparency (vector) + in the range from 0 (transparent) to 1 (opaque). If \code{FALSE} no alpha + level is added. If \code{TRUE} full opacity (1) is used for all colors.} + \item{rev}{logical. Should the ordering of the colors be reversed?} \item{...}{ Any arguments are passed to \code{\link[ggplot2:scale_fill_gradientn]{ggplot2:scale_fill_gradientn()}} } } -\details{The colors were extracted directly from the colorscale in XKCD's image (https://xkcd.com/2537/).} +\details{ + The 192 colors in the palette were extracted directly from the colormap in XKCD's image + (\url{https://xkcd.com/2537/}). -\author{Steve Haroz} + The \code{palette.colors} function extracts \code{n} colors along the 192 colors + by linear interpolation of the underlying RGB (red-green-blue) coordinates. + + The \code{scale_*_painbow} functions provide scales for ggplot2 graphics. +} -%% ~Make other sections like Warning with \section{Warning }{....} ~ +\author{Steve Haroz} \seealso{ See \code{\link[ggplot2:scale_fill_gradientn]{ggplot2:scale_fill_gradientn()}} for additional arguments. } \examples{ -# You can use it for continuous data -library(ggplot2) +## recreate painbow heatmap for continuous data +data("painbow_data", package = "painbow") + +## ggplot2 graphics +library("ggplot2") +ggplot(painbow_data) + + aes(x, y, fill = value) + + geom_tile() + + scale_fill_painbow() + +## base graphics +pd <- matrix(painbow_data$value, ncol = 205, byrow = TRUE)[, 205:1] +filled.contour(pd, color.palette = painbow.colors) +filled.contour(pd, color.palette = painbow.colors, nlevels = 100) + +# Old Faithful data +data("faithfuld", package = "ggplot2") ggplot(faithfuld) + aes(waiting, eruptions, fill = density) + geom_tile() + scale_fill_painbow() + +## illustrate usage of base palette +painbow.colors(5) ## original order +painbow.colors(5, rev = TRUE) ## reversed order +painbow.colors(5, alpha = TRUE) ## with explicit full opacity +painbow.colors(5, alpha = 0:4/4) ## with decreasing transparency }