Skip to main content

theframework/
thecolor.rs

1pub use crate::prelude::*;
2use std::ops::{Index, IndexMut};
3
4/// Holds a normalized color value and offers several import and export methods.
5#[derive(Serialize, Deserialize, Clone, Debug)]
6pub struct TheColor {
7    pub r: f32,
8    pub g: f32,
9    pub b: f32,
10    pub a: f32,
11
12    pub name: String,
13}
14
15impl Default for TheColor {
16    fn default() -> Self {
17        Self::gray()
18    }
19}
20
21impl PartialEq for TheColor {
22    fn eq(&self, other: &Self) -> bool {
23        const EPSILON: f32 = 0.0001;
24        (self.r - other.r).abs() < EPSILON
25            && (self.g - other.g).abs() < EPSILON
26            && (self.b - other.b).abs() < EPSILON
27            && (self.a - other.a).abs() < EPSILON
28    }
29}
30
31impl TheColor {
32    /// Creates a color from u8 values.
33    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
34        Self {
35            r,
36            g,
37            b,
38            a,
39            name: String::default(),
40        }
41    }
42
43    /// Creates a color from hsl.
44    pub fn from_hsl(h: f32, s: f32, l: f32) -> Self {
45        fn hue_angle(hue_in: f32, x: f32, y: f32) -> f32 {
46            let mut hue = hue_in;
47
48            if hue < 0.0 {
49                hue += 1.0;
50            } else if hue > 1.0 {
51                hue -= 1.0;
52            }
53
54            if hue < 1.0 / 6.0 {
55                return x + (y - x) * 6.0 * hue;
56            }
57            if hue < 1.0 / 2.0 {
58                return y;
59            }
60            if hue < 2.0 / 3.0 {
61                return x + (y - x) * ((2.0 / 3.0) - hue) * 6.0;
62            }
63
64            x
65        }
66
67        let (r, g, b) = if s == 0.0 {
68            (l, l, l)
69        } else {
70            let y = if l < 0.5 {
71                l * (1.0 + s)
72            } else {
73                l + s - l * s
74            };
75            let x = 2.0 * l - y;
76            let hue = h / 360.0;
77
78            (
79                hue_angle(hue + 1.0 / 3.0, x, y),
80                hue_angle(hue, x, y),
81                hue_angle(hue - 1.0 / 3.0, x, y),
82            )
83        };
84
85        Self {
86            r,
87            g,
88            b,
89            a: 1.0,
90            name: String::default(),
91        }
92    }
93
94    /// Creates a color from u8 values.
95    pub fn from_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
96        Self {
97            r: r as f32 / 255.0,
98            g: g as f32 / 255.0,
99            b: b as f32 / 255.0,
100            a: a as f32 / 255.0,
101            name: String::default(),
102        }
103    }
104
105    /// Creates a color from 4 u8 values.
106    pub fn from_u8_array(color: [u8; 4]) -> Self {
107        Self {
108            r: color[0] as f32 / 255.0,
109            g: color[1] as f32 / 255.0,
110            b: color[2] as f32 / 255.0,
111            a: color[3] as f32 / 255.0,
112            name: String::default(),
113        }
114    }
115
116    /// Creates a color from 3 u8 values.
117    pub fn from_u8_array_3(color: [u8; 3]) -> Self {
118        Self {
119            r: color[0] as f32 / 255.0,
120            g: color[1] as f32 / 255.0,
121            b: color[2] as f32 / 255.0,
122            a: 1.0,
123            name: String::default(),
124        }
125    }
126
127    /// Creates a color from an vec3.
128    pub fn from_vec3(color: Vec3<f32>) -> Self {
129        Self {
130            r: color.x,
131            g: color.y,
132            b: color.z,
133            a: 1.0,
134            name: String::default(),
135        }
136    }
137
138    /// Creates a color from an vec4.
139    pub fn from_vec4f(color: Vec4<f32>) -> Self {
140        Self {
141            r: color.x,
142            g: color.y,
143            b: color.z,
144            a: color.w,
145            name: String::default(),
146        }
147    }
148
149    /// Creates a color from a hex value.
150    pub fn from_hex(hex_color: &str) -> Self {
151        let mut r = 255;
152        let mut g = 255;
153        let mut b = 255;
154        let mut a = 255;
155
156        if hex_color.len() == 7 || hex_color.len() == 9 {
157            if let Ok(rr) = u8::from_str_radix(&hex_color[1..3], 16) {
158                r = rr;
159            }
160            if let Ok(gg) = u8::from_str_radix(&hex_color[3..5], 16) {
161                g = gg;
162            }
163            if let Ok(bb) = u8::from_str_radix(&hex_color[5..7], 16) {
164                b = bb;
165            }
166            if hex_color.len() == 9 {
167                if let Ok(aa) = u8::from_str_radix(&hex_color[7..9], 16) {
168                    a = aa;
169                }
170            }
171        }
172
173        Self {
174            r: r as f32 / 255.0,
175            g: g as f32 / 255.0,
176            b: b as f32 / 255.0,
177            a: a as f32 / 255.0,
178            name: String::default(),
179        }
180    }
181
182    /// Converts the color to a hexadecimal string.
183    pub fn to_hex(&self) -> String {
184        // Convert each color component to an integer in the range 0-255
185        let r = (self.r * 255.0).round() as u8;
186        let g = (self.g * 255.0).round() as u8;
187        let b = (self.b * 255.0).round() as u8;
188        let a = (self.a * 255.0).round() as u8;
189
190        // Convert to hexadecimal string. If alpha is fully opaque (255), omit it from the string.
191        if a == 255 {
192            format!("#{:02X}{:02X}{:02X}", r, g, b)
193        } else {
194            format!("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a)
195        }
196    }
197
198    /// Creates a white color.
199    pub fn white() -> Self {
200        Self {
201            r: 1.0,
202            g: 1.0,
203            b: 1.0,
204            a: 1.0,
205            name: "White".to_string(),
206        }
207    }
208
209    /// Creates a gray.
210    pub fn gray() -> Self {
211        Self {
212            r: 0.5,
213            g: 0.5,
214            b: 0.5,
215            a: 1.0,
216            name: "Gray".to_string(),
217        }
218    }
219
220    /// Creates a black color.
221    pub fn black() -> Self {
222        Self {
223            r: 0.0,
224            g: 0.0,
225            b: 0.0,
226            a: 1.0,
227            name: "Black".to_string(),
228        }
229    }
230
231    /// Creates a transparent color.
232    pub fn transparent() -> Self {
233        Self {
234            r: 0.0,
235            g: 0.0,
236            b: 0.0,
237            a: 0.0,
238            name: "Transparent".to_string(),
239        }
240    }
241
242    /// Creates an [f32;4] array
243    pub fn to_array(&self) -> [f32; 4] {
244        [self.r, self.g, self.b, self.a]
245    }
246
247    /// Creates an [f32;3] array
248    pub fn to_array_3(&self) -> [f32; 3] {
249        [self.r, self.g, self.b]
250    }
251
252    /// Creates an [u8;3] array
253    pub fn to_u8_array_3(&self) -> [u8; 3] {
254        [
255            (self.r * 255.0) as u8,
256            (self.g * 255.0) as u8,
257            (self.b * 255.0) as u8,
258        ]
259    }
260
261    /// Creates an [u8;4] array
262    pub fn to_u8_array(&self) -> [u8; 4] {
263        [
264            (self.r * 255.0) as u8,
265            (self.g * 255.0) as u8,
266            (self.b * 255.0) as u8,
267            (self.a * 255.0) as u8,
268        ]
269    }
270
271    /// Convert the color to Vec3f.
272    pub fn to_vec3(&self) -> Vec3<f32> {
273        Vec3::new(self.r, self.g, self.b)
274    }
275
276    /// Convert the color to Vec4f.
277    pub fn to_vec4(&self) -> Vec4<f32> {
278        Vec4::new(self.r, self.g, self.b, self.a)
279    }
280
281    pub fn as_srgba(&self) -> TheColor {
282        TheColor::new(
283            self.r.powf(0.45),
284            self.g.powf(0.45),
285            self.b.powf(0.45),
286            self.a.powf(0.45),
287        )
288    }
289
290    /// Convert the color to HSL
291    pub fn as_hsl(&self) -> Vec3<f32> {
292        let max = self.r.max(self.g.max(self.b));
293        let min = self.r.min(self.g.min(self.b));
294
295        let l = (max + min) / 2.0;
296        let mut h; // = l;
297        let s; // = l;
298
299        if max == min {
300            h = 0.0;
301            s = 0.0;
302        } else {
303            let d = max - min;
304            s = if l > 0.5 {
305                d / (2.0 - max - min)
306            } else {
307                d / (max + min)
308            };
309
310            h = if max == self.r {
311                (self.g - self.b) / d + if self.g < self.b { 6.0 } else { 0.0 }
312            } else if max == self.g {
313                (self.b - self.r) / d + 2.0
314            } else {
315                (self.r - self.g) / d + 4.0
316            };
317
318            h /= 6.0;
319        }
320
321        Vec3::new(h, s.clamp(0.0, 1.0), l.clamp(0.0, 1.0))
322    }
323
324    /// Lights or darken the color by the given amount.
325    pub fn lighten_darken(&self, amount: f32) -> Self {
326        let hsl = self.as_hsl();
327        let new_l = (hsl.z + amount).clamp(0.0, 1.0);
328
329        Self::from_hsl(hsl.x * 360.0, hsl.y, new_l)
330    }
331
332    /// Returns a new color as a mix between self and other.
333    pub fn mix(&self, other: &TheColor, v: f32) -> TheColor {
334        TheColor::new(
335            (1.0 - v) * self.r + other.r * v,
336            (1.0 - v) * self.g + other.g * v,
337            (1.0 - v) * self.b + other.b * v,
338            (1.0 - v) * self.a + other.a * v,
339        )
340    }
341}
342
343impl Index<usize> for TheColor {
344    type Output = f32;
345
346    fn index(&self, index: usize) -> &Self::Output {
347        match index {
348            0 => &self.r,
349            1 => &self.g,
350            2 => &self.b,
351            3 => &self.a,
352            _ => panic!("Index out of bounds!"),
353        }
354    }
355}
356
357impl IndexMut<usize> for TheColor {
358    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
359        match index {
360            0 => &mut self.r,
361            1 => &mut self.g,
362            2 => &mut self.b,
363            3 => &mut self.a,
364            _ => panic!("Index out of bounds!"),
365        }
366    }
367}
368
369// From<T>
370
371impl From<Vec3<f32>> for TheColor {
372    fn from(color: Vec3<f32>) -> Self {
373        Self::from_vec3(color)
374    }
375}
376
377impl From<Vec4<f32>> for TheColor {
378    fn from(color: Vec4<f32>) -> Self {
379        Self::from_vec4f(color)
380    }
381}
382
383impl From<[u8; 4]> for TheColor {
384    fn from(color: [u8; 4]) -> Self {
385        Self::from_u8_array(color)
386    }
387}
388
389impl From<[u8; 3]> for TheColor {
390    fn from(color: [u8; 3]) -> Self {
391        Self::from_u8_array_3(color)
392    }
393}
394
395impl From<&str> for TheColor {
396    fn from(color: &str) -> Self {
397        Self::from_hex(color)
398    }
399}
400
401impl From<(f32, f32, f32)> for TheColor {
402    fn from(color: (f32, f32, f32)) -> Self {
403        Self::from_hsl(color.0, color.1, color.2)
404    }
405}
406
407impl From<[f32; 3]> for TheColor {
408    fn from(color: [f32; 3]) -> Self {
409        Self {
410            name: "".into(),
411            r: color[0],
412            g: color[1],
413            b: color[2],
414            a: 1.0,
415        }
416    }
417}