1use crate::{VizError, VizResult};
2
3#[derive(Clone, Copy, Debug, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct LinearRgba {
7 pub red: f32,
9 pub green: f32,
11 pub blue: f32,
13 pub alpha: f32,
15}
16
17impl LinearRgba {
18 pub const WHITE: Self = Self { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 };
20 pub const BLACK: Self = Self { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
22
23 pub fn try_new(red: f32, green: f32, blue: f32, alpha: f32) -> VizResult<Self> {
25 let components = [red, green, blue, alpha];
26 if components.iter().any(|value| !value.is_finite() || !(0.0..=1.0).contains(value)) {
27 return Err(VizError::InvalidStyle(
28 "RGBA components must be finite and in 0.0..=1.0".into(),
29 ));
30 }
31 Ok(Self { red, green, blue, alpha })
32 }
33}
34
35impl Default for LinearRgba {
36 fn default() -> Self {
37 Self::WHITE
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::LinearRgba;
44
45 #[test]
46 fn rejects_non_finite_and_out_of_range_components() {
47 assert!(LinearRgba::try_new(f32::NAN, 0.0, 0.0, 1.0).is_err());
48 assert!(LinearRgba::try_new(1.1, 0.0, 0.0, 1.0).is_err());
49 assert_eq!(LinearRgba::try_new(0.1, 0.2, 0.3, 0.4).unwrap().alpha, 0.4);
50 }
51}