color.rs - source

gnuplot/

color.rs

1pub 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}