• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::style::{HSLColor, RGBAColor, RGBColor};
2 
3 /// Converts scalar values to colors.
4 pub trait ColorMap<ColorType: crate::prelude::Color, FloatType = f32>
5 where
6     FloatType: num_traits::Float,
7 {
8     /// Takes a scalar value 0.0 <= h <= 1.0 and returns the corresponding color.
9     /// Typically color-scales are named according to which color-type they return.
10     /// To use upper and lower bounds with this function see [get_color_normalized](ColorMap::get_color_normalized).
get_color(&self, h: FloatType) -> ColorType11     fn get_color(&self, h: FloatType) -> ColorType {
12         self.get_color_normalized(h, FloatType::zero(), FloatType::one())
13     }
14 
15     /// 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) -> ColorType16     fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> ColorType;
17 }
18 
19 /// This struct is used to dynamically construct colormaps by giving it a slice of colors.
20 /// It can then be used when being intantiated, but not with associated functions.
21 /// ```
22 /// use plotters::prelude::{BLACK,BLUE,WHITE,DerivedColorMap,ColorMap};
23 ///
24 /// let derived_colormap = DerivedColorMap::new(
25 ///     &[BLACK,
26 ///     BLUE,
27 ///     WHITE]
28 /// );
29 ///
30 /// assert_eq!(derived_colormap.get_color(0.0), BLACK);
31 /// assert_eq!(derived_colormap.get_color(0.5), BLUE);
32 /// assert_eq!(derived_colormap.get_color(1.0), WHITE);
33 /// ```
34 pub struct DerivedColorMap<ColorType> {
35     colors: Vec<ColorType>,
36 }
37 
38 impl<ColorType: crate::style::Color + Clone> DerivedColorMap<ColorType> {
39     /// This function lets the user define a new colormap by simply specifying colors in the correct order.
40     /// For calculation of the color values, the colors will be spaced evenly apart.
new(colors: &[ColorType]) -> Self41     pub fn new(colors: &[ColorType]) -> Self {
42         DerivedColorMap {
43             colors: colors.to_vec(),
44         }
45     }
46 }
47 
48 #[macro_export]
49 #[doc(hidden)]
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 
84 // Helper function to calculate the lower and upper index nearest to a provided float value.
85 //
86 // Used to obtain colors from a colorscale given a value h between 0.0 and 1.0.
87 // It also returns the relative difference which can then be used to calculate a linear interpolation between the two nearest colors.
88 // ```
89 // # use plotters::prelude::*;
90 // let r = calculate_relative_difference_index_lower_upper(1.2, 1.0, 3.0, 4);
91 // let (relative_difference, lower_index, upper_index) = r;
92 //
93 // assert_eq!(relative_difference, 0.7000000000000001);
94 // assert_eq!(lower_index, 0);
95 // assert_eq!(upper_index, 1);
96 // ```
97 #[doc(hidden)]
calculate_relative_difference_index_lower_upper< FloatType: num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive, >( h: FloatType, min: FloatType, max: FloatType, n_steps: usize, ) -> (FloatType, usize, usize)98 pub fn calculate_relative_difference_index_lower_upper<
99     FloatType: num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive,
100 >(
101     h: FloatType,
102     min: FloatType,
103     max: FloatType,
104     n_steps: usize,
105 ) -> (FloatType, usize, usize) {
106     // Ensure that we do have a value in bounds
107     let h = num_traits::clamp(h, min, max);
108     // Next calculate a normalized value between 0.0 and 1.0
109     let t = (h - min) / (max - min);
110     let approximate_index =
111         t * (FloatType::from_usize(n_steps).unwrap() - FloatType::one()).max(FloatType::zero());
112     // Calculate which index are the two most nearest of the supplied value
113     let index_lower = approximate_index.floor().to_usize().unwrap();
114     let index_upper = approximate_index.ceil().to_usize().unwrap();
115     // Calculate the relative difference, ie. is the actual value more towards the color of index_upper or index_lower?
116     let relative_difference = approximate_index.ceil() - approximate_index;
117     (relative_difference, index_lower, index_upper)
118 }
119 
120 macro_rules! implement_color_scale_for_derived_color_map{
121     ($($color_type:ident),+) => {
122         $(
123             impl<FloatType: num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive> ColorMap<$color_type, FloatType> for DerivedColorMap<$color_type> {
124                 fn get_color_normalized(&self, h: FloatType, min: FloatType, max: FloatType) -> $color_type {
125                     let (
126                         relative_difference,
127                         index_lower,
128                         index_upper
129                     ) = calculate_relative_difference_index_lower_upper(
130                         h,
131                         min,
132                         max,
133                         self.colors.len()
134                     );
135                     // Interpolate the final color linearly
136                     $crate::calculate_new_color_value!(
137                         relative_difference,
138                         self.colors,
139                         index_upper,
140                         index_lower,
141                         $color_type
142                     )
143                 }
144             }
145         )+
146     }
147 }
148 
149 implement_color_scale_for_derived_color_map! {RGBAColor, RGBColor, HSLColor}
150 
151 #[macro_export]
152 #[doc(hidden)]
153 // Counts the number of arguments which are separated by spaces
154 //
155 // This macro is used internally to determine the size of an array to hold all new colors.
156 // ```
157 // # use plotters::count;
158 // let counted = count!{Plotting is fun};
159 // assert_eq!(counted, 3);
160 //
161 // let counted2 = count!{0_usize was my favourite 1_f64 last century};
162 // assert_eq!(counted2, 7);
163 //
164 // let new_array = ["Hello"; count!(Plotting is fun)];
165 // assert_eq!(new_array, ["Hello"; 3]);
166 // ```
167 macro_rules! count {
168     () => (0usize);
169     ($x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
170 }
171 
172 #[macro_export]
173 #[doc(hidden)]
174 /// Converts a given color identifier and a sequence of colors to an array of them.
175 macro_rules! define_colors_from_list_of_values_or_directly{
176     ($color_type:ident, $(($($color_value:expr),+)),+) => {
177         [$($color_type($($color_value),+)),+]
178     };
179     ($($color_complete:tt),+) => {
180         [$($color_complete),+]
181     };
182 }
183 
184 #[macro_export]
185 #[doc(hidden)]
186 /// Implements the [ColorMap] trait on a given color scale.
187 macro_rules! implement_linear_interpolation_color_map {
188     ($color_scale_name:ident, $color_type:ident) => {
189         impl<FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive>
190             ColorMap<$color_type, FloatType> for $color_scale_name
191         {
192             fn get_color_normalized(
193                 &self,
194                 h: FloatType,
195                 min: FloatType,
196                 max: FloatType,
197             ) -> $color_type {
198                 let (
199                     relative_difference,
200                     index_lower,
201                     index_upper
202                 ) = calculate_relative_difference_index_lower_upper(
203                     h,
204                     min,
205                     max,
206                     Self::COLORS.len()
207                 );
208                 // Interpolate the final color linearly
209                 $crate::calculate_new_color_value!(
210                     relative_difference,
211                     Self::COLORS,
212                     index_upper,
213                     index_lower,
214                     $color_type
215                 )
216             }
217         }
218 
219         impl $color_scale_name {
220             #[doc = "Get color value from `"]
221             #[doc = stringify!($color_scale_name)]
222             #[doc = "` by supplying a parameter 0.0 <= h <= 1.0"]
223             pub fn get_color<FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive>(
224                 h: FloatType,
225             ) -> $color_type {
226                 let color_scale = $color_scale_name {};
227                 color_scale.get_color(h)
228             }
229 
230             #[doc = "Get color value from `"]
231             #[doc = stringify!($color_scale_name)]
232             #[doc = "` by supplying lower and upper bounds min, max and a parameter h where min <= h <= max"]
233             pub fn get_color_normalized<
234                 FloatType: std::fmt::Debug + num_traits::Float + num_traits::FromPrimitive + num_traits::ToPrimitive,
235             >(
236                 h: FloatType,
237                 min: FloatType,
238                 max: FloatType,
239             ) -> $color_type {
240                 let color_scale = $color_scale_name {};
241                 color_scale.get_color_normalized(h, min, max)
242             }
243         }
244     };
245 }
246 
247 #[doc(inline)]
248 pub use crate::def_linear_colormap;
249 
250 #[macro_export]
251 #[doc(hidden)]
252 /// Create a new colormap with evenly spaced colors and interpolates between them automatically.
253 ///
254 /// This macro works by taking a identifier (name) for the colormap, the type of color to specify, a
255 /// docstring and a list of colors and constructs an empty struct on which it implements the [ColorMap] trait.
256 ///
257 /// ```
258 /// use plotters::prelude::*;
259 /// def_linear_colormap! {
260 ///     BlackWhite,
261 ///     RGBColor,
262 ///     "Simple chromatic colormap from black to white.",
263 ///     (  0,   0,   0),
264 ///     (255, 255,   255)
265 /// }
266 ///
267 /// assert_eq!(BlackWhite::get_color(0.0), RGBColor(0,0,0));
268 /// ```
269 ///
270 /// Hint: Some helper macros and functions have been deliberately hidden from end users.
271 /// Look for them in the source code if you are interested.
272 macro_rules! def_linear_colormap{
273     ($color_scale_name:ident, $color_type:ident, $doc:expr, $(($($color_value:expr),+)),*) => {
274         #[doc = $doc]
275         pub struct $color_scale_name;
276 
277         impl $color_scale_name {
278             // const COLORS: [$color_type; $number_colors] = [$($color_type($($color_value),+)),+];
279             // const COLORS: [$color_type; $crate::count!($(($($color_value:expr),+))*)] = [$($color_type($($color_value),+)),+];
280             const COLORS: [$color_type; $crate::count!($(($($color_value:expr),+))*)] = $crate::define_colors_from_list_of_values_or_directly!{$color_type, $(($($color_value),+)),*};
281         }
282 
283         $crate::implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
284     };
285     ($color_scale_name:ident, $color_type:ident, $doc:expr, $($color_complete:tt),+) => {
286         #[doc = $doc]
287         pub struct $color_scale_name;
288 
289         impl $color_scale_name {
290             const COLORS: [$color_type; $crate::count!($($color_complete)*)] = $crate::define_colors_from_list_of_values_or_directly!{$($color_complete),+};
291         }
292 
293         $crate::implement_linear_interpolation_color_map!{$color_scale_name, $color_type}
294     }
295 }
296 
297 def_linear_colormap! {
298     ViridisRGBA,
299     RGBAColor,
300     "A colormap optimized for visually impaired people (RGBA format).
301     It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html).
302     Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)",
303     ( 68,   1,  84, 1.0),
304     ( 70,  50, 127, 1.0),
305     ( 54,  92, 141, 1.0),
306     ( 39, 127, 143, 1.0),
307     ( 31, 162, 136, 1.0),
308     ( 74, 194, 110, 1.0),
309     (160, 219,  57, 1.0),
310     (254, 232,  37, 1.0)
311 }
312 
313 def_linear_colormap! {
314     ViridisRGB,
315     RGBColor,
316     "A colormap optimized for visually impaired people (RGB Format).
317     It is currently the default colormap also used by [matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html).
318     Read more in this [paper](https://doi.org/10.1371/journal.pone.0199239)",
319     ( 68,   1,  84),
320     ( 70,  50, 127),
321     ( 54,  92, 141),
322     ( 39, 127, 143),
323     ( 31, 162, 136),
324     ( 74, 194, 110),
325     (160, 219,  57),
326     (254, 232,  37)
327 }
328 
329 def_linear_colormap! {
330     BlackWhite,
331     RGBColor,
332     "Simple chromatic colormap from black to white.",
333     (  0,   0,   0),
334     (255, 255,   255)
335 }
336 
337 def_linear_colormap! {
338     MandelbrotHSL,
339     HSLColor,
340     "Colormap created to replace the one used in the mandelbrot example.",
341     (0.0, 1.0, 0.5),
342     (1.0, 1.0, 0.5)
343 }
344 
345 def_linear_colormap! {
346     VulcanoHSL,
347     HSLColor,
348     "A vulcanic colormap that display red/orange and black colors",
349     (2.0/3.0, 1.0, 0.7),
350     (    0.0, 1.0, 0.7)
351 }
352 
353 use super::full_palette::*;
354 def_linear_colormap! {
355     Bone,
356     RGBColor,
357     "Dark colormap going from black over blue to white.",
358     BLACK,
359     BLUE,
360     WHITE
361 }
362 
363 def_linear_colormap! {
364     Copper,
365     RGBColor,
366     "Friendly black to brown colormap.",
367     BLACK,
368     BROWN,
369     ORANGE
370 }
371