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 pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
23 Self { r, g, b, a }
24 }
25
26 pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self {
28 Self { r, g, b, a: 1.0 }
29 }
30
31 pub const fn with_alpha(&self, alpha: f32) -> Self {
33 Self::new(self.r, self.g, self.b, alpha)
34 }
35
36 pub const fn splat_alpha(v: f32, alpha: f32) -> Self {
38 Self::new(v, v, v, alpha)
39 }
40
41 pub const fn splat(v: f32) -> Self {
43 Self::splat_alpha(v, 1.0)
44 }
45
46 pub fn lerp(self, other: Color, t: f32) -> Self {
48 self + (other - self) * t
49 }
50
51 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 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 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 pub fn from_hsv(h: f32, s: f32, v: f32) -> Self {
78 Self::from_hsva(h, s, v, 1.0)
79 }
80
81 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 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 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 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 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 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 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 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 pub const fn from_vec4(vec: Vector4) -> Self {
213 Self::new(vec.x, vec.y, vec.z, vec.w)
214 }
215
216 pub const fn to_vec4(&self) -> Vector4 {
218 Vector4::new(self.r, self.g, self.b, self.a)
219 }
220
221 pub const fn from_vec3(vec: Vector3) -> Self {
223 Self::new(vec.x, vec.y, vec.z, 1.0)
224 }
225
226 pub const fn to_vec3(&self) -> Vector3 {
228 Vector3::new(self.r, self.g, self.b)
229 }
230
231 pub const fn to_array(&self) -> [f32; 4] {
233 [self.r, self.g, self.b, self.a]
234 }
235
236 pub const fn to_array_rgb(&self) -> [f32; 3] {
238 [self.r, self.g, self.b]
239 }
240
241 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
388impl 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}