color_lib/definitions.rs
1//!
2//! This module includes all definitions of base colors, the Color trait and the ColorMap trait
3//!
4
5use crate::utils;
6
7/// A struct for defining a single color in RGBA space all values are between 0
8/// and 1
9#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
10pub struct ColorRGBA {
11 /// The red combonent
12 r: f32,
13 /// The green component
14 g: f32,
15 /// The blue component
16 b: f32,
17 /// The alpha component
18 a: f32,
19}
20
21impl ColorRGBA {
22 /// Constructs a new rgba color, all values are clamped to between 0 and 1
23 ///
24 /// # Parameters
25 ///
26 /// r: The red component
27 ///
28 /// g: The green component
29 ///
30 /// b: The blue component
31 ///
32 /// a: The alpha component
33 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
34 return Self {
35 r: r.clamp(0.0, 1.0),
36 g: g.clamp(0.0, 1.0),
37 b: b.clamp(0.0, 1.0),
38 a: a.clamp(0.0, 1.0),
39 };
40 }
41
42 /// Constructs a new rgba color with the alpha component equal to 1, all
43 /// values are clamped to between 0 and 1
44 ///
45 /// # Parameters
46 ///
47 /// r: The red component
48 ///
49 /// g: The green component
50 ///
51 /// b: The blue component
52 pub fn new_rgb(r: f32, g: f32, b: f32) -> Self {
53 return Self::new(r, g, b, 1.0);
54 }
55
56 /// Constructs a new rgba color without validating the input
57 ///
58 /// # Parameters
59 ///
60 /// r: The red component
61 ///
62 /// g: The green component
63 ///
64 /// b: The blue component
65 ///
66 /// a: The alpha component
67 pub unsafe fn new_unsafe(r: f32, g: f32, b: f32, a: f32) -> Self {
68 return Self {
69 r: r,
70 g: g,
71 b: b,
72 a: a,
73 };
74 }
75
76 /// Retrieves the red component of the color
77 pub fn get_red(&self) -> f32 {
78 return self.r;
79 }
80
81 /// Retrieves the green component of the color
82 pub fn get_green(&self) -> f32 {
83 return self.g;
84 }
85
86 /// Retrieves the blue component of the color
87 pub fn get_blue(&self) -> f32 {
88 return self.b;
89 }
90
91 /// Retrieves the alpha component of the color
92 pub fn get_alpha(&self) -> f32 {
93 return self.a;
94 }
95
96 /// Retrieves all the color components in an array in the order: red, green,
97 /// blue, alpha
98 pub fn get(&self) -> [f32; 4] {
99 return [self.r, self.g, self.b, self.a];
100 }
101}
102
103impl Color for ColorRGBA {
104 const TYPE: ColorType = ColorType::RGB;
105
106 fn get_rgba(&self) -> ColorRGBA {
107 return *self;
108 }
109}
110
111/// A struct for defining a single color in HSLA space all values are between 0
112/// and 1
113#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
114pub struct ColorHSLA {
115 /// The hue combonent
116 h: f32,
117 /// The saturation component
118 s: f32,
119 /// The lightness component
120 l: f32,
121 /// The alpha component
122 a: f32,
123}
124
125impl ColorHSLA {
126 /// Constructs a new hsla color, all values are clamped to between 0 and 1
127 ///
128 /// # Parameters
129 ///
130 /// h: The hue component
131 ///
132 /// s: The saturation component
133 ///
134 /// l: The lightness component
135 ///
136 /// a: The alpha component
137 pub fn new(h: f32, s: f32, l: f32, a: f32) -> Self {
138 return Self {
139 h: h.rem_euclid(1.0),
140 s: s.clamp(0.0, 1.0),
141 l: l.clamp(0.0, 1.0),
142 a: a.clamp(0.0, 1.0),
143 };
144 }
145
146 /// Constructs a new hsla color with the alpha component equal to 1, all
147 /// values are clamped to between 0 and 1
148 ///
149 /// # Parameters
150 ///
151 /// h: The hue component
152 ///
153 /// s: The saturation component
154 ///
155 /// l: The ligness component
156 pub fn new_hsl(h: f32, s: f32, l: f32) -> Self {
157 return Self::new(h, s, l, 1.0);
158 }
159
160 /// Constructs a new hsla color without validating the input
161 ///
162 /// # Parameters
163 ///
164 /// h: The hue component
165 ///
166 /// s: The saturation component
167 ///
168 /// l: The lightness component
169 ///
170 /// a: The alpha component
171 pub unsafe fn new_unsafe(h: f32, s: f32, l: f32, a: f32) -> Self {
172 return Self {
173 h: h,
174 s: s,
175 l: l,
176 a: a,
177 };
178 }
179
180 /// Retrieves the hue component of the color
181 pub fn get_hue(&self) -> f32 {
182 return self.h;
183 }
184
185 /// Retrieves the saturation component of the color
186 pub fn get_saturation(&self) -> f32 {
187 return self.s;
188 }
189
190 /// Retrieves the lightness component of the color
191 pub fn get_lightness(&self) -> f32 {
192 return self.l;
193 }
194
195 /// Retrieves the alpha component of the color
196 pub fn get_alpha(&self) -> f32 {
197 return self.a;
198 }
199
200 /// Retrieves all the color components in an array in the order: hue,
201 /// saturation, lightness, alpha
202 pub fn get(&self) -> [f32; 4] {
203 return [self.h, self.s, self.l, self.a];
204 }
205}
206
207impl Color for ColorHSLA {
208 const TYPE: ColorType = ColorType::HSL;
209
210 fn get_hsla(&self) -> ColorHSLA {
211 return *self;
212 }
213}
214
215/// A struct for defining a single color in HSLV space all values are between 0
216/// and 1
217#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
218pub struct ColorHSVA {
219 /// The hue combonent
220 h: f32,
221 /// The saturation component
222 s: f32,
223 /// The value component
224 v: f32,
225 /// The alpha component
226 a: f32,
227}
228
229impl ColorHSVA {
230 /// Constructs a new hsva color, all values are clamped to between 0 and 1
231 ///
232 /// # Parameters
233 ///
234 /// h: The hue component
235 ///
236 /// s: The saturation component
237 ///
238 /// v: The value component
239 ///
240 /// a: The alpha component
241 pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self {
242 return Self {
243 h: h.rem_euclid(1.0),
244 s: s.clamp(0.0, 1.0),
245 v: v.clamp(0.0, 1.0),
246 a: a.clamp(0.0, 1.0),
247 };
248 }
249
250 /// Constructs a new hsva color with the alpha component equal to 1, all
251 /// values are clamped to between 0 and 1
252 ///
253 /// # Parameters
254 ///
255 /// h: The hue component
256 ///
257 /// s: The saturation component
258 ///
259 /// v: The value component
260 pub fn new_hsv(h: f32, s: f32, v: f32) -> Self {
261 return Self::new(h, s, v, 1.0);
262 }
263
264 /// Constructs a new hsva color without validating the input
265 ///
266 /// # Parameters
267 ///
268 /// h: The hue component
269 ///
270 /// s: The saturation component
271 ///
272 /// v: The value component
273 ///
274 /// a: The alpha component
275 pub unsafe fn new_unsafe(h: f32, s: f32, v: f32, a: f32) -> Self {
276 return Self {
277 h: h,
278 s: s,
279 v: v,
280 a: a,
281 };
282 }
283
284 /// Retrieves the hue component of the color
285 pub fn get_hue(&self) -> f32 {
286 return self.h;
287 }
288
289 /// Retrieves the saturation component of the color
290 pub fn get_saturation(&self) -> f32 {
291 return self.s;
292 }
293
294 /// Retrieves the value component of the color
295 pub fn get_value(&self) -> f32 {
296 return self.v;
297 }
298
299 /// Retrieves the alpha component of the color
300 pub fn get_alpha(&self) -> f32 {
301 return self.a;
302 }
303
304 /// Retrieves all the color components in an array in the order: hue,
305 /// saturation, value, alpha
306 pub fn get(&self) -> [f32; 4] {
307 return [self.h, self.s, self.v, self.a];
308 }
309}
310
311impl Color for ColorHSVA {
312 const TYPE: ColorType = ColorType::HSV;
313
314 fn get_hsva(&self) -> ColorHSVA {
315 return *self;
316 }
317}
318
319/// A struct for defining a single color in HSIA space all values are between 0
320/// and 1
321#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
322pub struct ColorHSIA {
323 /// The hue combonent
324 h: f32,
325 /// The saturation component
326 s: f32,
327 /// The lightness component
328 i: f32,
329 /// The alpha component
330 a: f32,
331}
332
333impl ColorHSIA {
334 /// Constructs a new hsia color, all values are clamped to between 0 and 1
335 ///
336 /// # Parameters
337 ///
338 /// h: The hue component
339 ///
340 /// s: The saturation component
341 ///
342 /// i: The intensity component
343 ///
344 /// a: The alpha component
345 pub fn new(h: f32, s: f32, i: f32, a: f32) -> Self {
346 return Self {
347 h: h.rem_euclid(1.0),
348 s: s.clamp(0.0, 1.0),
349 i: i.clamp(0.0, 1.0),
350 a: a.clamp(0.0, 1.0),
351 };
352 }
353
354 /// Constructs a new hsli color with the alpha component equal to 1, all
355 /// values are clamped to between 0 and 1
356 ///
357 /// # Parameters
358 ///
359 /// h: The hue component
360 ///
361 /// s: The saturation component
362 ///
363 /// i: The intensity component
364 pub fn new_hsi(h: f32, s: f32, i: f32) -> Self {
365 return Self::new(h, s, i, 1.0);
366 }
367
368 /// Constructs a new hsia color without validating the input
369 ///
370 /// # Parameters
371 ///
372 /// h: The hue component
373 ///
374 /// s: The saturation component
375 ///
376 /// i: The intensity component
377 ///
378 /// a: The alpha component
379 pub unsafe fn new_unsafe(h: f32, s: f32, i: f32, a: f32) -> Self {
380 return Self {
381 h: h,
382 s: s,
383 i: i,
384 a: a,
385 };
386 }
387
388 /// Retrieves the hue component of the color
389 pub fn get_hue(&self) -> f32 {
390 return self.h;
391 }
392
393 /// Retrieves the saturation component of the color
394 pub fn get_saturation(&self) -> f32 {
395 return self.s;
396 }
397
398 /// Retrieves the intensity component of the color
399 pub fn get_intensity(&self) -> f32 {
400 return self.i;
401 }
402
403 /// Retrieves the alpha component of the color
404 pub fn get_alpha(&self) -> f32 {
405 return self.a;
406 }
407
408 /// Retrieves all the color components in an array in the order: hue,
409 /// saturation, intensity, alpha
410 pub fn get(&self) -> [f32; 4] {
411 return [self.h, self.s, self.i, self.a];
412 }
413}
414
415impl Color for ColorHSIA {
416 const TYPE: ColorType = ColorType::HSI;
417
418 fn get_hsia(&self) -> ColorHSIA {
419 return *self;
420 }
421}
422
423/// A generic N-dimensional color, all components are clamped between 0 and 1
424#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
425pub struct ColorND<const N: usize> {
426 values: [f32; N],
427}
428
429impl<const N: usize> ColorND<N> {
430 /// Constructs a new N-dimensional color
431 ///
432 /// # Parameters
433 ///
434 /// values: All color components
435 pub fn new(values: &[f32; N]) -> Self {
436 let values: [f32; N] = values
437 .iter()
438 .map(|value| {
439 return value.clamp(0.0, 1.0);
440 })
441 .collect::<Vec<f32>>()
442 .try_into()
443 .expect("Will never fail");
444
445 return Self { values };
446 }
447
448 /// Retrieves all the color components
449 pub fn get(&self) -> &[f32; N] {
450 return &self.values;
451 }
452}
453
454/// A simple enum type for determining which format is the base format of a
455/// color trait, this is used to implement the correct default functions such
456/// that only one color return function needs to be implemented
457#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
458pub enum ColorType {
459 RGB,
460 HSV,
461 HSL,
462 HSI,
463}
464
465/// Defines a single color which can be expressed in RGBA
466pub trait Color {
467 /// The base color type, the get_x method of this color must be implemented
468 /// while the others will be implemented automatically
469 const TYPE: ColorType;
470
471 /// Retrieves the RGBA color for this color
472 fn get_rgba(&self) -> ColorRGBA {
473 return match Self::TYPE {
474 ColorType::RGB => {
475 panic!("The get_rgba() method must be implemented for a color of TYPE RGB")
476 }
477 ColorType::HSV => utils::hsv_to_rgb(&self.get_hsva()),
478 ColorType::HSL => utils::hsl_to_rgb(&self.get_hsla()),
479 ColorType::HSI => utils::hsi_to_rgb(&self.get_hsia()),
480 };
481 }
482
483 /// Retrieves the HSVA color for this color
484 fn get_hsva(&self) -> ColorHSVA {
485 return match Self::TYPE {
486 ColorType::RGB => utils::rgb_to_hsv(&self.get_rgba()),
487 ColorType::HSV => {
488 panic!("The get_hsva() method must be implemented for a color of TYPE HSV")
489 }
490 ColorType::HSL => utils::hsl_to_hsv(&self.get_hsla()),
491 ColorType::HSI => utils::hsi_to_hsv(&self.get_hsia()),
492 };
493 }
494
495 /// Retrieves the HSLA color for this color
496 fn get_hsla(&self) -> ColorHSLA {
497 return match Self::TYPE {
498 ColorType::RGB => utils::rgb_to_hsl(&self.get_rgba()),
499 ColorType::HSV => utils::hsv_to_hsl(&self.get_hsva()),
500 ColorType::HSL => {
501 panic!("The get_hsla() method must be implemented for a color of TYPE HSL")
502 }
503 ColorType::HSI => utils::hsi_to_hsl(&self.get_hsia()),
504 };
505 }
506
507 /// Retrieves the HSIA color for this color
508 fn get_hsia(&self) -> ColorHSIA {
509 return match Self::TYPE {
510 ColorType::RGB => utils::rgb_to_hsi(&self.get_rgba()),
511 ColorType::HSV => utils::hsv_to_hsi(&self.get_hsva()),
512 ColorType::HSL => utils::hsl_to_hsi(&self.get_hsla()),
513 ColorType::HSI => {
514 panic!("The get_hsia() method must be implemented for a color of TYPE HSI")
515 }
516 };
517 }
518}
519
520/// Defines a color map which can convert a N-dimensional color into a normal
521/// color
522pub trait ColorMap<const N: usize> {
523 /// Retrieves the normal color from the N-dimensional color
524 ///
525 /// # Parameters
526 ///
527 /// color: The N-dimensional color to convert
528 fn get_color(&self, color: ColorND<N>) -> impl Color;
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 /// Test the ColorRGBA struct
536 mod color_rgba {
537 use super::*;
538
539 /// Test new method
540 #[test]
541 fn new() {
542 let result_valid = ColorRGBA::new(0.1, 0.2, 0.3, 0.4);
543 let result_low_r = ColorRGBA::new(-0.1, 0.2, 0.3, 0.4);
544 let result_low_g = ColorRGBA::new(0.1, -0.2, 0.3, 0.4);
545 let result_low_b = ColorRGBA::new(0.1, 0.2, -0.3, 0.4);
546 let result_low_a = ColorRGBA::new(0.1, 0.2, 0.3, -0.4);
547 let result_hig_r = ColorRGBA::new(1.1, 0.2, 0.3, 0.4);
548 let result_hig_g = ColorRGBA::new(0.1, 1.2, 0.3, 0.4);
549 let result_hig_b = ColorRGBA::new(0.1, 0.2, 1.3, 0.4);
550 let result_hig_a = ColorRGBA::new(0.1, 0.2, 0.3, 1.4);
551
552 assert_eq!(
553 result_valid,
554 ColorRGBA {
555 r: 0.1,
556 g: 0.2,
557 b: 0.3,
558 a: 0.4,
559 }
560 );
561 assert_eq!(
562 result_low_r,
563 ColorRGBA {
564 r: 0.0,
565 g: 0.2,
566 b: 0.3,
567 a: 0.4,
568 }
569 );
570 assert_eq!(
571 result_low_g,
572 ColorRGBA {
573 r: 0.1,
574 g: 0.0,
575 b: 0.3,
576 a: 0.4,
577 }
578 );
579 assert_eq!(
580 result_low_b,
581 ColorRGBA {
582 r: 0.1,
583 g: 0.2,
584 b: 0.0,
585 a: 0.4,
586 }
587 );
588 assert_eq!(
589 result_low_a,
590 ColorRGBA {
591 r: 0.1,
592 g: 0.2,
593 b: 0.3,
594 a: 0.0,
595 }
596 );
597 assert_eq!(
598 result_hig_r,
599 ColorRGBA {
600 r: 1.0,
601 g: 0.2,
602 b: 0.3,
603 a: 0.4,
604 }
605 );
606 assert_eq!(
607 result_hig_g,
608 ColorRGBA {
609 r: 0.1,
610 g: 1.0,
611 b: 0.3,
612 a: 0.4,
613 }
614 );
615 assert_eq!(
616 result_hig_b,
617 ColorRGBA {
618 r: 0.1,
619 g: 0.2,
620 b: 1.0,
621 a: 0.4,
622 }
623 );
624 assert_eq!(
625 result_hig_a,
626 ColorRGBA {
627 r: 0.1,
628 g: 0.2,
629 b: 0.3,
630 a: 1.0,
631 }
632 );
633 }
634
635 /// Test new_rgb method
636 #[test]
637 fn new_rgb() {
638 let result_valid = ColorRGBA::new_rgb(0.1, 0.2, 0.3);
639 let result_low_r = ColorRGBA::new_rgb(-0.1, 0.2, 0.3);
640 let result_low_g = ColorRGBA::new_rgb(0.1, -0.2, 0.3);
641 let result_low_b = ColorRGBA::new_rgb(0.1, 0.2, -0.3);
642 let result_hig_r = ColorRGBA::new_rgb(1.1, 0.2, 0.3);
643 let result_hig_g = ColorRGBA::new_rgb(0.1, 1.2, 0.3);
644 let result_hig_b = ColorRGBA::new_rgb(0.1, 0.2, 1.3);
645
646 assert_eq!(
647 result_valid,
648 ColorRGBA {
649 r: 0.1,
650 g: 0.2,
651 b: 0.3,
652 a: 1.0,
653 }
654 );
655 assert_eq!(
656 result_low_r,
657 ColorRGBA {
658 r: 0.0,
659 g: 0.2,
660 b: 0.3,
661 a: 1.0,
662 }
663 );
664 assert_eq!(
665 result_low_g,
666 ColorRGBA {
667 r: 0.1,
668 g: 0.0,
669 b: 0.3,
670 a: 1.0,
671 }
672 );
673 assert_eq!(
674 result_low_b,
675 ColorRGBA {
676 r: 0.1,
677 g: 0.2,
678 b: 0.0,
679 a: 1.0,
680 }
681 );
682 assert_eq!(
683 result_hig_r,
684 ColorRGBA {
685 r: 1.0,
686 g: 0.2,
687 b: 0.3,
688 a: 1.0,
689 }
690 );
691 assert_eq!(
692 result_hig_g,
693 ColorRGBA {
694 r: 0.1,
695 g: 1.0,
696 b: 0.3,
697 a: 1.0,
698 }
699 );
700 assert_eq!(
701 result_hig_b,
702 ColorRGBA {
703 r: 0.1,
704 g: 0.2,
705 b: 1.0,
706 a: 1.0,
707 }
708 );
709 }
710
711 /// Test get_red method
712 #[test]
713 fn get_red() {
714 let value = ColorRGBA {
715 r: 0.1,
716 g: 0.2,
717 b: 0.3,
718 a: 0.4,
719 };
720
721 assert_eq!(value.get_red(), 0.1);
722 }
723
724 /// Test get_green method
725 #[test]
726 fn get_green() {
727 let value = ColorRGBA {
728 r: 0.1,
729 g: 0.2,
730 b: 0.3,
731 a: 0.4,
732 };
733
734 assert_eq!(value.get_green(), 0.2);
735 }
736
737 /// Test get_blue method
738 #[test]
739 fn get_blue() {
740 let value = ColorRGBA {
741 r: 0.1,
742 g: 0.2,
743 b: 0.3,
744 a: 0.4,
745 };
746
747 assert_eq!(value.get_blue(), 0.3);
748 }
749
750 /// Test get_alpha method
751 #[test]
752 fn get_alpha() {
753 let value = ColorRGBA {
754 r: 0.1,
755 g: 0.2,
756 b: 0.3,
757 a: 0.4,
758 };
759
760 assert_eq!(value.get_alpha(), 0.4);
761 }
762
763 /// Test get method
764 #[test]
765 fn get() {
766 let value = ColorRGBA {
767 r: 0.1,
768 g: 0.2,
769 b: 0.3,
770 a: 0.4,
771 };
772
773 assert_eq!(value.get(), [0.1, 0.2, 0.3, 0.4]);
774 }
775 }
776
777 /// Test the ColorND struct
778 mod color_nd {
779 use super::*;
780
781 /// Test new method
782 #[test]
783 fn new() {
784 let result_1_low = ColorND::new(&[-0.2]);
785 let result_1_mid = ColorND::new(&[0.2]);
786 let result_1_hig = ColorND::new(&[1.2]);
787 let result_3_low = ColorND::new(&[-0.2, 0.3, 0.4]);
788 let result_3_mid = ColorND::new(&[0.2, 0.3, 0.4]);
789 let result_3_hig = ColorND::new(&[0.2, 0.3, 1.4]);
790
791 assert_eq!(result_1_low, ColorND { values: [0.0] });
792 assert_eq!(result_1_mid, ColorND { values: [0.2] });
793 assert_eq!(result_1_hig, ColorND { values: [1.0] });
794 assert_eq!(
795 result_3_low,
796 ColorND {
797 values: [0.0, 0.3, 0.4]
798 }
799 );
800 assert_eq!(
801 result_3_mid,
802 ColorND {
803 values: [0.2, 0.3, 0.4]
804 }
805 );
806 assert_eq!(
807 result_3_hig,
808 ColorND {
809 values: [0.2, 0.3, 1.0]
810 }
811 );
812 }
813 }
814}