1 use crate::style::{HSLColor, RGBAColor, RGBColor};
2 
3 use num_traits::{Float, FromPrimitive, ToPrimitive};
4 
5 /// Converts scalar values to colors.
6 pub trait ColorMap<ColorType: crate::prelude::Color, FloatType = f32>
7 where
8     FloatType: Float,
9 {
10     /// Takes a scalar value 0.0 <= h <= 1.0 and returns the corresponding color.
11     /// Typically color-scales are named according to which color-type they return.
12     /// To use upper and lower bounds with ths function see [get_color_normalized](ColorMap::get_color_normalized).
get_color(&self, h: FloatType) -> ColorType13     fn get_color(&self, h: FloatType) -> ColorType {
14         self.get_color_normalized(h, FloatType::zero(), FloatType::one())
15     }
16 
17     /// A slight abstraction over [get_color](ColorMap::get_color) function where lower and upper bound can be specified.
get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> ColorType18     fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> ColorType;
19 }
20 
21 /// This struct is used to dynamically construct colormaps by giving it a slice of colors.
22 /// It can then be used when being intantiated, but not with associated functions.
23 /// ```
24 /// use plotters::prelude::{BLACK,BLUE,WHITE,DerivedColorMap,ColorMap};
25 ///
26 /// let derived_colormap = DerivedColorMap::new(
27 ///     &[BLACK,
28 ///     BLUE,
29 ///     WHITE]
30 /// );
31 ///
32 /// assert_eq!(derived_colormap.get_color(0.0), BLACK);
33 /// assert_eq!(derived_colormap.get_color(0.5), BLUE);
34 /// assert_eq!(derived_colormap.get_color(1.0), WHITE);
35 /// ```
36 pub struct DerivedColorMap<ColorType> {
37     colors: Vec<ColorType>,
38 }
39 
40 impl<ColorType: crate::style::Color + Clone> DerivedColorMap<ColorType> {
41     /// This function lets the user define a new colormap by simply specifying colors in the correct order.
42     /// For calculation of the color values, the colors will be spaced evenly apart.
new(colors: &[ColorType]) -> Self43     pub fn new(colors: &[ColorType]) -> Self {
44         DerivedColorMap {
45             colors: colors.to_vec(),
46         }
47     }
48 }
49 
50 macro_rules! calculate_new_color_value(
51     ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, RGBColor) => {
52         RGBColor(
53             // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values
54             // In principle every cast should be safe which is why we choose to unwrap
55             //           (1.0  - r)                   *                                        color_value_1  +                    r *                                       color_value_2
56             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].0).unwrap()).round().to_u8().unwrap(),
57             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].1).unwrap()).round().to_u8().unwrap(),
58             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].2).unwrap()).round().to_u8().unwrap()
59         )
60     };
61     ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, RGBAColor) => {
62         RGBAColor(
63             // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values
64             // In principle every cast should be safe which is why we choose to unwrap
65             //           (1.0  - r)                   *                                        color_value_1  +                    r *                                       color_value_2
66             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].0).unwrap()).round().to_u8().unwrap(),
67             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].1).unwrap()).round().to_u8().unwrap(),
68             ((FloatType::one() - $relative_difference) * FloatType::from_u8($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_u8($colors[$index_lower].2).unwrap()).round().to_u8().unwrap(),
69             ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].3).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].3).unwrap()).to_f64().unwrap()
70         )
71     };
72     ($relative_difference:expr, $colors:expr, $index_upper:expr, $index_lower:expr, HSLColor) => {
73         HSLColor(
74             // These equations are a very complicated way of writing a simple linear extrapolation with lots of casting between numerical values
75             // In principle every cast should be safe which is why we choose to unwrap
76             //           (1.0  - r)                   *                                         color_value_1  +                    r *                                        color_value_2
77             ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].0).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].0).unwrap()).to_f64().unwrap(),
78             ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].1).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].1).unwrap()).to_f64().unwrap(),
79             ((FloatType::one() - $relative_difference) * FloatType::from_f64($colors[$index_upper].2).unwrap() + $relative_difference * FloatType::from_f64($colors[$index_lower].2).unwrap()).to_f64().unwrap(),
80         )
81     };
82 );
83 
calculate_relative_difference_index_lower_upper< FloatType: Float + FromPrimitive + ToPrimitive, >( h: FloatType, min: FloatType, max: FloatType, n_colors: usize, ) -> (FloatType, usize, usize)84 fn calculate_relative_difference_index_lower_upper<
85     FloatType: Float + FromPrimitive + ToPrimitive,
86 >(
87     h: FloatType,
88     min: FloatType,
89     max: FloatType,
90     n_colors: usize,
91 ) -> (FloatType, usize, usize) {
92     // Ensure that we do have a value in bounds
93     let h = num_traits::clamp(h, min, max);
94     // Next calculate a normalized value between 0.0 and 1.0
95     let t = (h - min) / (max - min);
96     let approximate_index =
97         t * (FloatType::from_usize(n_colors).unwrap() - FloatType::one()).max(FloatType::zero());
98     // Calculate which index are the two most nearest of the supplied value
99     let index_lower = approximate_index.floor().to_usize().unwrap();
100     let index_upper = approximate_index.ceil().to_usize().unwrap();
101     // Calculate the relative difference, ie. is the actual value more towards the color of index_upper or index_lower?
102     let relative_difference = approximate_index.ceil() - approximate_index;
103     (relative_difference, index_lower, index_upper)
104 }
105 
106 macro_rules! implement_color_scale_for_derived_color_map{
107     ($($color_type:ident),+) => {
108         $(
109             impl<FloatType: Float + FromPrimitive + ToPrimitive> ColorMap<$color_type, FloatType> for DerivedColorMap<$color_type> {
110                 fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> $color_type {
111                     let (
112                         relative_difference,
113                         index_lower,
114                         index_upper
115                     ) = calculate_relative_difference_index_lower_upper(
116                         h,
117                         min,
118                         max,
119                         self.colors.len()
120                     );
121                     // Interpolate the final color linearly
122                     calculate_new_color_value!(
123                         relative_difference,
124                         self.colors,
125                         index_upper,
126                         index_lower,
127                         $color_type
128                     )
129                 }
130             }
131         )+
132     }
133 }
134 
135 implement_color_scale_for_derived_color_map! {RGBAColor, RGBColor, HSLColor}
136 
137 macro_rules! count {
138     () => (0usize);
139     ($x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
140 }
141 
142 macro_rules! define_colors_from_list_of_values_or_directly{
143     ($color_type:ident, $(($($color_value:expr),+)),+) => {
144         [$($color_type($($color_value),+)),+]
145     };
146     ($($color_complete:tt),+) => {
147         [$($color_complete),+]
148     };
149 }
150 
151 macro_rules! implement_linear_interpolation_color_map {
152     ($color_scale_name:ident, $color_type:ident) => {
153         impl<FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive>
154             ColorMap<$color_type, FloatType> for $color_scale_name
155         {
156             fn get_color_normalized(
157                 &self,
158                 h: FloatType,
159                 min: FloatType,
160                 max: FloatType,
161             ) -> $color_type {
162                 let (
163                     relative_difference,
164                     index_lower,
165                     index_upper
166                 ) = calculate_relative_difference_index_lower_upper(
167                     h,
168                     min,
169                     max,
170                     Self::COLORS.len()
171                 );
172                 // Interpolate the final color linearly
173                 calculate_new_color_value!(
174                     relative_difference,
175                     Self::COLORS,
176                     index_upper,
177                     index_lower,
178                     $color_type
179                 )
180             }
181         }
182 
183         impl $color_scale_name {
184             #[doc = "Get color value from `"]
185             #[doc = stringify!($color_scale_name)]
186             #[doc = "` by supplying a parameter 0.0 <= h <= 1.0"]
187             pub fn get_color<FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive>(
188                 h: FloatType,
189             ) -> $color_type {
190                 let color_scale = $color_scale_name {};
191                 color_scale.get_color(h)
192             }
193 
194             #[doc = "Get color value from `"]
195             #[doc = stringify!($color_scale_name)]
196             #[doc = "` by supplying lower and upper bounds min, max and a parameter h where min <= h <= max"]
197             pub fn get_color_normalized<
198                 FloatType: std::fmt::Debug + Float + FromPrimitive + ToPrimitive,
199             >(
200                 h: FloatType,
201                 min: FloatType,
202                 max: FloatType,
203             ) -> $color_type {
204                 let color_scale = $color_scale_name {};
205                 color_scale.get_color_normalized(h, min, max)
206             }
207         }
208     };
209 }
210 
211 #[macro_export]
212 /// Macro to create a new colormap with evenly spaced colors at compile-time.
213 macro_rules! define_linear_interpolation_color_map{
214     ($color_scale_name:ident, $color_type:ident, $doc:expr, $(($($color_value:expr),+)),*) => {
215         #[doc = $doc]
216         pub struct $color_scale_name {}
217 
218         impl $color_scale_name {
219             // const COLORS: [$color_type; $number_colors] = [$($color_type($($color_value),+)),+];
220             // const COLORS: [$color_type; count!($(($($color_value:expr),+))*)] = [$($color_type($($color_value),+)),+];
221             const COLORS: [$color_type; count!($(($($color_value:expr),+))*)] = define_colors_from_list_of_values_or_directly!{$color_type, $(($($color_value),+)),*};
222         }
223 
224         implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
225     };
226     ($color_scale_name:ident, $color_type:ident, $doc:expr, $($color_complete:tt),+) => {
227         #[doc = $doc]
228         pub struct $color_scale_name {}
229 
230         impl $color_scale_name {
231             const COLORS: [$color_type; count!($($color_complete)*)] = define_colors_from_list_of_values_or_directly!{$($color_complete),+};
232         }
233 
234         implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
235     }
236 }
237 
238 define_linear_interpolation_color_map! {
239     ViridisRGBA,
240     RGBAColor,
241     "A colormap optimized for visually impaired people (RGBA format).
242     It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html).
243     Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)",
244     ( 68,   1,  84, 1.0),
245     ( 70,  50, 127, 1.0),
246     ( 54,  92, 141, 1.0),
247     ( 39, 127, 143, 1.0),
248     ( 31, 162, 136, 1.0),
249     ( 74, 194, 110, 1.0),
250     (160, 219,  57, 1.0),
251     (254, 232,  37, 1.0)
252 }
253 
254 define_linear_interpolation_color_map! {
255     ViridisRGB,
256     RGBColor,
257     "A colormap optimized for visually impaired people (RGB Format).
258     It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html).
259     Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)",
260     ( 68,   1,  84),
261     ( 70,  50, 127),
262     ( 54,  92, 141),
263     ( 39, 127, 143),
264     ( 31, 162, 136),
265     ( 74, 194, 110),
266     (160, 219,  57),
267     (254, 232,  37)
268 }
269 
270 define_linear_interpolation_color_map! {
271     BlackWhite,
272     RGBColor,
273     "Simple chromatic colormap from black to white.",
274     (  0,   0,   0),
275     (255, 255,   255)
276 }
277 
278 define_linear_interpolation_color_map! {
279     MandelbrotHSL,
280     HSLColor,
281     "Colormap created to replace the one used in the mandelbrot example.",
282     (0.0, 1.0, 0.5),
283     (1.0, 1.0, 0.5)
284 }
285 
286 define_linear_interpolation_color_map! {
287     VulcanoHSL,
288     HSLColor,
289     "A vulcanic colormap that display red/orange and black colors",
290     (2.0/3.0, 1.0, 0.7),
291     (    0.0, 1.0, 0.7)
292 }
293 
294 use super::full_palette::*;
295 define_linear_interpolation_color_map! {
296     Bone,
297     RGBColor,
298     "Dark colormap going from black over blue to white.",
299     BLACK,
300     BLUE,
301     WHITE
302 }
303 
304 define_linear_interpolation_color_map! {
305     Copper,
306     RGBColor,
307     "Friendly black to brown colormap.",
308     BLACK,
309     BROWN,
310     ORANGE
311 }
312