1use super::*;
4
5#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
11pub struct Percent(pub Numeric);
12
13impl Display for Percent {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 write!(f, "{}%", self.0)
16 }
17}
18
19impl Interpolatable for Percent {
20 fn interpolate(&self, other: &Self, t: f64) -> Self {
21 Self(self.0.interpolate(&other.0, t))
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub enum Opacity {
28 Alpha(Numeric),
30 Percent(Numeric),
32}
33
34impl Display for Opacity {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 Opacity::Alpha(value) => write!(f, "{}", value),
38 Opacity::Percent(value) => write!(f, "{}%", value),
39 }
40 }
41}
42
43impl Default for Opacity {
44 fn default() -> Self {
45 Self::Alpha(Numeric::F64(1.0))
46 }
47}
48
49impl Interpolatable for Opacity {
50 fn interpolate(&self, other: &Self, t: f64) -> Self {
51 Opacity::Alpha(Numeric::F64(
52 self.to_float_0_1().interpolate(&other.to_float_0_1(), t),
53 ))
54 }
55}
56
57impl From<f64> for Opacity {
58 fn from(value: f64) -> Self {
59 Numeric::F64(value).into()
60 }
61}
62
63impl From<i32> for Opacity {
64 fn from(value: i32) -> Self {
65 Numeric::from(value).into()
66 }
67}
68
69impl From<Numeric> for Opacity {
70 fn from(value: Numeric) -> Self {
71 Opacity::Alpha(value)
72 }
73}
74
75impl From<Percent> for Opacity {
76 fn from(value: Percent) -> Self {
77 Opacity::Percent(value.0)
78 }
79}
80
81impl Opacity {
82 pub fn to_float_0_1(&self) -> f64 {
84 match self {
85 Opacity::Alpha(value) => value.to_float().clamp(0.0, 1.0),
86 Opacity::Percent(value) => (value.to_float() / 100.0).clamp(0.0, 1.0),
87 }
88 }
89}
90
91impl From<f64> for ColorChannel {
92 fn from(value: f64) -> Self {
93 Numeric::F64(value).into()
94 }
95}
96
97impl From<i32> for ColorChannel {
98 fn from(value: i32) -> Self {
99 Numeric::from(value).into()
100 }
101}
102
103impl Into<ColorChannel> for Percent {
104 fn into(self) -> ColorChannel {
105 ColorChannel::Percent(self.0)
106 }
107}
108
109impl Into<Size> for Percent {
110 fn into(self) -> Size {
111 Size::Percent(self.0)
112 }
113}
114
115impl Into<Rotation> for Percent {
116 fn into(self) -> Rotation {
117 Rotation::Percent(self.0)
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
123pub enum ColorChannel {
124 Rotation(Rotation),
126 Integer(u8),
128 Percent(Numeric),
130}
131
132impl Display for ColorChannel {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 match self {
135 ColorChannel::Rotation(rot) => write!(f, "{}", rot),
136 ColorChannel::Integer(int) => write!(f, "{}", int),
137 ColorChannel::Percent(per) => write!(f, "{}%", per),
138 }
139 }
140}
141
142impl Default for ColorChannel {
143 fn default() -> Self {
144 Self::Percent(Numeric::F64(50.0))
145 }
146}
147
148impl From<Numeric> for Rotation {
149 fn from(value: Numeric) -> Self {
150 Rotation::Degrees(value)
151 }
152}
153
154impl From<Numeric> for ColorChannel {
155 fn from(value: Numeric) -> Self {
156 Self::Integer(value.to_int().clamp(0, 255) as u8)
157 }
158}
159
160impl ColorChannel {
161 pub fn to_float_0_1(&self) -> f64 {
163 match self {
164 Self::Percent(per) => (per.to_float() / 100.0).clamp(0_f64, 1_f64),
165 Self::Integer(zero_to_255) => {
166 let f_zero = (*zero_to_255) as f64;
167 (f_zero / 255.0_f64).clamp(0_f64, 1_f64)
168 }
169 Self::Rotation(rot) => rot.to_float_0_1(),
170 }
171 }
172}
173
174#[allow(non_camel_case_types)]
176#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq)]
177pub enum Color {
178 rgb(ColorChannel, ColorChannel, ColorChannel),
180 rgba(ColorChannel, ColorChannel, ColorChannel, ColorChannel),
182 hsl(Rotation, ColorChannel, ColorChannel),
184 hsla(Rotation, ColorChannel, ColorChannel, ColorChannel),
186
187 #[default]
189 SLATE,
190 GRAY,
192 ZINC,
194 NEUTRAL,
196 STONE,
198 RED,
200 ORANGE,
202 AMBER,
204 YELLOW,
206 LIME,
208 GREEN,
210 EMERALD,
212 TEAL,
214 CYAN,
216 SKY,
218 BLUE,
220 INDIGO,
222 VIOLET,
224 PURPLE,
226 FUCHSIA,
228 PINK,
230 ROSE,
232 BLACK,
234 WHITE,
236 TRANSPARENT,
238 NONE,
240}
241
242impl Hash for Color {
243 fn hash<H: Hasher>(&self, state: &mut H) {
244 core::mem::discriminant(self).hash(state);
245 }
246}
247
248impl Display for Color {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 match self {
252 Self::rgb(r, g, b) => write!(f, "rgb({}, {}, {})", r, g, b),
253 Self::rgba(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
254 Self::hsl(h, s, l) => write!(f, "hsl({}, {}, {})", h, s, l),
255 Self::hsla(h, s, l, a) => write!(f, "hsla({}, {}, {}, {})", h, s, l, a),
256 Self::SLATE => write!(f, "SLATE"),
257 Self::GRAY => write!(f, "GRAY"),
258 Self::ZINC => write!(f, "ZINC"),
259 Self::NEUTRAL => write!(f, "NEUTRAL"),
260 Self::STONE => write!(f, "STONE"),
261 Self::RED => write!(f, "RED"),
262 Self::ORANGE => write!(f, "ORANGE"),
263 Self::AMBER => write!(f, "AMBER"),
264 Self::YELLOW => write!(f, "YELLOW"),
265 Self::LIME => write!(f, "LIME"),
266 Self::GREEN => write!(f, "GREEN"),
267 Self::EMERALD => write!(f, "EMERALD"),
268 Self::TEAL => write!(f, "TEAL"),
269 Self::CYAN => write!(f, "CYAN"),
270 Self::SKY => write!(f, "SKY"),
271 Self::BLUE => write!(f, "BLUE"),
272 Self::INDIGO => write!(f, "INDIGO"),
273 Self::VIOLET => write!(f, "VIOLET"),
274 Self::PURPLE => write!(f, "PURPLE"),
275 Self::FUCHSIA => write!(f, "FUCHSIA"),
276 Self::PINK => write!(f, "PINK"),
277 Self::ROSE => write!(f, "ROSE"),
278 Self::BLACK => write!(f, "BLACK"),
279 Self::WHITE => write!(f, "WHITE"),
280 Self::TRANSPARENT => write!(f, "TRANSPARENT"),
281 Self::NONE => write!(f, "NONE"),
282 }
283 }
284}
285
286impl Color {
287 pub fn rgb(r: ColorChannel, g: ColorChannel, b: ColorChannel) -> Self {
292 Self::rgb(r, g, b)
293 }
294
295 pub fn rgba(r: ColorChannel, g: ColorChannel, b: ColorChannel, a: ColorChannel) -> Self {
297 Self::rgba(r, g, b, a)
298 }
299
300 pub fn hsl(h: Rotation, s: ColorChannel, l: ColorChannel) -> Self {
302 Self::hsl(h, s, l)
303 }
304
305 pub fn hsla(h: Rotation, s: ColorChannel, l: ColorChannel, a: ColorChannel) -> Self {
307 Self::hsla(h, s, l, a)
308 }
309
310 pub fn to_piet_color(&self) -> piet::Color {
312 let rgba = self.to_rgba_0_1();
313 piet::Color::rgba(rgba[0], rgba[1], rgba[2], rgba[3])
314 }
315
316 pub fn from_rgba_0_1(rgba_0_1: [f64; 4]) -> Self {
318 Self::rgba(
319 ColorChannel::Percent(Numeric::F64(rgba_0_1[0] * 100.0)),
320 ColorChannel::Percent(Numeric::F64(rgba_0_1[1] * 100.0)),
321 ColorChannel::Percent(Numeric::F64(rgba_0_1[2] * 100.0)),
322 ColorChannel::Percent(Numeric::F64(rgba_0_1[3] * 100.0)),
323 )
324 }
325
326 pub fn from_hex(hex: &str) -> Self {
328 let r = u8::from_str_radix(&hex[0..2], 16).unwrap() as f64 / 255.0;
329 let g = u8::from_str_radix(&hex[2..4], 16).unwrap() as f64 / 255.0;
330 let b = u8::from_str_radix(&hex[4..6], 16).unwrap() as f64 / 255.0;
331 let a = if hex.len() == 8 {
332 u8::from_str_radix(&hex[6..8], 16).unwrap() as f64 / 255.0
333 } else {
334 1.0
335 };
336 Self::rgba(
337 ColorChannel::Percent(Numeric::F64(r * 100.0)),
338 ColorChannel::Percent(Numeric::F64(g * 100.0)),
339 ColorChannel::Percent(Numeric::F64(b * 100.0)),
340 ColorChannel::Percent(Numeric::F64(a * 100.0)),
341 )
342 }
343
344 pub fn alpha_0_1(&self) -> f64 {
346 self.to_rgba_0_1()[3]
347 }
348
349 pub fn with_alpha_factor(&self, factor: f64) -> Self {
351 let mut rgba = self.to_rgba_0_1();
352 rgba[3] = (rgba[3] * factor.clamp(0.0, 1.0)).clamp(0.0, 1.0);
353 Self::from_rgba_0_1(rgba)
354 }
355
356 pub fn to_rgba_0_1(&self) -> [f64; 4] {
358 match self {
359 Self::hsla(h, s, l, a) => {
360 let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
361 [rgb[0], rgb[1], rgb[2], a.to_float_0_1()]
362 }
363 Self::hsl(h, s, l) => {
364 let rgb = hsl_to_rgb(h.to_float_0_1(), s.to_float_0_1(), l.to_float_0_1());
365 [rgb[0], rgb[1], rgb[2], 1.0]
366 }
367 Self::rgba(r, g, b, a) => [
368 r.to_float_0_1(),
369 g.to_float_0_1(),
370 b.to_float_0_1(),
371 a.to_float_0_1(),
372 ],
373 Self::rgb(r, g, b) => [r.to_float_0_1(), g.to_float_0_1(), b.to_float_0_1(), 1.0],
374
375 Self::SLATE => Self::rgb(
377 Numeric::from(0x64).into(),
378 Numeric::from(0x74).into(),
379 Numeric::from(0x8b).into(),
380 )
381 .to_rgba_0_1(),
382 Self::GRAY => Self::rgb(
383 Numeric::from(0x6b).into(),
384 Numeric::from(0x72).into(),
385 Numeric::from(0x80).into(),
386 )
387 .to_rgba_0_1(),
388 Self::ZINC => Self::rgb(
389 Numeric::from(0x71).into(),
390 Numeric::from(0x71).into(),
391 Numeric::from(0x7a).into(),
392 )
393 .to_rgba_0_1(),
394 Self::NEUTRAL => Self::rgb(
395 Numeric::from(0x73).into(),
396 Numeric::from(0x73).into(),
397 Numeric::from(0x73).into(),
398 )
399 .to_rgba_0_1(),
400 Self::STONE => Self::rgb(
401 Numeric::from(0x78).into(),
402 Numeric::from(0x71).into(),
403 Numeric::from(0x6c).into(),
404 )
405 .to_rgba_0_1(),
406 Self::RED => Self::rgb(
407 Numeric::from(0xeF).into(),
408 Numeric::from(0x44).into(),
409 Numeric::from(0x44).into(),
410 )
411 .to_rgba_0_1(),
412 Self::ORANGE => Self::rgb(
413 Numeric::from(0xf9).into(),
414 Numeric::from(0x73).into(),
415 Numeric::from(0x16).into(),
416 )
417 .to_rgba_0_1(),
418 Self::AMBER => Self::rgb(
419 Numeric::from(0xf5).into(),
420 Numeric::from(0x9e).into(),
421 Numeric::from(0x0b).into(),
422 )
423 .to_rgba_0_1(),
424 Self::YELLOW => Self::rgb(
425 Numeric::from(0xea).into(),
426 Numeric::from(0xb3).into(),
427 Numeric::from(0x08).into(),
428 )
429 .to_rgba_0_1(),
430 Self::LIME => Self::rgb(
431 Numeric::from(0x84).into(),
432 Numeric::from(0xcc).into(),
433 Numeric::from(0x16).into(),
434 )
435 .to_rgba_0_1(),
436 Self::GREEN => Self::rgb(
437 Numeric::from(0x22).into(),
438 Numeric::from(0xc5).into(),
439 Numeric::from(0x5e).into(),
440 )
441 .to_rgba_0_1(),
442 Self::EMERALD => Self::rgb(
443 Numeric::from(0x10).into(),
444 Numeric::from(0xb9).into(),
445 Numeric::from(0x81).into(),
446 )
447 .to_rgba_0_1(),
448 Self::TEAL => Self::rgb(
449 Numeric::from(0x14).into(),
450 Numeric::from(0xb8).into(),
451 Numeric::from(0xa6).into(),
452 )
453 .to_rgba_0_1(),
454 Self::CYAN => Self::rgb(
455 Numeric::from(0x06).into(),
456 Numeric::from(0xb6).into(),
457 Numeric::from(0xd4).into(),
458 )
459 .to_rgba_0_1(),
460 Self::SKY => Self::rgb(
461 Numeric::from(0x0e).into(),
462 Numeric::from(0xa5).into(),
463 Numeric::from(0xe9).into(),
464 )
465 .to_rgba_0_1(),
466 Self::BLUE => Self::rgb(
467 Numeric::from(0x3b).into(),
468 Numeric::from(0x82).into(),
469 Numeric::from(0xf6).into(),
470 )
471 .to_rgba_0_1(),
472 Self::INDIGO => Self::rgb(
473 Numeric::from(0x63).into(),
474 Numeric::from(0x66).into(),
475 Numeric::from(0xf1).into(),
476 )
477 .to_rgba_0_1(),
478 Self::VIOLET => Self::rgb(
479 Numeric::from(0x8b).into(),
480 Numeric::from(0x5c).into(),
481 Numeric::from(0xf6).into(),
482 )
483 .to_rgba_0_1(),
484 Self::PURPLE => Self::rgb(
485 Numeric::from(0xa8).into(),
486 Numeric::from(0x55).into(),
487 Numeric::from(0xf7).into(),
488 )
489 .to_rgba_0_1(),
490 Self::FUCHSIA => Self::rgb(
491 Numeric::from(0xd9).into(),
492 Numeric::from(0x46).into(),
493 Numeric::from(0xef).into(),
494 )
495 .to_rgba_0_1(),
496 Self::PINK => Self::rgb(
497 Numeric::from(0xec).into(),
498 Numeric::from(0x48).into(),
499 Numeric::from(0x99).into(),
500 )
501 .to_rgba_0_1(),
502 Self::ROSE => Self::rgb(
503 Numeric::from(0xf4).into(),
504 Numeric::from(0x3f).into(),
505 Numeric::from(0x5e).into(),
506 )
507 .to_rgba_0_1(),
508 Self::BLACK => Self::rgb(
509 Numeric::from(0x00).into(),
510 Numeric::from(0x00).into(),
511 Numeric::from(0x00).into(),
512 )
513 .to_rgba_0_1(),
514 Self::WHITE => Self::rgb(
515 Numeric::from(0xff).into(),
516 Numeric::from(0xff).into(),
517 Numeric::from(0xff).into(),
518 )
519 .to_rgba_0_1(),
520 Self::TRANSPARENT | Self::NONE => Self::rgba(
521 Numeric::from(0xff).into(),
522 Numeric::from(0xff).into(),
523 Numeric::from(0xFF).into(),
524 Numeric::from(0x00).into(),
525 )
526 .to_rgba_0_1(),
527 }
528 }
529
530 pub fn to_hsla_0_1(&self) -> [f64; 4] {
533 let [r, g, b, a] = self.to_rgba_0_1();
534
535 let max = r.max(g).max(b);
536 let min = r.min(g).min(b);
537 let chroma = max - min;
538
539 let h = if chroma == 0.0 {
540 0.0
541 } else if max == r {
542 ((g - b) / chroma + 6.0) % 6.0 / 6.0
543 } else if max == g {
544 ((b - r) / chroma + 2.0) / 6.0
545 } else {
546 ((r - g) / chroma + 4.0) / 6.0
547 };
548
549 let l = (max + min) / 2.0;
550
551 let s = if l == 0.0 || l == 1.0 {
552 0.0
553 } else {
554 (max - l) / l.min(1.0 - l)
555 };
556
557 [h, s, l, a]
558 }
559}
560
561const RGB_UNIT_MAX: f64 = 255.0;
563fn hsl_to_rgb(h: f64, s: f64, l: f64) -> [f64; 3] {
564 if s == 0.0 {
565 let unit = RGB_UNIT_MAX * l;
566 return [
567 unit / RGB_UNIT_MAX,
568 unit / RGB_UNIT_MAX,
569 unit / RGB_UNIT_MAX,
570 ];
571 }
572
573 let temp1 = if l < 0.5 {
574 l * (1.0 + s)
575 } else {
576 l + s - l * s
577 };
578
579 let temp2 = 2.0 * l - temp1;
580 let hue = h;
581
582 let temp_r = bound(hue + (1.0 / 3.0), 1.0);
583 let temp_g = bound(hue, 1.0);
584 let temp_b = bound(hue - (1.0 / 3.0), 1.0);
585
586 let r = calc_rgb_unit(temp_r, temp1, temp2);
587 let g = calc_rgb_unit(temp_g, temp1, temp2);
588 let b = calc_rgb_unit(temp_b, temp1, temp2);
589 [r / RGB_UNIT_MAX, g / RGB_UNIT_MAX, b / RGB_UNIT_MAX]
590}
591
592fn calc_rgb_unit(unit: f64, temp1: f64, temp2: f64) -> f64 {
593 let mut result = temp2;
594 if 6.0 * unit < 1.0 {
595 result = temp2 + (temp1 - temp2) * 6.0 * unit
596 } else if 2.0 * unit < 1.0 {
597 result = temp1
598 } else if 3.0 * unit < 2.0 {
599 result = temp2 + (temp1 - temp2) * ((2.0 / 3.0) - unit) * 6.0
600 }
601 result * RGB_UNIT_MAX
602}
603
604pub fn bound(r: f64, entire: f64) -> f64 {
606 let mut n = r;
607 loop {
608 let less = n < 0.0;
609 let bigger = n > entire;
610 if !less && !bigger {
611 break n;
612 }
613 if less {
614 n += entire;
615 } else {
616 n -= entire;
617 }
618 }
619}
620
621impl Into<ColorMessage> for &Color {
622 fn into(self) -> ColorMessage {
623 let rgba = self.to_rgba_0_1();
624 ColorMessage::Rgba(rgba)
625 }
626}
627impl PartialEq<ColorMessage> for Color {
628 fn eq(&self, other: &ColorMessage) -> bool {
629 let self_rgba = self.to_rgba_0_1();
630
631 match other {
632 ColorMessage::Rgb(other_rgba) => {
633 self_rgba[0] == other_rgba[0]
634 && self_rgba[1] == other_rgba[1]
635 && self_rgba[2] == other_rgba[2]
636 && self_rgba[3] == 1.0
637 }
638 ColorMessage::Rgba(other_rgba) => {
639 self_rgba[0] == other_rgba[0]
640 && self_rgba[1] == other_rgba[1]
641 && self_rgba[2] == other_rgba[2]
642 && self_rgba[3] == other_rgba[3]
643 }
644 }
645 }
646}
647impl Interpolatable for Color {
648 fn interpolate(&self, other: &Self, t: f64) -> Self {
649 let rgba_s = self.to_rgba_0_1();
650 let rgba_o = other.to_rgba_0_1();
651 let rgba_i = [
652 rgba_s[0].interpolate(&rgba_o[0], t),
653 rgba_s[1].interpolate(&rgba_o[1], t),
654 rgba_s[2].interpolate(&rgba_o[2], t),
655 rgba_s[3].interpolate(&rgba_o[3], t),
656 ];
657 Color::from_rgba_0_1(rgba_i)
658 }
659}