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