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}