---
title: "An introduction to qualpalr"
author: "Johan Larsson"
date: "`r Sys.Date()`"
bibliography: qualpal.bib
vignette: >
  %\VignetteIndexEntry{An introduction to qualpalr}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
---

## Overview

`qualpalr` generates distinct qualitative color palettes, primarily for use in
R graphics. Given `n` (the number of colors to generate), along with a subset
in the [hsl color space](https://en.wikipedia.org/wiki/HSL_and_HSV) (a
cylindrical representation of the RGB color space) `qualpalr` attempts to find
the `n` colors in the provided color subspace that *maximize the smallest
pairwise color difference*. This is done by projecting the color subset from the
HSL color space to the DIN99d space. DIN99d is (approximately) perceptually
uniform, that is, the euclidean distance between two colors in the space is
proportional to their perceived difference.

## Examples

`qualpalr` relies on one basic function, `qualpal()`, which takes as its input
`n` (the number of colors to generate) and `colorspace`, which can be either

* a list of numeric vectors `h` (hue from -360 to 360), `s` (saturation from
0 to 1), and `l` (lightness from 0 to 1), all of length 2, specifying a min and
max.
* a character vector specifying one of the predefined color subspaces, which at
the time of writing are *pretty*, *pretty_dark*, *rainbow*, and *pastels*.

```{r}
#| label: basic-usage
library(qualpalr)
pal <- qualpal(n = 5, list(h = c(-200, 120), s = c(0.3, 0.8), l = c(0.4, 0.9)))

# Adapt the color space to deuteranomaly of severity 0.7
pal <- qualpal(n = 5, colorspace = "pretty", cvd = "deutan", cvd_severity = 0.7)
```

The resulting object, `pal`, is a list with several color tables and a distance
matrix based on the DIN99d color difference formula.

```{r}
#| label: qualpal-list
pal
```

Methods for `pairs` and `plot` have been written for `qualpal` objects to help
visualize the results.

```{r}
#| fig-show: hold
# Multidimensional scaling plot
plot(pal)

# Pairs plot in the Lab color space
pairs(pal, colorspace = "DIN99d")
```

The colors are most easily used in R by accessing `pal$hex`

```{r map, fig.width=5, fig.height=5}
#| fig-width: 5
#| fig-height: 5
library(maps)
map("france", fill = TRUE, col = pal$hex, mar = c(0, 0, 0, 0))
```

## Details

`qualpal` begins by generating a point cloud out of the HSL color subspace
provided by the user, using a quasi-random Halton sequence. Here is the color
subscape in HSL with settings
`h = c(-200, 120), s = c(0.3, 0.8), l = c(0.4, 0.9)`.

```{r}
#| label: details-input
#| message: false
#| echo: false
options(rgl.useNULL = TRUE)
library(rgl)

# Set up color subspace as in qualpal()
h <- c(-200, 120)
s <- c(0.3, 0.8)
l <- c(0.4, 0.9)

# Generate a quasi-random Halton sequence
rnd <- randtoolbox::halton(1000, dim = 3)

# Convert random sequence to specified color space
scale_runif <- function(x, new_min, new_max) {
  (new_max - new_min) * (x - 1) + new_max
}

rnd[, 2] <- sqrt(rnd[, 2])
H <- scale_runif(rnd[, 1], min(h), max(h))
S <- scale_runif(rnd[, 2], min(s), max(s))
L <- scale_runif(rnd[, 3], min(l), max(l))

HSL <- cbind(H, S, L)
HSL[HSL[, 1] < 0, 1] <- HSL[HSL[, 1] < 0, 1] + 360

# Set up variables to produce the HSL cylinder
x <- S * cos(H * pi / 180)
y <- S * sin(H * pi / 180)

# Convert to RGB to enable coloring
RGB <- qualpalr:::convert_colors(HSL, "hsl", "rgb")

plot3d(cbind(x, y, L), col = rgb(RGB), main = "HSL")
rglwidget()
```

The program then proceeds by projecting these colors into the sRGB space.

```{r}
#| label: rgb-space
#| echo: false
plot3d(RGB, col = rgb(RGB), main = "RGB")
rglwidget()
```

It then continues projecting the colors, first into the XYZ space, then CIELab
(not shown here), and then finally the DIN99d space.

```{r}
#| label: din-space
#| echo: false
DIN99d <- qualpalr:::convert_colors(RGB, "rgb", "din99d")

plot3d(DIN99d, col = rgb(RGB), main = "DIN99d")
rglwidget()
```

The DIN99d color space [@cui_uniform_2002] is a euclidean, perceptually uniform
color space. This means that the difference between two colors is equal to the
euclidean distance between them. We take advantage of this by computing a
distance matrix on all the colors in the subset, finding their pairwise color
differences. We then apply a power transformation [@huang_power_2015] to fine
tune these differences.

To select the `n` colors that the user wanted, we proceed greedily: first, we
find the two most distant points, then we find the third point that maximizes 
the minimum distance to the previously selected points. This is repeated until
`n` points are selected. These points are then returned to the user; below is an
example using `n = 5`.

```{r}
#| label: selected-points
#| echo: false
pal <- qualpal(5, list(h = c(-200, 120), s = c(0.3, 0.8), l = c(0.4, 0.9)))
plot3d(DIN99d, col = rgb(RGB), main = "DIN99d", alpha = 0.1)
plot3d(pal$DIN99d, col = pal$hex, main = "DIN99d", add = TRUE, size = 5)
rglwidget()
```

### Color Specifications

At the time of writing, qualpalr works only in the sRGB color space with
the *CIE Standard Illuminant D65* reference white. 

## Thanks

[Bruce Lindbloom's webpage](http://www.brucelindbloom.com/) has
been instrumental in making qualpalr. Thanks also to
[i want hue](https://medialab.github.io/iwanthue/), which inspired me
to make qualpalr.

## References
