# `Scales.Palette`
[🔗](https://github.com/giggleton/scales/blob/v0.1.0/lib/scales/palette.ex#L1)

Palettes: what turns a value's place among the data into a value of its aesthetic, a colour, a
size, a shape or a line type. A palette is a value naming its function, so it compares and
prints. A continuous palette is handed places, 0 to 1 over the data's range (`continuous/2`); a
discrete one the number of levels it must tell apart (`discrete/2`).

## Continuous palettes

  * `:identity`, R's `pal_identity()`: each place as it is.
  * `{:rescale, {low, high}}`, R's `pal_rescale()`: each place moved onto `low` to `high`.
  * `{:area, {low, high}}`, R's `pal_area()`: each place's square root moved onto `low` to
    `high`, so an area drawn at that size grows with the place.
  * `{:abs_area, max}`, R's `abs_area()`: the square root of each place's size moved onto 0 to
    `max`; a place may lie below 0 or past 1.
  * `{:gradient, colors}`, R's `pal_gradient_n()`, which R's two- and three-colour gradients are
    too: the colour that far along a gradient through `colors`, spaced evenly from 0 to 1, each
    place between two blended in CIE L*a*b* and its alpha along a straight line; a place at a
    colour takes it exactly, one outside 0 to 1 gives `nil`, and one colour is every place's.
    `:none` among the colours is R's `NA`, white with no alpha, as `"transparent"` is.
  * `{:gradient, colors, values}`, R's `pal_gradient_n(colours, values)`: each place moved first
    along the straight lines through `values`, each value at its own place spread evenly from 0
    to 1, then taken along the gradient; a place outside the values gives `nil`. The values are
    taken as R's `approxfun()` takes them: sorted, so unsorted ones run the gradient backwards; a
    `nil` dropped; equal values one, at their places' mean; as many as you like, not one per
    colour. Fewer than two left raise `ArgumentError`. R also warns of equal values.

## Discrete palettes

  * `{:hue, %{hue: {low, high}, chroma: chroma, luminance: luminance, direction: direction}}`,
    R's `pal_hue()`: colours of one chroma and luminance, their hues spaced evenly from `low` to
    `high`, handed out in reverse for a `direction` of -1. A range within a degree of the whole
    circle leaves its last share off, since its end is its start.
  * `{:gray, {start, finish}}`, R's `pal_grey()`: greys from the level `start` to `finish`, 0
    black and 1 white, spaced evenly once raised to the power 2.2.
  * `{:viridis, %{option: option, begin: begin, end: end, direction: direction, alpha:
    alpha}}`, R's `pal_viridis()`: colours of a viridis palette, as
    `Scales.ColorOperations.viridis/2` gives them.
  * `{:shape, :solid}` or `{:shape, :open}`, R's `pal_shape()`: six shapes by name, solid
    (`:circle_small`, `:triangle`, `:square`, `:plus`, `:square_cross`, `:asterisk`, R's 16, 17,
    15, 3, 7 and 8) or open (`:circle_open`, `:triangle_open`, `:square_open` and the same last
    three, R's 1, 2, 0, 3, 7 and 8).
  * `:linetype`, R's `pal_linetype()`: 13 line types, each as its dash pattern, a tuple of
    lengths, `{}` for a solid line.
  * `{:manual, values}`, R's `pal_manual()`: `values` in order.
  * `{:brewer, %{palette: palette, direction: direction}}`, R's `pal_brewer()`: the colours of
    one of ColorBrewer's palettes, named as R spells it (`brewer_palettes/1` lists them), which
    has a set of its own for each count from 3 to its largest. Fewer than 3 levels take the
    start of its set of 3, and a `direction` of -1 hands the colours out in reverse once the
    `nil`s past its largest set are added, so those come first, as R's do. A name it does not
    know is refused, where R warns and gives Greens.

A discrete palette asked for more levels than it holds gives `nil` for each past its end, as R
gives `NA` (and warns). No levels give none, where R's hue palette refuses them.

## A palette of the other kind

Each function takes a palette of the other kind too, as R's `as_continuous_pal()` and
`as_discrete_pal()` convert one. `continuous/2` hands a discrete palette of colours on as a
gradient through its `nlevels/1` colours, and one of numbers as those numbers joined by
straight lines, spaced evenly from 0 to 1; a place outside them gives `nil`, and a palette of
shapes, line types or other values is refused. `discrete/2` hands a continuous palette the
levels' places, spaced evenly from 0 to 1 by `spread/2`. `:identity` and `{:abs_area, max}`,
which R gives no type, are not converted: R hands them back as they are, so asked for 3 levels
they give the one number 3, and here they are refused.

Colours are uppercase `#RRGGBB`, or `#RRGGBBAA` with an alpha of their own, as
`Scales.ColorOperations` writes them.

## ColorBrewer

This product includes color specifications and designs developed by Cynthia Brewer
(http://colorbrewer.org/). They are Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The
Pennsylvania State University, licensed under the Apache License, Version 2.0, as R's
RColorBrewer carries them.

# `brewer_type`

```elixir
@type brewer_type() :: :seq | :div | :qual
```

A ColorBrewer palette's type: sequential, diverging or qualitative.

# `continuous`

```elixir
@type continuous() ::
  :identity
  | {:rescale, {number(), number()}}
  | {:area, {number(), number()}}
  | {:abs_area, number()}
  | {:gradient, [Scales.ColorOperations.t(), ...]}
  | {:gradient, [Scales.ColorOperations.t(), ...], [number() | nil, ...]}
```

A continuous palette.

# `discrete`

```elixir
@type discrete() ::
  {:hue,
   %{
     hue: {number(), number()},
     chroma: number(),
     luminance: number(),
     direction: 1 | -1
   }}
  | {:gray, {number(), number()}}
  | {:viridis,
     %{
       option: atom(),
       begin: number(),
       end: number(),
       direction: 1 | -1,
       alpha: number()
     }}
  | {:shape, :solid | :open}
  | :linetype
  | {:manual, list()}
  | {:brewer, %{palette: String.t(), direction: 1 | -1}}
```

A discrete palette.

# `t`

```elixir
@type t() :: continuous() | discrete()
```

A palette.

# `value_type`

```elixir
@type value_type() :: :color | :numeric | :shape | :linetype
```

What a palette's values are: colours, numbers, shapes or line types.

# `brewer_palettes`

```elixir
@spec brewer_palettes(brewer_type()) :: [String.t(), ...]
```

The names of ColorBrewer's palettes of `type`, sequential (`:seq`), diverging (`:div`) or
qualitative (`:qual`), as R spells them, in the order R's `pal_brewer()` numbers them, so its
`pal_brewer("qual", 5)` is `"Pastel2"`.

## Examples

    iex> Scales.Palette.brewer_palettes(:qual)
    ["Accent", "Dark2", "Paired", "Pastel1", "Pastel2", "Set1", "Set2", "Set3"]

# `continuous`

```elixir
@spec continuous(t(), [Scales.double()]) :: [Scales.double() | String.t()]
```

Each place's value under a continuous palette, a place running from 0 to 1 over the data's
range; a `nil` place gives `nil`. A discrete palette is converted first, as the moduledoc says.

A place may be any double (`t:Scales.double/0`), each given R's answer: the numeric palettes
carry an infinity or NaN through their arithmetic, the area palettes' square root making NaN
below 0, and a colour is `nil` at an infinity or NaN, but one colour's, which an infinity takes
too; a manual palette of numbers keeps NaN.

## Examples

    iex> Scales.Palette.continuous({:rescale, {2, 5}}, [0, 0.3, 1, nil])
    [2.0, 2.9, 5.0, nil]

    iex> Scales.Palette.continuous({:area, {1.0, 6.0}}, [0.2, 1])
    [3.23606797749979, 6.0]

    iex> Scales.Palette.continuous({:gradient, ["#132B43", "#56B1F7"]}, [0, 0.5, 1])
    ["#132B43", "#336A98", "#56B1F7"]

    iex> Scales.Palette.continuous({:gradient, ["#132B43", "#56B1F7"], [0, 0.25]}, [0.1, 0.5])
    ["#2C5D86", nil]

    iex> Scales.Palette.continuous({:gradient, ["#FF000080", "#0000FF"]}, [0.5, 1.5])
    ["#CA0088C0", nil]

    iex> Scales.Palette.continuous({:manual, [1.0, 5.0, 9.0]}, [0.25, 1.5])
    [3.0, nil]

    iex> Scales.Palette.continuous({:area, {1.0, 6.0}}, [-0.5, :infinity])
    [:nan, :infinity]

# `discrete`

```elixir
@spec discrete(t(), non_neg_integer()) :: list()
```

The values of a discrete palette for `count` levels, in level order. A continuous palette is
converted first, as the moduledoc says.

## Examples

    iex> hue = %{hue: {15.0, 375.0}, chroma: 100.0, luminance: 65.0, direction: 1}
    iex> Scales.Palette.discrete({:hue, hue}, 3)
    ["#F8766D", "#00BA38", "#619CFF"]

    iex> Scales.Palette.discrete({:manual, ["#FF0000", "#0000FF"]}, 3)
    ["#FF0000", "#0000FF", nil]

    iex> Scales.Palette.discrete({:rescale, {1, 6}}, 3)
    [1.0, 3.5, 6.0]

# `named`

```elixir
@spec named(String.t()) :: t()
```

The palette scales names `name`, R's `get_palette()` for scales' own names, in any case: each
ColorBrewer palette as `pal_brewer()` gives it (`brewer_palettes/1`), each viridis option as
`pal_viridis()` does (`Scales.ColorOperations.viridis_options/0`), `"grey"`, `pal_grey()`, and
`"hue"`, `pal_hue()`, each with R's defaults. Any other name raises `ArgumentError` listing
these; R also knows grDevices' palettes by name (`"Okabe-Ito"`, `"Blues 3"`), which are left
out.

## Examples

    iex> Scales.Palette.named("set1")
    {:brewer, %{palette: "Set1", direction: 1}}

    iex> Scales.Palette.named("Grey")
    {:gray, {0.2, 0.8}}

# `nlevels`

```elixir
@spec nlevels(t()) :: non_neg_integer() | nil
```

How many levels a discrete palette tells apart, R's `palette_nlevels()`: 6 shapes, 13 line
types, a manual palette's values, a brewer palette's largest set, and 255 for a hue, grey or
viridis palette, as R says; `nil` for a continuous palette. A discrete palette converted to a
continuous one is a gradient through this many of its values.

## Examples

    iex> Scales.Palette.nlevels({:brewer, %{palette: "Paired", direction: 1}})
    12

    iex> Scales.Palette.nlevels({:gradient, ["#132B43", "#56B1F7"]})
    nil

# `spread`

```elixir
@spec spread({number(), number()}, non_neg_integer()) :: [number()]
```

`count` numbers spread evenly from `low` to `high`, R's `seq(low, high, length.out = count)`:
the ends exact, each number between them `low` plus its index times the step, in R's order of
operations, so the fourth of seven from 0.1 to 1 is `0.54999999999999993`, not `0.55`.

## Examples

    iex> Scales.Palette.spread({0.0, 1.0}, 5)
    [0.0, 0.25, 0.5, 0.75, 1.0]

    iex> Scales.Palette.spread({2, 6}, 1)
    [2]

# `type`

```elixir
@spec type(t()) :: value_type() | nil
```

What a palette's values are, R's `palette_type()`: `:color`, `:numeric`, `:shape` or
`:linetype`, or `nil` for `:identity` and `{:abs_area, max}`, which R gives no type.

A manual palette's is guessed from its values, as R's `pal_manual()` guesses it: `:color` where
every value is a string `Scales.ColorOperations.parse/1` reads, as no values are, so not
`:none`, `:numeric` where every one is a number, else `nil`, where R names how its values are
stored (`"character"`). R reads a colour as hex or one of its own colour names, so `"grey50"` is a
colour there and `"transparent"` is not; here they are the other way round.

## Examples

    iex> Scales.Palette.type({:brewer, %{palette: "Set1", direction: 1}})
    :color

    iex> Scales.Palette.type({:manual, [1, 5, 9]})
    :numeric

    iex> Scales.Palette.type({:manual, [:circle_small, :triangle]})
    nil

---

*Consult [api-reference.md](api-reference.md) for complete listing*
