gnuplot/
color.rs1pub use self::ColorType::*;
2use crate::util::OneWayOwned;
3use std::fmt::{Debug, Display};
4
5pub type ColorIndex = u8;
6pub type ColorComponent = u8;
7pub type ColorInt = u32;
8pub type RGBInts = (ColorComponent, ColorComponent, ColorComponent);
9pub type ARGBInts = (
10 ColorComponent,
11 ColorComponent,
12 ColorComponent,
13 ColorComponent,
14);
15
16/// Option type (for plots, borders, and text) that allows the various different gnuplot
17/// color formats. The gnuplot [colorspec reference](http://gnuplot.info/docs_6.0/loc3640.html)
18/// also explains these.
19///
20/// **NOTE**: Gnuplot interprets the alpha channel in an unusual way, where 0 is fully opaque and
21/// 255 is fully transparent. This wrapper inverts the meaning (as described below) to match the
22/// more common interpretation.
23///
24/// There are many equivalent ways of specifying colors, and this allows the user to chose the most convenient.
25/// For example, all the following will produce the same blue color:
26/// `RGBColor("blue".into())`, `RGBColor("0x0000ff".into())`, `RGBColor("#0000ff".into())`, `RGBColor("0xff0000ff".into())`,
27/// `RGBColor("#ff0000ff".into())`, `RGBIntegerColor(0, 0, 255)`, `ARGBColor(255, 0, 0, 255)`,
28///
29/// See example usages of these colors in `color.rs` and `variable_color.rs` in the
30/// [Examples folder](https://github.com/SiegeLord/RustGnuplot/tree/master/gnuplot/examples) on Github
31#[derive(Debug, Clone, PartialEq, PartialOrd)]
32pub enum ColorType<T = String>
33{
34 /// string (`&str` or `String`, but usually created with `&str`) in one of the following gnuplot-supported formats
35 /// - colorname --- e.g. "blue" [See the gnuplot
36 /// [list of colornames](http://gnuplot.info/docs_6.0/loc11229.html)]
37 /// - 0xRRGGBB --- string containing hexadecimal constant
38 /// - 0xAARRGGBB --- string containing hexadecimal constant
39 /// - #RRGGBB --- string containing hexadecimal in x11 format
40 /// - #AARRGGBB --- string containing hexadecimal in x11 format
41 ///
42 /// "#AARRGGBB" represents an RGB color with an alpha channel value in the high bits.
43 /// An alpha value of 255 (FF) represents a fully opaque color; i.e., "#FFRRGGBB" is the same as "#RRGGBB".
44 /// An alpha value of 0 represents full transparency.
45 RGBString(T),
46 /// tuple of u8 representing red, green and blue values as 0-255
47 RGBInteger(ColorComponent, ColorComponent, ColorComponent),
48 /// tuple of u8 representing alpha, red, green and blue values as 0-255.
49 /// As with `RGBColor`, an alpha value of 255 (FF) represents a fully opaque color;
50 /// an alpha value of 0 represents full transparency.
51 ARGBInteger(
52 ColorComponent,
53 ColorComponent,
54 ColorComponent,
55 ColorComponent,
56 ),
57 /// Vector of tuples of `u8` (as per `RGBColor`), but instead of a single color for the whole
58 /// plot, the vector should contain a separte color for each data point.
59 VariableRGBInteger(Vec<RGBInts>),
60 /// Vector of tuples of `u8` (as per `ARGBColor`), but as with `VariableRGBColor`, a separate
61 /// color value is given for each data point.
62 VariableARGBInteger(Vec<ARGBInts>),
63 /// Sets the color of the plot element to a value picked from the current palette (see
64 /// [set_palette()](crate::AxesCommon::set_palette())). The value supplied to this color type
65 /// selects the color within the color range of the palette: i.e. it if the color bar range had been
66 /// set with `ax.set_cb_range(Fix(min), Fix(max))`, the value would be expected to be between
67 /// `min` and `max`.
68 ///
69 /// Example of usage is give in the `color` example.
70 ///
71 /// Compare with [PaletteFracColor]
72 PaletteFracColor(f64),
73 /// Sets the color of the plot element to a value picked from the current palette (see
74 /// [set_palette()](crate::AxesCommon::set_palette()) . The value supplied to this color type
75 /// selects the color as a fraction of the current color range i.e. it is expected to be
76 /// between `0` and `1`.
77 ///
78 /// Example of usage is give in the `color` example.
79 ///
80 /// Comparing with [PaletteCBColor]: given the following code
81 /// ```
82 /// use gnuplot::{PaletteCBColor, PaletteFracColor, Fix, Figure, AxesCommon, Color};
83 ///# let min = -5.0; // or any value
84 ///# let max = 12.0; // or any value
85 ///
86 ///# let frac = 0.5; // or any value 0.0 <= frac <= 1.0
87 ///# let x = [1,2,3];
88 ///# let y = [4,5,6];
89 /// assert!(frac >= 0.0);
90 /// assert!(frac <= 1.0);
91 ///
92 /// let mut fg = Figure::new();
93 /// let ax = fg.axes2d();
94 /// ax.set_cb_range(Fix(min), Fix(max));
95 /// let col1 = Color(PaletteFracColor(frac));
96 /// let cb_range = max - min;
97 /// let col2 = Color(PaletteCBColor(min + (frac * cb_range)));
98 /// ax.lines(x, y, &[col1]);
99 /// ax.lines(x, y, &[col2]);
100 /// ```
101 /// the two lines should give the same color for any values of `max` and `min`, and `0 <= frac <= 1`.
102 PaletteCBColor(f64),
103 /// Vector of `f64` values which act as indexes into the current palette to set the color of
104 /// each data point. These variable values work in the same was as the single fixed value supplied
105 /// to a [PaletteCBColor]
106 VariablePaletteColor(Vec<f64>),
107 /// Similar to `VariablePaletteColor` in that it takes a `Vec<f64>` to set the indexes into the
108 /// color map for each data point, but in addition to the color data it takes a string hold the name
109 /// of the colormap to use. This should have been previously created in the workspace using the
110 /// [create_colormap()](crate::AxesCommon::create_colormap) function.
111 SavedColorMap(T, Vec<f64>),
112 /// Set the color of all elements of the plot to the `n`th color in the current gnuplot color cycle.
113 Index(ColorIndex),
114 /// A color type that sets the color per element using a index `n` which represents the `n`th
115 /// color in the current gnuplot color scheme. In gnuplot this is the last element in the plot command,
116 /// in Rust gnuplot, the color type takes a vector of u8, where each index is treated the same as the
117 /// fixed `IndexColor`.
118 /// This is useful for setting bars/boxes etc to be
119 /// the same color from multiple plot commands. The `variable_color` example has examples of this usage.
120 VariableIndex(Vec<ColorIndex>),
121 /// Set the color of the plot to the current background color.
122 Background,
123 /// Fixed black color
124 Black,
125}
126
127impl<T: Display + Debug> ColorType<T>
128{
129 /// Returns the gnuplot string that will produce the requested color
130 pub fn command(&self) -> String
131 {
132 match self
133 {
134 RGBString(s) => format!(r#"rgb "{}""#, from_string(s.to_string())),
135 RGBInteger(r, g, b) => format!(r#"rgb {}"#, from_argb(255, *r, *g, *b)),
136 ARGBInteger(a, r, g, b) => format!(r#"rgb {}"#, from_argb(*a, *r, *g, *b)),
137 VariableRGBInteger(_) => "rgb variable".into(),
138 VariableARGBInteger(_) => "rgb variable".into(),
139 PaletteFracColor(v) => format!("palette frac {v}"),
140 PaletteCBColor(v) => format!("palette cb {v}"),
141 VariablePaletteColor(_) => "palette z".into(),
142 SavedColorMap(s, _) => format!("palette {s}"),
143 VariableIndex(_) => "variable".into(),
144 Background => "bgnd".into(),
145 Index(n) => format!("{}", n),
146 Black => "black".into(),
147 }
148 }
149
150 pub fn data(&self) -> Vec<f64>
151 {
152 match self
153 {
154 VariableRGBInteger(items) => items
155 .iter()
156 .map(|(r, g, b)| from_argb(255, *r, *g, *b) as f64)
157 .collect(),
158 VariableARGBInteger(items) => items
159 .iter()
160 .map(|(a, r, g, b)| from_argb(*a, *r, *g, *b) as f64)
161 .collect(),
162 VariablePaletteColor(items) => items.clone(),
163 SavedColorMap(_, items) => items.clone(),
164 VariableIndex(items) => items.iter().map(|v| *v as f64).collect(),
165 c => panic!("data() called on non-variable color type: {:?}", *c),
166 }
167 }
168
169 pub fn is_variable(&self) -> bool
170 {
171 matches!(
172 self,
173 VariableRGBInteger(_)
174 | VariableARGBInteger(_)
175 | VariableIndex(_)
176 | VariablePaletteColor(_)
177 | SavedColorMap(_, _)
178 )
179 }
180
181 pub fn has_alpha(&self) -> bool
182 {
183 match self
184 {
185 RGBString(s) =>
186 {
187 let s = s.to_string();
188 s.starts_with("0x") && s.len() == 10 || s.starts_with("#") && s.len() == 9
189 }
190 ARGBInteger(_, _, _, _) | VariableARGBInteger(_) => true,
191 _ => false,
192 }
193 }
194}
195
196fn from_argb(a: ColorComponent, r: ColorComponent, g: ColorComponent, b: ColorComponent)
197 -> ColorInt
198{
199 ((255 - a as ColorInt) << 24)
200 + ((r as ColorInt) << 16)
201 + ((g as ColorInt) << 8)
202 + (b as ColorInt)
203}
204
205fn from_string(argb: String) -> String
206{
207 if let Some(trimmed_argb) = argb.strip_prefix("0x").or_else(|| argb.strip_prefix("#"))
208 {
209 if trimmed_argb.len() == 8
210 {
211 if let Ok(argb_int) = ColorInt::from_str_radix(trimmed_argb, 16)
212 {
213 let a = 255 - ((argb_int >> 24) & 0xff);
214 let argb_int = (a << 24) + (argb_int & 0xffffff);
215 format!("#{:08x}", argb_int)
216 }
217 else
218 {
219 // Let gnuplot sort it out.
220 argb
221 }
222 }
223 else
224 {
225 argb
226 }
227 }
228 else
229 {
230 argb
231 }
232}
233
234fn float_color_to_int(v: f64) -> Result<u8, String>
235{
236 if !(0.0..=1.0).contains(&v)
237 {
238 Err(format!(
239 "Float value must be greater than zero and less than one. Actual value: {}",
240 v
241 ))
242 }
243 else
244 {
245 Ok(((v * 255.0).round()) as u8)
246 }
247}
248
249/// Converts a set of `f64` red, green and blue values in the range `0 <= x <= 1` to a 3-tuple of `u8` suitable for use as
250/// an [RGBInteger]
251///
252/// Returns an error String if any of the arguments are not in the range `0 <= x <= 1`
253///
254/// Ses also [floats_to_argb]
255///
256/// # Arguments
257/// * r - red. 0: no red, 1: fully red
258/// * g - green. 0: no green, 1: fully green
259/// * b - blue. 0: no blue, 1: fully blue
260fn floats_to_rgb(r: f64, g: f64, b: f64) -> Result<RGBInts, String>
261{
262 Ok((
263 float_color_to_int(r)?,
264 float_color_to_int(g)?,
265 float_color_to_int(b)?,
266 ))
267}
268
269/// Converts a set of `f64` red, green and blue values in the range `0 <= x <= 1` to a 3-tuple of `u8` suitable for use as
270/// an [ARGBInteger]
271///
272/// Returns an error String if any of the arguments are not in the range `0 <= x <= 1`
273///
274/// Ses also [floats_to_rgb]
275///
276/// # Arguments
277/// * a - alpha (transparency) value. 0: completely opaque, 1: completely transparent.
278/// * r - red. 0: no red, 1: fully red
279/// * g - green. 0: no green, 1: fully green
280/// * b - blue. 0: no blue, 1: fully blue
281fn floats_to_argb(a: f64, r: f64, g: f64, b: f64) -> Result<ARGBInts, String>
282{
283 Ok((
284 float_color_to_int(a)?,
285 float_color_to_int(r)?,
286 float_color_to_int(g)?,
287 float_color_to_int(b)?,
288 ))
289}
290
291impl<'l> From<&'l str> for ColorType<String>
292{
293 /// Converts `&str` into [RGBString]
294 fn from(value: &'l str) -> Self
295 {
296 ColorType::RGBString(String::from(value))
297 }
298}
299
300impl<'l> From<String> for ColorType<String>
301{
302 /// Converts `String` into [RGBString]
303 fn from(value: String) -> Self
304 {
305 ColorType::RGBString(value)
306 }
307}
308
309impl<'l> From<&'l str> for ColorType<&'l str>
310{
311 /// Converts `&str` into [RGBString]
312 fn from(value: &'l str) -> Self
313 {
314 ColorType::RGBString(value)
315 }
316}
317
318impl<T> From<ARGBInts> for ColorType<T>
319{
320 /// Converts `(u8, u8, u8, u8)` into [ARGBInteger]
321 fn from(value: ARGBInts) -> Self
322 {
323 ColorType::ARGBInteger(value.0, value.1, value.2, value.3)
324 }
325}
326
327impl<T> From<RGBInts> for ColorType<T>
328{
329 /// Converts `(u8, u8, u8)` into [RGBInteger]
330 fn from(value: RGBInts) -> Self
331 {
332 ColorType::RGBInteger(value.0, value.1, value.2)
333 }
334}
335
336impl<T> TryFrom<(f64, f64, f64)> for ColorType<T>
337{
338 type Error = String;
339 /// Converts `(f64, f64, f64)` into [RGBInteger].
340 /// Returns an error unless all values are in the range `0 <= v <= 1`.
341 fn try_from(value: (f64, f64, f64)) -> Result<Self, Self::Error>
342 {
343 let ints = floats_to_rgb(value.0, value.1, value.2)?;
344 Ok(ColorType::RGBInteger(ints.0, ints.1, ints.2))
345 }
346}
347
348impl<T> TryFrom<(f64, f64, f64, f64)> for ColorType<T>
349{
350 type Error = String;
351 /// Converts `(f64, f64, f64, f64)` into [ARGBInteger].
352 /// Returns an error unless all values are in the range `0 <= v <= 1`.
353 fn try_from(value: (f64, f64, f64, f64)) -> Result<Self, Self::Error>
354 {
355 let ints = floats_to_argb(value.0, value.1, value.2, value.3)?;
356 Ok(ColorType::ARGBInteger(ints.0, ints.1, ints.2, ints.3))
357 }
358}
359
360impl<T> From<Vec<RGBInts>> for ColorType<T>
361{
362 /// Converts `Vec<(u8, u8, u8)>` into [VariableRGBInteger]
363 fn from(value: Vec<RGBInts>) -> Self
364 {
365 ColorType::VariableRGBInteger(value)
366 }
367}
368
369impl<T> From<Vec<ARGBInts>> for ColorType<T>
370{
371 /// Converts `Vec<(u8, u8, u8, u8)>` into [VariableARGBInteger]
372 fn from(value: Vec<ARGBInts>) -> Self
373 {
374 ColorType::VariableARGBInteger(value)
375 }
376}
377
378impl<T> From<ColorIndex> for ColorType<T>
379{
380 /// Converts `u8` into [Index]
381 fn from(value: ColorIndex) -> Self
382 {
383 ColorType::Index(value)
384 }
385}
386
387impl<T> From<Vec<ColorIndex>> for ColorType<T>
388{
389 /// Converts `Vec<u8>` into [VariableIndex]
390 fn from(value: Vec<ColorIndex>) -> Self
391 {
392 ColorType::VariableIndex(value)
393 }
394}
395
396impl<T: Display> OneWayOwned for ColorType<T>
397{
398 type Output = ColorType<String>;
399
400 fn to_one_way_owned(&self) -> ColorType<String>
401 {
402 match self
403 {
404 RGBString(s) => RGBString(s.to_string()),
405 RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
406 VariableRGBInteger(d) => VariableRGBInteger(d.clone()),
407 PaletteFracColor(v) => PaletteFracColor(*v),
408 PaletteCBColor(v) => PaletteCBColor(*v),
409 VariablePaletteColor(d) => VariablePaletteColor(d.clone()),
410 SavedColorMap(s, d) => SavedColorMap(s.to_string(), d.clone()),
411 VariableIndex(d) => VariableIndex(d.clone()),
412 Background => Background,
413 Index(n) => Index(*n),
414 Black => Black,
415 ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
416 VariableARGBInteger(d) => VariableARGBInteger(d.clone()),
417 }
418 }
419}
420
421impl ColorType<String>
422{
423 pub fn to_ref(&self) -> ColorType<&str>
424 {
425 match self
426 {
427 RGBString(s) => RGBString(s),
428 RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
429 VariableRGBInteger(d) => VariableRGBInteger(d.to_vec()),
430 VariableARGBInteger(d) => VariableARGBInteger(d.to_vec()),
431 PaletteFracColor(v) => PaletteFracColor(*v),
432 PaletteCBColor(v) => PaletteCBColor(*v),
433 VariablePaletteColor(d) => VariablePaletteColor(d.to_vec()),
434 SavedColorMap(s, d) => SavedColorMap(s, d.to_vec()),
435 VariableIndex(d) => VariableIndex(d.to_vec()),
436 Background => Background,
437 Index(n) => Index(*n),
438 Black => Black,
439 ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
440 }
441 }
442}