1pub use crate::prelude::*;
2use std::ops::{Index, IndexMut};
3
4#[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 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 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 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 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 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 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 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 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 pub fn to_hex(&self) -> String {
184 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 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 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 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 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 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 pub fn to_array(&self) -> [f32; 4] {
244 [self.r, self.g, self.b, self.a]
245 }
246
247 pub fn to_array_3(&self) -> [f32; 3] {
249 [self.r, self.g, self.b]
250 }
251
252 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 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 pub fn to_vec3(&self) -> Vec3<f32> {
273 Vec3::new(self.r, self.g, self.b)
274 }
275
276 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 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; let s; 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 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 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
369impl 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}