plotters_unsable/style/
color.rs

1use super::palette::Palette;
2/// The abstraction of a color
3use std::marker::PhantomData;
4
5/// Any color representation
6pub trait Color {
7    /// Convert the RGB representation to the standard RGB tuple
8    fn rgb(&self) -> (u8, u8, u8);
9
10    /// Get the alpha channel of the color
11    fn alpha(&self) -> f64;
12}
13
14/// The trait for any color that can composite with other color
15pub trait Mixable: Color {
16    /// Introduce alpha channel to the color
17    fn mix(&self, alpha: f64) -> CompsitableColor<Self> {
18        CompsitableColor(self, alpha)
19    }
20}
21
22impl<T: Color + Sized> Mixable for T {}
23
24impl Color for Box<&dyn Color> {
25    fn rgb(&self) -> (u8, u8, u8) {
26        self.as_ref().rgb()
27    }
28
29    fn alpha(&self) -> f64 {
30        self.as_ref().alpha()
31    }
32}
33
34/// Color without alpha channel
35pub trait SimpleColor {
36    fn rgb(&self) -> (u8, u8, u8);
37}
38
39impl<T: SimpleColor> Color for T {
40    fn rgb(&self) -> (u8, u8, u8) {
41        SimpleColor::rgb(self)
42    }
43
44    fn alpha(&self) -> f64 {
45        1.0
46    }
47}
48
49/// A color in the given palette
50pub struct PaletteColor<P: Palette>(usize, PhantomData<P>);
51
52impl<P: Palette> PaletteColor<P> {
53    /// Pick a color from the palette
54    pub fn pick(idx: usize) -> PaletteColor<P> {
55        PaletteColor(idx % P::COLORS.len(), PhantomData)
56    }
57}
58
59impl<P: Palette> SimpleColor for PaletteColor<P> {
60    fn rgb(&self) -> (u8, u8, u8) {
61        P::COLORS[self.0]
62    }
63}
64
65/// Simple color with additional alpha channel
66pub struct CompsitableColor<'a, T: Color + ?Sized>(&'a T, f64);
67
68impl<'a, T: Color> Color for CompsitableColor<'a, T> {
69    fn rgb(&self) -> (u8, u8, u8) {
70        (self.0).rgb()
71    }
72
73    fn alpha(&self) -> f64 {
74        (self.0).alpha() * self.1
75    }
76}
77
78/// The color described by it's RGB value
79pub struct RGBColor(pub u8, pub u8, pub u8);
80
81impl SimpleColor for RGBColor {
82    fn rgb(&self) -> (u8, u8, u8) {
83        (self.0, self.1, self.2)
84    }
85}
86
87macro_rules! predefined_color {
88    ($name:ident, $r:expr, $g:expr, $b:expr, $doc: expr) => {
89        #[doc = $doc]
90        pub struct $name;
91        impl SimpleColor for $name {
92            fn rgb(&self) -> (u8,u8,u8) {
93                return ($r, $g, $b);
94            }
95        }
96    };
97
98    ($name:ident, $r:expr, $g:expr, $b:expr, $a: expr, $doc: expr) => {
99        #[doc = $doc]
100        pub struct $name;
101        impl Color for $name {
102            fn rgb(&self) -> (u8,u8,u8) {
103                return ($r, $g, $b);
104            }
105            fn alpha(&self) -> f64 {
106                $a
107            }
108        }
109    }
110}
111
112predefined_color!(White, 255, 255, 255, "The predefined white color");
113predefined_color!(Black, 0, 0, 0, "The predefined black color");
114predefined_color!(Red, 255, 0, 0, "The predefined red color");
115predefined_color!(Green, 0, 255, 0, "The predefined green color");
116predefined_color!(Blue, 0, 0, 255, "The predefined blue color");
117predefined_color!(Yellow, 255, 255, 0, "The predefined yellow color");
118predefined_color!(Cyan, 0, 255, 255, "The predefined cyan color");
119predefined_color!(Magenta, 255, 0, 255, "The predefined magenta color");
120predefined_color!(Transparent, 0, 0, 0, 0.0, "The predefined transparent");
121
122/// The color described by HSL color space
123pub struct HSLColor(pub f64, pub f64, pub f64);
124
125impl SimpleColor for HSLColor {
126    #[allow(clippy::many_single_char_names)]
127    fn rgb(&self) -> (u8, u8, u8) {
128        let (h, s, l) = (
129            self.0.min(1.0).max(0.0),
130            self.1.min(1.0).max(0.0),
131            self.2.min(1.0).max(0.0),
132        );
133
134        if s == 0.0 {
135            let value = (l * 255.0).round() as u8;
136            return (value, value, value);
137        }
138
139        let q = if l < 0.5 {
140            l * (1.0 + s)
141        } else {
142            l + s - l * s
143        };
144        let p = 2.0 * l - q;
145
146        let cvt = |mut t| {
147            if t < 0.0 {
148                t += 1.0;
149            }
150            if t > 1.0 {
151                t -= 1.0;
152            }
153            let value = if t < 1.0 / 6.0 {
154                p + (q - p) * 6.0 * t
155            } else if t < 1.0 / 2.0 {
156                q
157            } else if t < 2.0 / 3.0 {
158                p + (q - p) * (2.0 / 3.0 - t) * 6.0
159            } else {
160                p
161            };
162            (value * 255.0).round() as u8
163        };
164
165        (cvt(h + 1.0 / 3.0), cvt(h), cvt(h - 1.0 / 3.0))
166    }
167}