leptos_chartistry/colours/
mod.rs

1mod colourmaps;
2mod scheme;
3
4pub use colourmaps::*;
5pub use scheme::{ColourScheme, DivergingGradient, LinearGradientSvg, SequentialGradient};
6
7use std::str::FromStr;
8
9/// A colour in RGB format.
10#[derive(Copy, Clone, Debug, PartialEq)]
11pub struct Colour {
12    red: u8,
13    green: u8,
14    blue: u8,
15}
16
17impl Colour {
18    /// Create a new colour with the given red, green, and blue values.
19    #[deprecated(since = "0.1.1", note = "renamed to `from_rgb`")]
20    pub const fn new(red: u8, green: u8, blue: u8) -> Self {
21        Self::from_rgb(red, green, blue)
22    }
23
24    /// Create a new colour with the given red, green, and blue values.
25    pub const fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
26        Self { red, green, blue }
27    }
28
29    fn interpolate(self, rhs: Self, ratio: f64) -> Self {
30        let ratio = ratio.clamp(0.0, 1.0);
31        let interpolate = |pre: u8, post: u8| {
32            let pre = pre as f64;
33            let post = post as f64;
34            let diff = post - pre;
35            (pre + (diff * ratio)).round() as u8
36        };
37        Colour {
38            red: interpolate(self.red, rhs.red),
39            green: interpolate(self.green, rhs.green),
40            blue: interpolate(self.blue, rhs.blue),
41        }
42    }
43}
44
45impl std::fmt::Display for Colour {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        write!(f, "#{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
48    }
49}
50
51impl FromStr for Colour {
52    type Err = String;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        let s = s.trim_start_matches('#');
56        let len = s.len();
57        if len != 6 {
58            return Err(format!("expected 6 characters, got {}", len));
59        }
60        let red = u8::from_str_radix(&s[0..2], 16).map_err(|e| e.to_string())?;
61        let green = u8::from_str_radix(&s[2..4], 16).map_err(|e| e.to_string())?;
62        let blue = u8::from_str_radix(&s[4..6], 16).map_err(|e| e.to_string())?;
63        Ok(Colour { red, green, blue })
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_colour_interpolation() {
73        let black = Colour::from_rgb(0, 0, 0);
74        let white = Colour::from_rgb(255, 255, 255);
75        assert_eq!(black.interpolate(white, 1.0), white);
76        assert_eq!(black.interpolate(white, 0.0), black);
77        assert_eq!(white.interpolate(black, 1.0), black);
78        assert_eq!(white.interpolate(black, 0.0), white);
79        assert_eq!(black.interpolate(white, 0.2), Colour::from_rgb(51, 51, 51));
80        assert_eq!(
81            white.interpolate(black, 0.2),
82            Colour::from_rgb(204, 204, 204)
83        );
84        let other = Colour::from_rgb(34, 202, 117);
85        assert_eq!(black.interpolate(other, 0.4), Colour::from_rgb(14, 81, 47));
86        assert_eq!(
87            white.interpolate(other, 0.2),
88            Colour::from_rgb(211, 244, 227)
89        );
90        assert_eq!(
91            white.interpolate(other, 0.8),
92            Colour::from_rgb(78, 213, 145)
93        );
94    }
95}