Skip to main content

flake/draw_state/shapes/
color.rs

1use crate::prelude::{Vector3, Vector4};
2use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3
4#[repr(C)]
5#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod, PartialEq)]
6pub struct Color {
7    pub r: f32,
8    pub g: f32,
9    pub b: f32,
10    pub a: f32,
11}
12
13impl Default for Color {
14    fn default() -> Self {
15        BLACK
16    }
17}
18
19#[allow(unused)]
20impl Color {
21    /// Create a color using r, g, b, a. All values go from 0-1
22    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
23        Self { r, g, b, a }
24    }
25
26    /// Create a color using r, g, b. All values go from 0-1
27    pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
28        Self { r, g, b, a: 1.0 }
29    }
30
31    /// Get the same color a different transparency/alpha.
32    pub const fn with_alpha(&self, alpha: f32) -> Self {
33        Self::new(self.r, self.g, self.b, alpha)
34    }
35
36    /// Create a color where RGA is a single number.
37    pub const fn splat_alpha(v: f32, alpha: f32) -> Self {
38        Self::new(v, v, v, alpha)
39    }
40
41    /// Create a color where RGA is a single number. (Alpha = 1)
42    pub const fn splat(v: f32) -> Self {
43        Self::splat_alpha(v, 1.0)
44    }
45
46    /// Linearly interpolate to another color using 0-1.
47    pub fn lerp(self, other: Color, t: f32) -> Self {
48        self + (other - self) * t
49    }
50
51    // Clamps the color from 0-1
52    pub const fn clamped(&self) -> Self {
53        Self::new(
54            self.r.clamp(0.0, 1.0),
55            self.g.clamp(0.0, 1.0),
56            self.b.clamp(0.0, 1.0),
57            self.a.clamp(0.0, 1.0),
58        )
59    }
60
61    /// Create a color using RGBA. Each value goes from 0-255.
62    pub const fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
63        Self::new(
64            r as f32 / 255.0,
65            g as f32 / 255.0,
66            b as f32 / 255.0,
67            a as f32 / 255.0,
68        )
69    }
70
71    /// Create a color using RGB. Each value goes from 0-255. (Alpha = 1)
72    pub const fn from_rgb8(r: u8, g: u8, b: u8) -> Self {
73        Self::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0)
74    }
75
76    /// Create a color using hue-saturation-value-alpha. All values go from 0-1.
77    pub fn from_hsv(h: f32, s: f32, v: f32) -> Self {
78        Self::from_hsva(h, s, v, 1.0)
79    }
80
81    /// Create a color using hue-saturation-value-alpha. All values go from 0-1.
82    pub fn from_hsva(mut h: f32, s: f32, v: f32, a: f32) -> Self {
83        h *= 360.0;
84        let h = h.rem_euclid(360.0);
85        let c = v * s;
86        let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
87        let m = v - c;
88
89        let (r, g, b) = match h / 60.0 {
90            h_prime if h_prime < 1.0 => (c, x, 0.0),
91            h_prime if h_prime < 2.0 => (x, c, 0.0),
92            h_prime if h_prime < 3.0 => (0.0, c, x),
93            h_prime if h_prime < 4.0 => (0.0, x, c),
94            h_prime if h_prime < 5.0 => (x, 0.0, c),
95            _ => (c, 0.0, x),
96        };
97
98        Self::new(r + m, g + m, b + m, a)
99    }
100
101    /// Returns (h, s, v) all values from 0-1.
102    pub fn to_hsv(&self) -> (f32, f32, f32) {
103        let max = self.r.max(self.g).max(self.b);
104        let min = self.r.min(self.g).min(self.b);
105        let delta = max - min;
106
107        let h = if delta == 0.0 {
108            0.0
109        } else if max == self.r {
110            60.0 * (((self.g - self.b) / delta).rem_euclid(6.0))
111        } else if max == self.g {
112            60.0 * (((self.b - self.r) / delta) + 2.0)
113        } else {
114            60.0 * (((self.r - self.g) / delta) + 4.0)
115        };
116
117        let s = if max == 0.0 { 0.0 } else { delta / max };
118        let v = max;
119
120        (h / 360.0, s, v)
121    }
122
123    /// Returns (h, s, v, a) all values from 0-1.
124    pub fn to_hsva(&self) -> (f32, f32, f32, f32) {
125        let (h, s, v) = self.to_hsv();
126        (h, s, v, self.a)
127    }
128
129    /// Accepts "RRGGBB", "RGB", "RRGGBBAA", "RGBA", with or without leading '#'
130    pub fn from_hex(hex: &str) -> Option<Self> {
131        let hex = hex.trim_start_matches('#');
132
133        let expand = |c: char| -> Option<u8> {
134            let d = c.to_digit(16)? as u8;
135            Some((d << 4) | d)
136        };
137
138        match hex.len() {
139            3 => {
140                let mut chars = hex.chars();
141                let r = expand(chars.next()?)?;
142                let g = expand(chars.next()?)?;
143                let b = expand(chars.next()?)?;
144                Some(Self::from_rgb8(r, g, b))
145            }
146            4 => {
147                let mut chars = hex.chars();
148                let r = expand(chars.next()?)?;
149                let g = expand(chars.next()?)?;
150                let b = expand(chars.next()?)?;
151                let a = expand(chars.next()?)?;
152                Some(Self::from_rgba8(r, g, b, a))
153            }
154            6 => {
155                let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
156                let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
157                let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
158                Some(Self::from_rgb8(r, g, b))
159            }
160            8 => {
161                let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
162                let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
163                let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
164                let a = u8::from_str_radix(&hex[6..8], 16).ok()?;
165                Some(Self::from_rgba8(r, g, b, a))
166            }
167            _ => None,
168        }
169    }
170
171    /// Returns "#RRGGBB"
172    pub fn to_hex(&self) -> String {
173        format!(
174            "#{:02X}{:02X}{:02X}",
175            (self.r.clamp(0.0, 1.0) * 255.0).round() as u8,
176            (self.g.clamp(0.0, 1.0) * 255.0).round() as u8,
177            (self.b.clamp(0.0, 1.0) * 255.0).round() as u8,
178        )
179    }
180
181    /// Returns "#RRGGBBAA"
182    pub fn to_hex_alpha(&self) -> String {
183        format!(
184            "#{:02X}{:02X}{:02X}{:02X}",
185            (self.r.clamp(0.0, 1.0) * 255.0).round() as u8,
186            (self.g.clamp(0.0, 1.0) * 255.0).round() as u8,
187            (self.b.clamp(0.0, 1.0) * 255.0).round() as u8,
188            (self.a.clamp(0.0, 1.0) * 255.0).round() as u8,
189        )
190    }
191
192    /// Returns a tuple of (r, g, b) from 0-255.
193    pub const fn to_rgb(&self) -> (u8, u8, u8) {
194        (
195            (self.r.clamp(0.0, 1.0) * 255.0).round() as u8,
196            (self.g.clamp(0.0, 1.0) * 255.0).round() as u8,
197            (self.b.clamp(0.0, 1.0) * 255.0).round() as u8,
198        )
199    }
200
201    /// Returns a tuple of `(r, g, b, a)` from 0-255.
202    pub const fn to_rgba(&self) -> (u8, u8, u8, u8) {
203        (
204            (self.r.clamp(0.0, 1.0) * 255.0).round() as u8,
205            (self.g.clamp(0.0, 1.0) * 255.0).round() as u8,
206            (self.b.clamp(0.0, 1.0) * 255.0).round() as u8,
207            (self.a.clamp(0.0, 1.0) * 255.0).round() as u8,
208        )
209    }
210
211    /// Create a color from a `Vector4<f32>` where `x -> r`, `y -> g`, `z -> b`, `w -> a`
212    pub const fn from_vec4(vec: Vector4) -> Self {
213        Self::new(vec.x, vec.y, vec.z, vec.w)
214    }
215
216    /// Create a `Vector4<f32>` from a color where `r -> x`, `g -> y`, `b -> z`, `a -> w`
217    pub const fn to_vec4(&self) -> Vector4 {
218        Vector4::new(self.r, self.g, self.b, self.a)
219    }
220
221    /// Create a color from a `Vector3<f32>` where `x -> r`, `y -> g`, `z -> b`
222    pub const fn from_vec3(vec: Vector3) -> Self {
223        Self::new(vec.x, vec.y, vec.z, 1.0)
224    }
225
226    /// Create a `Vector3<f32>` from a color where `r -> x`, `g -> y`, `b -> z`
227    pub const fn to_vec3(&self) -> Vector3 {
228        Vector3::new(self.r, self.g, self.b)
229    }
230
231    /// Convert color to an array of [r, g, b, a]
232    pub const fn to_array(&self) -> [f32; 4] {
233        [self.r, self.g, self.b, self.a]
234    }
235
236    /// Convert color to an array of [r, g, b]
237    pub const fn to_array_rgb(&self) -> [f32; 3] {
238        [self.r, self.g, self.b]
239    }
240
241    /// Convert color to a `wgpu::Color`
242    pub const fn to_wgpu(&self) -> wgpu::Color {
243        wgpu::Color {
244            r: self.r as f64,
245            g: self.g as f64,
246            b: self.b as f64,
247            a: self.a as f64,
248        }
249    }
250}
251
252impl From<Color> for [f32; 4] {
253    fn from(c: Color) -> Self {
254        c.to_array()
255    }
256}
257
258impl From<[f32; 4]> for Color {
259    fn from(arr: [f32; 4]) -> Self {
260        Self::new(arr[0], arr[1], arr[2], arr[3])
261    }
262}
263
264impl From<Color> for [f32; 3] {
265    fn from(c: Color) -> Self {
266        c.to_array_rgb()
267    }
268}
269
270impl From<[f32; 3]> for Color {
271    fn from(arr: [f32; 3]) -> Self {
272        Self::from_rgb(arr[0], arr[1], arr[2])
273    }
274}
275
276impl From<Color> for (f32, f32, f32, f32) {
277    fn from(c: Color) -> Self {
278        c.to_array().into()
279    }
280}
281
282impl From<(f32, f32, f32, f32)> for Color {
283    fn from(arr: (f32, f32, f32, f32)) -> Self {
284        Self::new(arr.0, arr.1, arr.2, arr.3)
285    }
286}
287
288impl From<Color> for (f32, f32, f32) {
289    fn from(c: Color) -> Self {
290        c.to_array_rgb().into()
291    }
292}
293
294impl From<(f32, f32, f32)> for Color {
295    fn from(arr: (f32, f32, f32)) -> Self {
296        Self::from_rgb(arr.0, arr.1, arr.2)
297    }
298}
299
300impl Add<Color> for Color {
301    type Output = Self;
302
303    fn add(self, rhs: Color) -> Self::Output {
304        Color::new(
305            self.r + rhs.r,
306            self.g + rhs.g,
307            self.b + rhs.b,
308            self.a + rhs.a,
309        )
310    }
311}
312
313impl Sub<Color> for Color {
314    type Output = Self;
315
316    fn sub(self, rhs: Color) -> Self::Output {
317        Color::new(
318            self.r - rhs.r,
319            self.g - rhs.g,
320            self.b - rhs.b,
321            self.a - rhs.a,
322        )
323    }
324}
325
326impl Mul<Color> for Color {
327    type Output = Self;
328
329    fn mul(self, rhs: Color) -> Self::Output {
330        Color::new(
331            self.r * rhs.r,
332            self.g * rhs.g,
333            self.b * rhs.b,
334            self.a * rhs.a,
335        )
336    }
337}
338
339impl Div<Color> for Color {
340    type Output = Self;
341
342    fn div(self, rhs: Color) -> Self::Output {
343        Color::new(
344            self.r / rhs.r,
345            self.g / rhs.g,
346            self.b / rhs.b,
347            self.a / rhs.a,
348        )
349    }
350}
351
352impl AddAssign<Color> for Color {
353    fn add_assign(&mut self, rhs: Color) {
354        self.r += rhs.r;
355        self.g += rhs.g;
356        self.b += rhs.b;
357        self.a += rhs.a;
358    }
359}
360
361impl SubAssign<Color> for Color {
362    fn sub_assign(&mut self, rhs: Color) {
363        self.r -= rhs.r;
364        self.g -= rhs.g;
365        self.b -= rhs.b;
366        self.a -= rhs.a;
367    }
368}
369
370impl MulAssign<Color> for Color {
371    fn mul_assign(&mut self, rhs: Color) {
372        self.r *= rhs.r;
373        self.g *= rhs.g;
374        self.b *= rhs.b;
375        self.a *= rhs.a;
376    }
377}
378
379impl DivAssign<Color> for Color {
380    fn div_assign(&mut self, rhs: Color) {
381        self.r /= rhs.r;
382        self.g /= rhs.g;
383        self.b /= rhs.b;
384        self.a /= rhs.a;
385    }
386}
387
388// Scalar variants
389impl Mul<f32> for Color {
390    type Output = Self;
391
392    fn mul(self, rhs: f32) -> Self::Output {
393        Color::new(self.r * rhs, self.g * rhs, self.b * rhs, self.a * rhs)
394    }
395}
396
397impl Div<f32> for Color {
398    type Output = Self;
399
400    fn div(self, rhs: f32) -> Self::Output {
401        Color::new(self.r / rhs, self.g / rhs, self.b / rhs, self.a / rhs)
402    }
403}
404
405impl MulAssign<f32> for Color {
406    fn mul_assign(&mut self, rhs: f32) {
407        self.r *= rhs;
408        self.g *= rhs;
409        self.b *= rhs;
410        self.a *= rhs;
411    }
412}
413
414impl DivAssign<f32> for Color {
415    fn div_assign(&mut self, rhs: f32) {
416        self.r /= rhs;
417        self.g /= rhs;
418        self.b /= rhs;
419        self.a /= rhs;
420    }
421}
422
423macro_rules! define_colors {
424    ($($name:ident = ($r:expr, $g:expr, $b:expr, $a:expr)),* $(,)?) => {
425        impl Color {
426            $(pub const $name: Self = Self::new($r, $g, $b, $a);)*
427        }
428        $(pub const $name: Color = Color::$name;)*
429    };
430}
431
432define_colors! {
433    TRANSPARENT = (0.0, 0.0, 0.0, 0.0),
434    WHITE       = (1.0, 1.0, 1.0, 1.0),
435    BLACK       = (0.0, 0.0, 0.0, 1.0),
436    LIGHT_BLACK = (0.05, 0.05, 0.05, 1.0),
437
438    GRAY        = (0.2, 0.2, 0.2, 1.0),
439    DARK_GRAY   = (0.1, 0.1, 0.1, 1.0),
440    LIGHT_GRAY  = (0.3, 0.3, 0.3, 1.0),
441
442    RED         = (1.0, 0.03, 0.04, 1.0),
443    DARK_RED    = (0.7, 0.03, 0.04, 1.0),
444    LIGHT_RED   = (1.0, 0.2, 0.2, 1.0),
445
446    GREEN       = (0.0, 1.0, 0.0, 1.0),
447    DARK_GREEN  = (0.0, 0.3, 0.0, 1.0),
448    LIGHT_GREEN = (0.4, 1.0, 0.3, 1.0),
449
450    BLUE        = (0.03, 0.04, 1.0, 1.0),
451    DARK_BLUE   = (0.02, 0.03, 0.7, 1.0),
452    LIGHT_BLUE  = (0.2, 0.4, 1.0, 1.0),
453
454    YELLOW      = (1.0, 1.0, 0.03, 1.0),
455    DARK_YELLOW = (0.7, 0.7, 0.02, 1.0),
456    LIGHT_YELLOW= (1.0, 1.0, 0.2, 1.0),
457
458    MAGENTA       = (1.0, 0.03, 1.0, 1.0),
459    DARK_MAGENTA  = (0.7, 0.02, 0.7, 1.0),
460    LIGHT_MAGENTA = (1.0, 0.2, 1.0, 1.0),
461
462    PURPLE       = (0.3, 0.03, 1.0, 1.0),
463    DARK_PURPLE  = (0.2, 0.02, 0.7, 1.0),
464    LIGHT_PURPLE = (0.45, 0.2, 1.0, 1.0),
465
466    CYAN        = (0.03, 1.0, 1.0, 1.0),
467    DARK_CYAN   = (0.02, 0.7, 0.7, 1.0),
468    LIGHT_CYAN  = (0.5, 1.0, 1.0, 1.0),
469
470    ORANGE      = (1.0, 0.3, 0.03, 1.0),
471    DARK_ORANGE = (0.7, 0.2, 0.02, 1.0),
472    LIGHT_ORANGE= (1.0, 0.45, 0.2, 1.0),
473}