palette/macros/
color_theory.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
macro_rules! impl_lab_color_schemes {
    ($color_ty: ident $(<$phantom_ty: ident>)? [$a: ident, $b: ident] [$($other_component: ident),+]) => {
        impl<$($phantom_ty,)? T> crate::color_theory::Complementary for $color_ty<$($phantom_ty,)? T>
        where
            T: core::ops::Neg<Output = T>,
        {
            fn complementary(self) -> Self {
                Self {
                    $a: -self.$a,
                    $b: -self.$b,
                    $($other_component: self.$other_component,)+
                }
            }
        }

        impl<$($phantom_ty,)? T, A> crate::color_theory::Complementary for crate::Alpha<$color_ty<$($phantom_ty,)? T>, A>
        where
            $color_ty<$($phantom_ty,)? T>: crate::color_theory::Complementary,
        {
            fn complementary(self) -> Self {
                crate::Alpha {
                    color: self.color.complementary(),
                    alpha: self.alpha
                }
            }
        }

        impl<$($phantom_ty,)? T> crate::color_theory::Tetradic for $color_ty<$($phantom_ty,)? T>
        where
            T: core::ops::Neg<Output = T> + Clone,
        {
            fn tetradic(self) -> (Self, Self, Self) {
                use crate::color_theory::Complementary;

                let first = Self {
                    $a: -self.$b.clone(),
                    $b: self.$a.clone(),
                    $($other_component: self.$other_component.clone(),)+
                };

                let second = self.clone().complementary();
                let third = first.clone().complementary();

                (first, second, third)
            }
        }

        impl<$($phantom_ty,)? T, A> crate::color_theory::Tetradic for crate::Alpha<$color_ty<$($phantom_ty,)? T>, A>
        where
            $color_ty<$($phantom_ty,)? T>: crate::color_theory::Tetradic,
            A: Clone,
        {
            fn tetradic(self) -> (Self, Self, Self) {
                let (color1, color2, color3) = self.color.tetradic();

                (
                    crate::Alpha{
                        color: color1,
                        alpha: self.alpha.clone(),
                    },
                    crate::Alpha{
                        color: color2,
                        alpha: self.alpha.clone(),
                    },
                    crate::Alpha{
                        color: color3,
                        alpha: self.alpha,
                    },
                )
            }
        }
    };
    ($color_ty: ident $(<$phantom_ty:ident>)? [$($other_component: ident),+]) => {
        impl_lab_color_schemes!($color_ty $(<$phantom_ty>)? [a, b] [$($other_component),+]);
    };
}

#[cfg(test)]
macro_rules! test_lab_color_schemes {
    ($color_ty: ident / $radial_ty: ident [$a: ident, $b: ident] [$($other_component: ident),+]) => {
        #[cfg(feature = "approx")]
        #[test]
        fn complementary() {
            use crate::{color_theory::Complementary, convert::FromColorUnclamped};

            let quadrant1: $color_ty = $color_ty::new(0.5f32, 0.1, 0.3);
            let quadrant2: $color_ty = $color_ty::new(0.5f32, 0.1, -0.3);
            let quadrant3: $color_ty = $color_ty::new(0.5f32, -0.1, -0.3);
            let quadrant4: $color_ty = $color_ty::new(0.5f32, -0.1, 0.3);

            let quadrant1_radial = $radial_ty::from_color_unclamped(quadrant1);
            let quadrant2_radial = $radial_ty::from_color_unclamped(quadrant2);
            let quadrant3_radial = $radial_ty::from_color_unclamped(quadrant3);
            let quadrant4_radial = $radial_ty::from_color_unclamped(quadrant4);

            assert_relative_eq!(quadrant1.complementary(), $color_ty::from_color_unclamped(quadrant1_radial.complementary()), epsilon = 0.000001);
            assert_relative_eq!(quadrant2.complementary(), $color_ty::from_color_unclamped(quadrant2_radial.complementary()), epsilon = 0.000001);
            assert_relative_eq!(quadrant3.complementary(), $color_ty::from_color_unclamped(quadrant3_radial.complementary()), epsilon = 0.000001);
            assert_relative_eq!(quadrant4.complementary(), $color_ty::from_color_unclamped(quadrant4_radial.complementary()), epsilon = 0.000001);
        }

        #[cfg(feature = "approx")]
        #[test]
        fn tetradic() {
            use crate::{color_theory::Tetradic, convert::FromColorUnclamped};
            fn convert_tuple<T, U>(input: (T, T, T)) -> (U, U, U)
            where
                U: FromColorUnclamped<T>,
            {
                (
                    U::from_color_unclamped(input.0),
                    U::from_color_unclamped(input.1),
                    U::from_color_unclamped(input.2),
                )
            }

            fn check_tuples(a: ($color_ty, $color_ty, $color_ty), b: ($color_ty, $color_ty, $color_ty)) {
                let (a1, a2, a3) = a;
                let (b1, b2, b3) = b;

                assert_relative_eq!(a1, b1, epsilon = 0.000001);
                assert_relative_eq!(a2, b2, epsilon = 0.000001);
                assert_relative_eq!(a3, b3, epsilon = 0.000001);
            }

            let quadrant1 = $color_ty::new(0.5f32, 0.1, 0.3);
            let quadrant2 = $color_ty::new(0.5f32, 0.1, -0.3);
            let quadrant3 = $color_ty::new(0.5f32, -0.1, -0.3);
            let quadrant4 = $color_ty::new(0.5f32, -0.1, 0.3);

            let quadrant1_radial = $radial_ty::from_color_unclamped(quadrant1);
            let quadrant2_radial = $radial_ty::from_color_unclamped(quadrant2);
            let quadrant3_radial = $radial_ty::from_color_unclamped(quadrant3);
            let quadrant4_radial = $radial_ty::from_color_unclamped(quadrant4);

            check_tuples(quadrant1.tetradic(), convert_tuple::<_, $color_ty>(quadrant1_radial.tetradic()));
            check_tuples(quadrant2.tetradic(), convert_tuple::<_, $color_ty>(quadrant2_radial.tetradic()));
            check_tuples(quadrant3.tetradic(), convert_tuple::<_, $color_ty>(quadrant3_radial.tetradic()));
            check_tuples(quadrant4.tetradic(), convert_tuple::<_, $color_ty>(quadrant4_radial.tetradic()));
        }
    };
    ($color_ty: ident / $radial_ty: ident [$($other_component: ident),+]) => {
        test_lab_color_schemes!($color_ty / $radial_ty [a, b] [$($other_component),+]);
    };
}