color_brewery/
lib.rs

1//! Color schemes and gradients.
2//!
3//! This create provides the [color schemes by Cynthia
4//! Brewer](http://colorbrewer2.org/) and Matplotlib colormaps as well
5//! as gradients build from them (or from your own colors).  The
6//! default representation of colors is the one given by the [rgb
7//! crate][] but any type can be used: it suffices it implements the
8//! [`RGBColor`] trait.
9//!
10//! [rgb crate]: https://crates.io/crates/rgb
11
12use std::f64::consts::PI;
13use std::marker::PhantomData;
14use rgb::{RGBA, RGB8, RGB16, RGBA8, RGBA16};
15
16mod palettes;
17use palettes::ty::PaletteData;
18pub use palettes::ty::{PaletteType, Trivalent};
19
20/// A “continuous” range of colors parametrized by reals in \[0, 1\].
21pub trait ColorRange<Color> {
22    /// Returns the color corresponding to `t` ∈ \[0., 1.\].
23    fn rgb(&self, t: f64) -> Color;
24
25    /// Return an iterator yielding uniform sampling of `n` points
26    /// between `a` and `b` (with the bounds `a` and `b` included in
27    /// the list of points) together with colors.  It is not required
28    /// that `a <= b`.
29    fn range(self, mut a: f64, mut b: f64, n: usize) -> Range<Self, Color>
30    where Self: Sized {
31        if a == f64::INFINITY { a = f64::MAX; }
32        else if a == f64::NEG_INFINITY { a = f64::MIN };
33        if b == f64::NEG_INFINITY { b = f64::MIN; }
34        else if b == f64::INFINITY { b = f64::MAX };
35        // `a` or `b` NaN will give an iterator yielding NaN.
36        if n == 0 {
37            Range { range: self,  color: PhantomData,
38                    a, b, flast: 0., last: 0,
39                    i: 1, j: 0 } // Empty iterator
40        } else {
41            Range { range: self,  color: PhantomData,
42                    a, b, flast: (n - 1) as f64,
43                    last: n - 1, i: 0, j: n - 1 }
44        }
45    }
46}
47
48/// An iterator yielding `f64` in a given range together with colors.
49pub struct Range<R, Color> {
50    range: R,
51    color: PhantomData<Color>,
52    a: f64, // finite or NaN
53    b: f64, // finite or NaN
54    flast: f64, // `last` as a floating-point number
55    last: usize,
56    i: usize, // first position to be consumed (i ≤ j)
57    j: usize, // last position to be consumed
58}
59
60impl<R, Color> Range<R, Color> where R: ColorRange<Color> {
61    /// Return the float and RGB color of the position `k` (assuming
62    /// it is in the range `0 ..= self.last`).
63    fn rgb(&self, k: usize) -> (f64, Color) {
64        if k == 0 {
65            (self.a, R::rgb(&self.range, 0.))
66        } else if k == self.last {
67            (self.b, R::rgb(&self.range, 1.))
68        } else {
69            let alpha = (self.last - k) as f64;
70            let beta = k as f64;
71            let t = beta / self.flast;
72            let mut x = (alpha * self.a + beta * self.b) / self.flast;
73            if x.is_infinite() {
74                x = (1. - t) * self.a + t * self.b;
75            }
76            (x, R::rgb(&self.range, t))
77        }
78
79    }
80}
81
82impl<R, Color> Iterator for Range<R, Color>
83where R: ColorRange<Color> {
84    type Item = (f64, Color);
85
86    fn next(&mut self) -> Option<Self::Item> {
87        if self.i <= self.j {
88            let item = self.rgb(self.i);
89            self.i += 1;
90            Some(item)
91        } else {
92            None
93        }
94    }
95
96    fn size_hint(&self) -> (usize, Option<usize>) {
97        let len = self.last - self.i + 1;
98        (len, Some(len))
99    }
100}
101
102impl<R, Color> ExactSizeIterator for Range<R, Color>
103where R: ColorRange<Color> {
104    fn len(&self) -> usize { self.last - self.i + 1 }
105}
106
107impl<R, Color> DoubleEndedIterator for Range<R, Color>
108where R: ColorRange<Color> {
109    fn next_back(&mut self) -> Option<Self::Item> {
110        if self.i <= self.j {
111            let item = self.rgb(self.j);
112            if self.j == 0 {
113                self.i = 1
114            } else {
115                self.j -= 1;
116            }
117            Some(item)
118        } else {
119            None
120        }
121    }
122}
123
124/// Specifies the methods a RGB color encoding must provide.
125pub trait RGBColor: Sized {
126    /// Return the red, green, blue and alpha components of the color
127    /// (in \[0, 255\]).
128    fn to_rgba(&self) -> RGBA<f64>;
129
130    /// Create a color from its RGBA components (in \[0, 255\]).
131    fn from_rgba(rgba: RGBA<f64>) -> Self;
132
133    /// Return the color corresponding to the hue `h` ∈ \[0., 1.\].
134    ///
135    /// # Example
136    ///
137    /// ```
138    /// use rgb::RGB8;
139    /// use color_brewery::{RGBColor, ColorRange};
140    /// let rgb = RGB8::HUE.rgb(0.5);
141    /// ```
142    const HUE: Hue<Self> = Hue { color: PhantomData };
143
144    /// Return a gradient from color `c0` to color `c1`.
145    ///
146    /// # Example
147    ///
148    /// ```
149    /// use rgb::RGB8;
150    /// use color_brewery::{RGBColor, ColorRange};
151    /// let red = RGB8::new(255,0, 0);
152    /// let blue = RGB8::new(0, 0, 255);
153    /// let grad = red.gradient(&blue);
154    /// let rgb = grad.rgb(0.5);
155    /// ```
156    fn gradient(&self, c1: &Self) -> Gradient<Self> {
157        let lch0 = Lch::from_rgb(Self::to_rgba(self));
158        let lch1 = Lch::from_rgb(Self::to_rgba(c1));
159        let h0 = lch0.h;
160        let h1 = lch1.h;
161        let dh = {
162            if h1 > h0 && h1 - h0 > PI { h1 - (h0 + TWO_PI) }
163            else if h1 < h0 && h0 - h1 > PI { h1 + TWO_PI - h0 }
164            else { h1 - h0 } };
165        Gradient { c0: lch0,
166                   dc: Lch { l: lch1.l - lch0.l, c: lch1.c - lch0.c,
167                             h: dh, a: lch1.a - lch0.a },
168                   color: PhantomData }
169    }
170
171    /// Find palettes matching certain criteria.
172    fn palettes(len: usize) -> PaletteFind<Self> {
173        PaletteFind {
174            len,
175            typ: vec![],
176            blind: Trivalent::No, // "no" means "not necessarily want"
177            print: Trivalent::No,
178            photocopy: Trivalent::No,
179            lcd: Trivalent::No,
180            color: PhantomData,
181        }
182    }
183
184
185    /// Matplotlib magma color scheme.
186    ///
187    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/magma.png" width="100%" height="40" alt="magma">
188    #[inline]
189    fn magma() -> Palette<Self> { Palette::new(&palettes::MAGMA) }
190
191    /// Matplotlib inferno color scheme.
192    ///
193    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/inferno.png" width="100%" height="40" alt="inferno">
194    #[inline]
195    fn inferno() -> Palette<Self> { Palette::new(&palettes::INFERNO) }
196
197    /// Matplotlib plasma color scheme.
198    ///
199    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/plasma.png" width="100%" height="40" alt="plasma">
200    #[inline]
201    fn plasma() -> Palette<Self> { Palette::new(&palettes::PLASMA) }
202
203    /// Matplotlib viridis color scheme.
204    ///
205    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/viridis.png" width="100%" height="40" alt="viridis">
206    #[inline]
207    fn viridis() -> Palette<Self> { Palette::new(&palettes::VIRIDIS) }
208
209    /// Matplotlib cividis color scheme.
210    ///
211    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/cividis.png" width="100%" height="40" alt="cividis">
212    #[inline]
213    fn cividis() -> Palette<Self> { Palette::new(&palettes::CIVIDIS) }
214
215    /// Matplotlib twilight color scheme.
216    ///
217    #[inline]
218    fn twilight() -> Palette<Self> { Palette::new(&palettes::TWILIGHT) }
219
220    /// Matplotlib turbo color scheme.
221    ///
222    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/turbo.png" width="100%" height="40" alt="turbo">
223    #[inline]
224    fn turbo() -> Palette<Self> { Palette::new(&palettes::TURBO) }
225
226    /// Brewer "Light yellow to dark green" sequential scheme.
227    ///
228    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/YlGn.png" width="100%" height="40" alt="ylgn">
229    #[inline]
230    fn ylgn() -> PaletteIter<Self> { PaletteIter::new(&palettes::YLGN) }
231
232    /// Brewer "Light yellow to green to dark blue" sequential scheme.
233    ///
234    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/YlGnBu.png" width="100%" height="40" alt="ylgnbu">
235    #[inline]
236    fn ylgnbu() -> PaletteIter<Self> { PaletteIter::new(&palettes::YLGNBU) }
237
238    /// Brewer "Light green to dark blue" sequential scheme.
239    ///
240    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/GnBu.png" width="100%" height="40" alt="gnbu">
241    #[inline]
242    fn gnbu() -> PaletteIter<Self> { PaletteIter::new(&palettes::GNBU) }
243
244    /// Brewer "Light blue to dark green" sequential scheme.
245    ///
246    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/BuGn.png" width="100%" height="40" alt="bugn">
247    #[inline]
248    fn bugn() -> PaletteIter<Self> { PaletteIter::new(&palettes::BUGN) }
249
250    /// Brewer "Light purple to blue to dark green" sequential scheme.
251    ///
252    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PuBuGn.png" width="100%" height="40" alt="pubugn">
253    #[inline]
254    fn pubugn() -> PaletteIter<Self> { PaletteIter::new(&palettes::PUBUGN) }
255
256    /// Brewer "Light purple to dark blue" sequential scheme.
257    ///
258    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PuBu.png" width="100%" height="40" alt="pubu">
259    #[inline]
260    fn pubu() -> PaletteIter<Self> { PaletteIter::new(&palettes::PUBU) }
261
262    /// Brewer "Light blue to dark purple" sequential scheme.
263    ///
264    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/BuPu.png" width="100%" height="40" alt="bupu">
265    #[inline]
266    fn bupu() -> PaletteIter<Self> { PaletteIter::new(&palettes::BUPU) }
267
268    /// Brewer "Light red to dark purple" sequential scheme.
269    ///
270    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/RdPu.png" width="100%" height="40" alt="rdpu">
271    #[inline]
272    fn rdpu() -> PaletteIter<Self> { PaletteIter::new(&palettes::RDPU) }
273
274    /// Brewer "Light purple to dark red" sequential scheme.
275    ///
276    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PuRd.png" width="100%" height="40" alt="purd">
277    #[inline]
278    fn purd() -> PaletteIter<Self> { PaletteIter::new(&palettes::PURD) }
279
280    /// Brewer "Light orange to dark red" sequential scheme.
281    ///
282    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/OrRd.png" width="100%" height="40" alt="orrd">
283    #[inline]
284    fn orrd() -> PaletteIter<Self> { PaletteIter::new(&palettes::ORRD) }
285
286    /// Brewer "Light yellow to orange to dark red" sequential scheme.
287    ///
288    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/YlOrRd.png" width="100%" height="40" alt="ylorrd">
289    #[inline]
290    fn ylorrd() -> PaletteIter<Self> { PaletteIter::new(&palettes::YLORRD) }
291
292    /// Brewer "Light yellow to orange to dark brown" sequential scheme.
293    ///
294    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/YlOrBr.png" width="100%" height="40" alt="ylorbr">
295    #[inline]
296    fn ylorbr() -> PaletteIter<Self> { PaletteIter::new(&palettes::YLORBR) }
297
298
299    /// Brewer "Light to dark purple" sequential scheme.
300    ///
301    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Purples.png" width="100%" height="40" alt="purples">
302    #[inline]
303    fn purples() -> PaletteIter<Self> { PaletteIter::new(&palettes::PURPLES) }
304
305    /// Brewer "Light to dark blue" sequential scheme.
306    ///
307    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Blues.png" width="100%" height="40" alt="blues">
308    #[inline]
309    fn blues() -> PaletteIter<Self> { PaletteIter::new(&palettes::BLUES) }
310
311    /// Brewer "Light to dark green" sequential scheme.
312    ///
313    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Greens.png" width="100%" height="40" alt="greens">
314    #[inline]
315    fn greens() -> PaletteIter<Self> { PaletteIter::new(&palettes::GREENS) }
316
317    /// Brewer "Light to dark orange" sequential scheme.
318    ///
319    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Oranges.png" width="100%" height="40" alt="oranges">
320    #[inline]
321    fn oranges() -> PaletteIter<Self> { PaletteIter::new(&palettes::ORANGES) }
322
323    /// Brewer "Light to dark red" sequential scheme.
324    ///
325    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Reds.png" width="100%" height="40" alt="reds">
326    #[inline]
327    fn reds() -> PaletteIter<Self> { PaletteIter::new(&palettes::REDS) }
328
329    /// Brewer "Light to dark gray" sequential scheme.
330    ///
331    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Greys.png" width="100%" height="40" alt="greys">
332    #[inline]
333    fn greys() -> PaletteIter<Self> { PaletteIter::new(&palettes::GREYS) }
334
335
336    /// Brewer "Dark orange to light to dark purple" diverging scheme
337    ///
338    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PuOr.png" width="100%" height="40" alt="puor">
339    #[inline]
340    fn puor() -> PaletteIter<Self> { PaletteIter::new(&palettes::PUOR) }
341
342    /// Brewer "Dark brown to light to dark blue-green" diverging scheme
343    ///
344    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/BrBG.png" width="100%" height="40" alt="brbg">
345    #[inline]
346    fn brbg() -> PaletteIter<Self> { PaletteIter::new(&palettes::BRBG) }
347
348    /// Brewer "Dark reddish-purple to light to dark green" diverging scheme
349    ///
350    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PRGn.png" width="100%" height="40" alt="prgn">
351    #[inline]
352    fn prgn() -> PaletteIter<Self> { PaletteIter::new(&palettes::PRGN) }
353
354    /// Brewer "Dark magenta to light to dark yellow-green" diverging scheme
355    ///
356    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/PiYG.png" width="100%" height="40" alt="piyg">
357    #[inline]
358    fn piyg() -> PaletteIter<Self> { PaletteIter::new(&palettes::PIYG) }
359
360    /// Brewer "Dark red to light to dark blue" diverging scheme.
361    ///
362    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/RdBu.png" width="100%" height="40" alt="rdbu">
363    #[inline]
364    fn rdbu() -> PaletteIter<Self> { PaletteIter::new(&palettes::RDBU) }
365
366    /// Brewer "Dark red to light to dark grey" diverging scheme.
367    ///
368    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/RdGy.png" width="100%" height="40" alt="rdgy">
369    #[inline]
370    fn rdgy() -> PaletteIter<Self> { PaletteIter::new(&palettes::RDGY) }
371
372    /// Brewer "Dark red to light yelow to dark blue" diverging scheme.
373    ///
374    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/RdYlBu.png" width="100%" height="40" alt="rdylbu">
375    #[inline]
376    fn rdylbu() -> PaletteIter<Self> { PaletteIter::new(&palettes::RDYLBU) }
377
378    /// Brewer "Dark red, orange, light yellow, green, dark blue"
379    /// diverging scheme.
380    ///
381    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Spectral.png" width="100%" height="40" alt="spectral">
382    #[inline]
383    fn spectral() -> PaletteIter<Self> { PaletteIter::new(&palettes::SPECTRAL) }
384
385    /// Brewer "Dark red, orange, light yellow, yellow-green, dark green"
386    /// diverging scheme.
387    ///
388    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/RdYlGn.png" width="100%" height="40" alt="rdylgn">
389    #[inline]
390    fn rdylgn() -> PaletteIter<Self> { PaletteIter::new(&palettes::RDYLGN) }
391
392
393    /// Brewer qualitative scheme: includes bold, readily named, basic
394    /// colors (such as red, green, blue).
395    ///
396    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Set1.png" width="100%" height="40" alt="set1">
397    #[inline]
398    fn set1() -> PaletteIter<Self> { PaletteIter::new(&palettes::SET1) }
399
400    /// Brewer qualitative scheme: Lighter version of [`Self::set1`].
401    ///
402    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Pastel1.png" width="100%" height="40" alt="pastel1">
403    #[inline]
404    fn pastel1() -> PaletteIter<Self> { PaletteIter::new(&palettes::PASTEL1) }
405
406    /// Brewer qualitative scheme: Includes mostly a mixture colors
407    /// (such as blue-green, red-orange).
408    ///
409    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Set2.png" width="100%" height="40" alt="set2">
410    #[inline]
411    fn set2() -> PaletteIter<Self> { PaletteIter::new(&palettes::SET2) }
412
413    /// Brewer qualitative scheme: Lighter version of [`Self::set2`].
414    ///
415    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Pastel2.png" width="100%" height="40" alt="pastel2">
416    #[inline]
417    fn pastel2() -> PaletteIter<Self> { PaletteIter::new(&palettes::PASTEL2) }
418
419    /// Brewer qualitative scheme: Darker version of [`Self::set2`].
420    ///
421    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Dark2.png" width="100%" height="40" alt="dark2">
422    #[inline]
423    fn dark2() -> PaletteIter<Self> { PaletteIter::new(&palettes::DARK2) }
424
425    /// Brewer qualitative scheme: Medium saturation set with more
426    /// lightness variation and more classes than [`Self::set1`] and
427    /// [`Self::set2`].
428    ///
429    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Set3.png" width="100%" height="40" alt="set3">
430    #[inline]
431    fn set3() -> PaletteIter<Self> { PaletteIter::new(&palettes::SET3) }
432
433    /// Brewer qualitative scheme: Light/dark paris for namable hues.
434    ///
435    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Paired.png" width="100%" height="40" alt="paired">
436    #[inline]
437    fn paired() -> PaletteIter<Self> { PaletteIter::new(&palettes::PAIRED) }
438
439    /// Brewer qualitative scheme: Include lightness and saturation
440    /// extremes to accent small or important areas.
441    ///
442    /// <img src="https://raw.githubusercontent.com/d3/d3-scale-chromatic/master/img/Accent.png" width="100%" height="40" alt="accent">
443    #[inline]
444    fn accent() -> PaletteIter<Self> { PaletteIter::new(&palettes::ACCENT) }
445
446    /// Convert the color to grayscale.
447    fn to_gray(&self) -> Self {
448        let RGBA{ r, g, b, a } = Self::to_rgba(self);
449        let x = 0.299 * r + 0.587 * g + 0.114 * b;
450        Self::from_rgba(RGBA{ r: x, g: x, b: x, a })
451    }
452}
453
454impl RGBColor for RGBA<f64> {
455    #[inline]
456    fn to_rgba(&self) -> RGBA<f64> { *self }
457
458    #[inline]
459    fn from_rgba(c: RGBA<f64>) -> Self { c }
460}
461
462impl RGBColor for RGB8 {
463    #[inline]
464    fn to_rgba(&self) -> RGBA<f64> {
465        RGBA{ r: self.r as f64, g: self.g as f64, b: self.b as f64, a: 255. }
466    }
467
468    #[inline]
469    fn from_rgba(c: RGBA<f64>) -> Self {
470        RGB8 { r: c.r as u8,  g: c.g as u8,  b: c.b as u8 }
471    }
472}
473
474impl RGBColor for RGB16 {
475    #[inline]
476    fn to_rgba(&self) -> RGBA<f64> {
477        RGBA{ r: self.r as f64, g: self.g as f64, b: self.b as f64, a: 255. }
478    }
479
480    #[inline]
481    fn from_rgba(c: RGBA<f64>) -> Self {
482        RGB16 { r: c.r as u16,  g: c.g as u16,  b: c.b as u16 }
483    }
484}
485
486impl RGBColor for RGBA8 {
487    #[inline]
488    fn to_rgba(&self) -> RGBA<f64> {
489        RGBA{ r: self.r as f64, g: self.g as f64, b: self.b as f64,
490              a: self.a as f64 }
491    }
492
493    #[inline]
494    fn from_rgba(c: RGBA<f64>) -> Self {
495        RGBA8 { r: c.r as u8,  g: c.g as u8,  b: c.b as u8, a: c.a as u8 }
496    }
497}
498
499impl RGBColor for RGBA16 {
500    #[inline]
501    fn to_rgba(&self) -> RGBA<f64> {
502        RGBA{ r: self.r as f64, g: self.g as f64, b: self.b as f64,
503              a: self.a as f64 }
504    }
505
506    #[inline]
507    fn from_rgba(c: RGBA<f64>) -> Self {
508        RGBA16 { r: c.r as u16,  g: c.g as u16,  b: c.b as u16, a: c.a as u16 }
509    }
510}
511
512/// The type for colors in the CIE L*C*h*_ab color space with a D50
513/// reference white point and an alpha component.  This color space is
514/// CIE L*a*b* with polar coordinates.
515#[derive(Clone, Copy)]
516struct Lch {
517    /// The lightness in the range 0. to 100.
518    l: f64,
519    /// The chroma, in the range 0. to 181.02, but less in practice.
520    c: f64,
521    /// The hue in degrees in the range 0. to 2π.
522    h: f64,
523    /// Alpha component
524    a: f64,
525}
526
527const EPS0: f64 = 6. / 29.;
528const EPS: f64 = EPS0 * EPS0 * EPS0 ;
529const TWO_PI: f64 = 2. * PI;
530
531impl Lch {
532    fn from_rgb(c: RGBA<f64>) -> Lch {
533        // See https://github.com/dbuenzli/gg/blob/b8704687d669d139bb4ac7a54115afc7e5caaa55/src/gg.ml#L2926
534        const C0: f64 = 1. / 3.;
535        const C1: f64 = 841. / 108.;
536        const C2: f64 = 4. / 29.;
537        let xr = 0.4522795 * c.r + 0.3993744 * c.g + 0.1483460 * c.b;
538        let yr = 0.2225105 * c.r + 0.7168863 * c.g + 0.0606032 * c.b;
539        let zr = 0.0168820 * c.r + 0.1176865 * c.g + 0.8654315 * c.b;
540        let fx = if xr > EPS { xr.powf(C0) } else { C1 * xr + C2 };
541        let fy = if yr > EPS { yr.powf(C0) } else { C1 * yr + C2 };
542        let fz = if zr > EPS { zr.powf(C0) } else { C1 * zr + C2 };
543        let l = 116. * fy - 16.;
544        let a = 500. * (fx - fy);
545        let b = 200. * (fy - fz);
546        let h = { let h = b.atan2(a);
547                  if h < 0. { h + TWO_PI } else { h } };
548        Lch { l, c: a.hypot(b), h, a: c.a }
549    }
550
551    fn to_rgb(&self) -> RGBA<f64> {
552        const C0: f64 = 108. / 841.;
553        const C1: f64 = 4. / 29.;
554        let a = self.c * self.h.cos();
555        let b =  self.c * self.h.sin();
556        let fy = (self.l + 16.) / 116.;
557        let fx = a / 500. + fy;
558        let fz = fy - b / 200.;
559        let fx1 = if fx > EPS0 { fx * fx * fx } else { C0 * (fx - C1) };
560        let fy1 = if fy > EPS0 { fy * fy * fy } else { C0 * (fy - C1) };
561        let fz1 = if fz > EPS0 { fz * fz * fz } else { C0 * (fz - C1) };
562        let r = 3.0215932  * fx1 - 1.6168777 * fy1 - 0.4047152 * fz1;
563        let g = -0.9437222 * fx1 + 1.9161365 * fy1 + 0.0275856 * fz1;
564        let b = 0.0693906  * fx1 - 0.2290271 * fy1 + 1.1596365 * fz1;
565        RGBA { r, g, b, a: self.a }
566    }
567}
568
569/// Hue
570///
571pub struct Hue<Color> { color: PhantomData<Color> }
572
573impl<Color: RGBColor> ColorRange<Color> for Hue<Color> {
574    fn rgb(&self, t: f64) -> Color {
575        let t = 6. * t;
576        let f = 255. * t.fract();
577        let ti = t.trunc().rem_euclid(6.);
578        let rgba = {
579            if ti == 0.      { RGBA{ r: 255., g: f,     b: 0.,      a: 255.} }
580            else if ti == 1. { RGBA{ r: 255. - f, g: 255., b: 0.,   a: 255.} }
581            else if ti == 2. { RGBA{ r: 0.,   g: 255.,  b: f,       a: 255.} }
582            else if ti == 3. { RGBA{ r: 0.,   g: 255. - f, b: 255., a: 255.} }
583            else if ti == 4. { RGBA{ r: f,    g: 0.,    b: 255.,    a: 255.} }
584            else             { RGBA{ r: 255., g: 0.,    b: 255. - f, a: 255.} }
585        };
586        Color::from_rgba(rgba)
587    }
588}
589
590
591/// Gradient between two colors.
592///
593/// Created by [`RGBColor::gradient`].  See the [`ColorRange`] trait
594/// for methods.
595pub struct Gradient<Color> {
596    c0: Lch, // first color
597    dc: Lch, // last - fist color
598    color: PhantomData<Color>,
599}
600
601impl<Color> Gradient<Color>
602where Color: RGBColor {
603    /// Returns the color corresponding to `t` ∈ \[0., 1.\] but does
604    /// not check the later condition.
605    #[inline]
606    fn rgb_unsafe(&self, t: f64) -> Color {
607        let lhc = Lch { l: self.c0.l + t * self.dc.l,
608              c: self.c0.c + t * self.dc.c,
609              h: self.c0.h + t * self.dc.h,
610              a: self.c0.a + t * self.dc.a };
611        Color::from_rgba(lhc.to_rgb())
612    }
613}
614
615impl<Color> ColorRange<Color> for Gradient<Color>
616where Color: RGBColor {
617    /// Returns the color corresponding to `t` ∈ \[0., 1.\], where
618    /// `t == 0.` returns the first color provided in the gradient and
619    /// `t == 1.` the second.
620    fn rgb(&self, t: f64) -> Color { self.rgb_unsafe(t.clamp(0., 1.)) }
621}
622
623
624#[derive(Clone, Copy)]
625pub struct Palette<Color> {
626    palette: &'static PaletteData,
627    color: PhantomData<Color>,
628}
629
630impl<Color: RGBColor> Palette<Color> {
631    fn new(palette: &'static PaletteData) -> Self {
632        Self { palette, color: PhantomData }
633    }
634}
635
636/// # Color palettes (aka colormaps)
637///
638/// A Colormap with certain characteristics.
639impl<Color> Palette<Color>
640where Color: RGBColor {
641    /// Returns the number of colors in the palette.
642    ///
643    /// Palettes countains at least 2 colors.
644    pub fn len(&self) -> usize { self.palette.rgb.len() }
645
646    /// Says whether the palette is `Seq`uential, `Div`ergent or
647    /// `Qual`itative.
648    pub fn typ(&self) -> PaletteType { self.palette.typ }
649
650    /// Says whether the palette is colorblind safe (if the palette
651    /// specifies it).
652    pub fn blind(&self) -> Trivalent { self.palette.blind }
653
654    /// Says whether the palette is suitable for desktop color
655    /// printing.
656    pub fn print(&self) -> Trivalent { self.palette.print }
657
658    /// Says whether the palette will withstand black and white
659    /// photocopying.
660    pub fn photocopy(&self) -> Trivalent { self.palette.photocopy }
661
662    /// Says whether the palette is friendly for LCD screens (which
663    /// tend to wash-out colors).
664    pub fn lcd(&self) -> Trivalent { self.palette.lcd }
665
666    /// Returns the RGB colors of the palette.
667    pub fn colors(&self) -> Vec<Color> {
668        self.palette.rgb.iter().map(|&c| Color::from_rgba(c)).collect()
669    }
670
671    /// Returns a gradient constructed from the palette.
672    /// It only makes sense for sequential and some diverging palettes.
673    pub fn gradient(&self) -> PaletteGradient<Color> {
674        PaletteGradient {
675            gradients: self.palette.rgb.windows(2)
676                .map(|c| { let c0 = Color::from_rgba(c[0]);
677                           let c1 = Color::from_rgba(c[1]);
678                           c0.gradient(&c1) })
679                .collect() }
680    }
681}
682
683/// A gradient based on a [`Palette`].
684pub struct PaletteGradient<Color> {
685    gradients: Vec<Gradient<Color>>,
686}
687
688impl<Color> ColorRange<Color> for PaletteGradient<Color>
689where Color: RGBColor {
690    fn rgb(&self, t: f64) -> Color {
691        let n = self.gradients.len();
692        let tn = t.clamp(0., 1.) * n as f64;
693        let i = tn.trunc() as usize;
694        if i < n { self.gradients[i].rgb_unsafe(tn.fract()) }
695        else { self.gradients[n-1].rgb_unsafe(1.) }
696    }
697}
698
699/// An exact size iterator over [`Palette`]s.
700#[derive(Clone, Copy)]
701pub struct PaletteIter<Color> {
702    palettes: &'static Vec<PaletteData>,
703    i: usize, // first position to be consumed (i < j)
704    j: usize, // position after the last one to be consumed
705    color: PhantomData<Color>,
706}
707
708impl<Color: RGBColor> PaletteIter<Color> {
709    fn new(palettes: &'static Vec<PaletteData>) -> Self {
710        Self { palettes, i: 0, j: palettes.len(),
711               color: PhantomData }
712    }
713}
714
715impl<Color> Iterator for PaletteIter<Color>
716where Color: RGBColor {
717    type Item = Palette<Color>;
718
719    fn next(&mut self) -> Option<Self::Item> {
720        if self.i >= self.j { return None }
721        let x = Palette::new(&self.palettes[self.i]);
722        self.i += 1;
723        Some(x)
724    }
725
726    fn size_hint(&self) -> (usize, Option<usize>) {
727        let len = self.j - self.i;
728        (len, Some(len))
729    }
730}
731
732impl<Color: RGBColor> ExactSizeIterator for PaletteIter<Color> {}
733
734impl<Color: RGBColor> DoubleEndedIterator for PaletteIter<Color>{
735    fn next_back(&mut self) -> Option<Self::Item> {
736        if self.i >= self.j { return None }
737        self.j -= 1;
738        Some(Palette::new(&self.palettes[self.j]))
739    }
740}
741
742
743/// Set criteria to find matching palettes.
744///
745/// Created by [`RGBColor::palettes`].
746#[derive(Clone)]
747pub struct PaletteFind<Color> {
748    len: usize,
749    typ: Vec<PaletteType>,
750    blind: Trivalent,
751    print: Trivalent,
752    photocopy: Trivalent,
753    lcd: Trivalent,
754    color: PhantomData<Color>,
755}
756
757fn satisfy(prop: Trivalent, specified: Trivalent) -> bool {
758    use Trivalent::*;
759    match specified {
760        Yes => matches!(prop, Yes),
761        No => true,
762        Maybe => matches!(prop, Yes | Maybe),
763    }
764}
765
766impl<Color> PaletteFind<Color>
767where Color: RGBColor {
768    /// Find [`Palette`]s with this type.  Use several times to
769    /// specify more than one [`PaletteType`].
770    pub fn typ(mut self, t: PaletteType) -> Self {
771        self.typ.push(t);
772        self
773    }
774
775    /// Search palettes possibly ([`Trivalent::Maybe`]) or definitely
776    /// ([`Trivalent::Yes`]) suitable for color blind people.
777    pub fn blind(mut self, at_least: Trivalent) -> Self {
778        self.blind = at_least;
779        self
780    }
781
782    /// Search palettes possibly ([`Trivalent::Maybe`]) or definitely
783    /// ([`Trivalent::Yes`]) suitable for desktop color printing.
784    pub fn print(mut self, at_least: Trivalent) -> Self {
785        self.print = at_least;
786        self
787    }
788
789    /// Search palettes possibly ([`Trivalent::Maybe`]) or definitely
790    /// ([`Trivalent::Yes`]) suitable for black and white photocopying.
791    pub fn photocopy(mut self, at_least: Trivalent) -> Self {
792        self.photocopy = at_least;
793        self
794    }
795
796    /// Search palettes possibly ([`Trivalent::Maybe`]) or definitely
797    /// ([`Trivalent::Yes`]) suitable for LCD screens.
798    pub fn lcd(mut self, at_least: Trivalent) -> Self {
799        self.lcd = at_least;
800        self
801    }
802
803    /// Return an iterator on all known palettes.
804    fn all() -> impl Iterator<Item = Palette<Color>> {
805        palettes::ALL_MATPLOTLIB_PALETTES.iter().copied()
806            .chain(palettes::ALL_BREWER_PALETTES.iter()
807                   .flat_map(|v| v.iter()))
808            .map(|c| Palette::new(c))
809    }
810
811    /// Return the list of palettes of length at least `len` (and
812    /// satisfying the criteria set with other methods).
813    pub fn find(self) -> impl Iterator<Item = Palette<Color>> {
814        use PaletteType::*;
815        let typ = { if self.typ.is_empty() { vec![Seq, Div, Qual] }
816                    else { self.typ } };
817        Self::all().filter(move |p| {
818            p.len() >= self.len
819                && typ.contains(&p.palette.typ)
820                && satisfy(p.palette.blind, self.blind)
821                && satisfy(p.palette.print, self.print)
822                && satisfy(p.palette.photocopy, self.photocopy)
823                && satisfy(p.palette.lcd, self.lcd)
824        })
825    }
826}
827
828
829
830// color Blindness
831// http://vision.psychol.cam.ac.uk/jdmollon/papers/colourmaps.pdf
832// https://www.mapbox.com/blog/colorblind-simulation/
833// http://colororacle.org/ — https://github.com/nvkelso/colora-oracle-java
834
835
836
837#[cfg(test)]
838mod tests {
839    use super::*;
840
841    #[test]
842    fn hue_range() {
843        for (i, (x, c)) in RGB8::HUE.range(0., 1., 11).enumerate() {
844            assert!((x - 0.1 * i as f64).abs() <= 1e-15,
845                    "{} ≉ {}", x, 0.1 * i as f64);
846            assert_eq!(RGB8::HUE.rgb(x), c);
847        }
848    }
849}