palette/macros/
clamp.rs

1macro_rules! impl_is_within_bounds {
2    (
3        $ty: ident
4        {$($component: ident => [$get_min: expr, $get_max: expr]),+}
5        $(where $($where: tt)+)?
6    ) => {
7        // add empty generics brackets
8        impl_is_within_bounds!($ty<> {$($component => [$get_min, $get_max]),+} $(where $($where)+)?);
9    };
10    (
11        $ty: ident <$($ty_param: ident),*>
12        {$($component: ident => [$get_min: expr, $get_max: expr]),+}
13        $(where $($where: tt)+)?
14    ) => {
15        impl<$($ty_param,)* T> crate::IsWithinBounds for $ty<$($ty_param,)* T>
16        where
17            T: crate::num::PartialCmp,
18            T::Mask: core::ops::BitAnd<Output = T::Mask>,
19            $($($where)+)?
20        {
21            #[inline]
22            fn is_within_bounds(&self) -> T::Mask {
23                $(
24                    self.$component.gt_eq(&$get_min)
25                    & Option::from($get_max).map_or(crate::BoolMask::from_bool(true), |max|self.$component.lt_eq(&max))
26                )&+
27            }
28        }
29    };
30}
31
32macro_rules! impl_is_within_bounds_hwb {
33    (
34        $ty: ident
35        $(where $($where: tt)+)?
36    ) => {
37        // add empty generics brackets
38        impl_is_within_bounds_hwb!($ty<> $(where $($where)+)?);
39    };
40    (
41        $ty: ident <$($ty_param: ident),*>
42        $(where $($where: tt)+)?
43    ) => {
44        impl<$($ty_param,)* T> crate::IsWithinBounds for $ty<$($ty_param,)* T>
45        where
46            T: crate::num::PartialCmp + core::ops::Add<Output = T> + Clone,
47            T::Mask: core::ops::BitAnd<Output = T::Mask>,
48            $($($where)+)?
49        {
50            #[inline]
51            fn is_within_bounds(&self) -> T::Mask {
52                self.blackness.gt_eq(&Self::min_blackness()) & self.blackness.lt_eq(&Self::max_blackness()) &
53                self.whiteness.gt_eq(&Self::min_whiteness()) & self.whiteness.lt_eq(&Self::max_blackness()) &
54                (self.whiteness.clone() + self.blackness.clone()).lt_eq(&T::max_intensity())
55            }
56        }
57    };
58}
59
60macro_rules! _clamp_value {
61    ($value: expr, $min: expr) => {
62        crate::clamp_min($value, $min)
63    };
64    ($value: expr, $min: expr, $max: expr) => {
65        crate::clamp($value, $min, $max)
66    };
67    (@assign $value: expr, $min: expr) => {
68        crate::clamp_min_assign($value, $min)
69    };
70    (@assign $value: expr, $min: expr, $max: expr) => {
71        crate::clamp_assign($value, $min, $max)
72    };
73}
74
75macro_rules! impl_clamp {
76    (
77        $ty: ident
78        {$($component: ident => [$get_min: expr $(, $get_max: expr)?]),+}
79        $(other {$($other: ident),+})?
80        $(where $($where: tt)+)?
81    ) => {
82        // add empty generics brackets
83        impl_clamp!($ty<> {$($component => [$get_min $(, $get_max)?]),+} $(other {$($other),+})? $(where $($where)+)?);
84    };
85    (
86        $ty: ident <$($ty_param: ident),*>
87        {$($component: ident => [$get_min: expr $(, $get_max: expr)?]),+}
88        $(other {$($other: ident),+})?
89        $(where $($where: tt)+)?
90    ) => {
91        impl<$($ty_param,)* T> crate::Clamp for $ty<$($ty_param,)* T>
92        where
93            T: crate::num::Clamp,
94            $($($where)+)?
95        {
96            #[inline]
97            fn clamp(self) -> Self {
98                Self {
99                    $($component: _clamp_value!(self.$component, $get_min $(, $get_max)?),)+
100                    $($($other: self.$other,)+)?
101                }
102            }
103        }
104
105        impl<$($ty_param,)* T> crate::ClampAssign for $ty<$($ty_param,)* T>
106        where
107            T: crate::num::ClampAssign,
108            $($($where)+)?
109        {
110            #[inline]
111            fn clamp_assign(&mut self) {
112                $(_clamp_value!(@assign &mut self.$component, $get_min $(, $get_max)?);)+
113            }
114        }
115    };
116}
117
118macro_rules! impl_clamp_hwb {
119    (
120        $ty: ident
121        $(phantom: $phantom: ident)?
122        $(where $($where: tt)+)?
123    ) => {
124        // add empty generics brackets
125        impl_clamp_hwb!($ty<> $(phantom: $phantom)? $(where $($where)+)?);
126    };
127    (
128        $ty: ident <$($ty_param: ident),*>
129        $(phantom: $phantom: ident)?
130        $(where $($where: tt)+)?
131    ) => {
132        impl<$($ty_param,)* T> crate::Clamp for $ty<$($ty_param,)* T>
133        where
134            T: crate::num::One
135                + crate::num::Clamp
136                + crate::num::PartialCmp
137                + core::ops::Add<Output = T>
138                + core::ops::DivAssign
139                + Clone,
140            T::Mask: crate::bool_mask::Select<T>,
141            $($($where)+)?
142        {
143            #[inline]
144            fn clamp(self) -> Self {
145                let mut whiteness = crate::clamp_min(self.whiteness.clone(), Self::min_whiteness());
146                let mut blackness = crate::clamp_min(self.blackness.clone(), Self::min_blackness());
147
148                let sum = self.blackness + self.whiteness;
149                let divisor = sum.gt(&T::max_intensity()).select(sum, T::one());
150                whiteness /= divisor.clone();
151                blackness /= divisor;
152
153                Self {hue: self.hue, whiteness, blackness $(, $phantom: self.$phantom)?}
154            }
155        }
156
157        impl<$($ty_param,)* T> crate::ClampAssign for $ty<$($ty_param,)* T>
158        where
159            T: crate::num::One
160                + crate::num::ClampAssign
161                + crate::num::PartialCmp
162                + core::ops::Add<Output = T>
163                + core::ops::DivAssign
164                + Clone,
165            T::Mask: crate::bool_mask::Select<T>,
166            $($($where)+)?
167        {
168            #[inline]
169            fn clamp_assign(&mut self) {
170                crate::clamp_min_assign(&mut self.whiteness, Self::min_whiteness());
171                crate::clamp_min_assign(&mut self.blackness, Self::min_blackness());
172
173                let sum = self.blackness.clone() + self.whiteness.clone();
174                let divisor = sum.gt(&T::max_intensity()).select(sum, T::one());
175                self.whiteness /= divisor.clone();
176                self.blackness /= divisor;
177            }
178        }
179    };
180}
181
182//Helper macro for checking ranges and clamping.
183#[cfg(test)]
184macro_rules! assert_ranges {
185    (@make_tuple $first:pat, $next:ident,) => (($first, $next));
186
187    (@make_tuple $first:pat, $next:ident, $($rest:ident,)*) => (
188        assert_ranges!(@make_tuple ($first, $next), $($rest,)*)
189    );
190
191    (
192        $ty:ident < $($ty_params:ty),+ >;
193        clamped {$($clamped:ident: $clamped_from:expr => $clamped_to:expr),+}
194        clamped_min {$($clamped_min:ident: $clamped_min_from:expr => $clamped_min_to:expr),*}
195        unclamped {$($unclamped:ident: $unclamped_from:expr => $unclamped_to:expr),*}
196    ) => (
197        {
198            use core::iter::repeat;
199            use crate::{Clamp, IsWithinBounds};
200
201            {
202                print!("checking below clamp bounds... ");
203                $(
204                    let from = $clamped_from;
205                    let to = $clamped_to;
206                    let diff = to - from;
207                    let $clamped = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
208                )+
209
210                $(
211                    let from = $clamped_min_from;
212                    let to = $clamped_min_to;
213                    let diff = to - from;
214                    let $clamped_min = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
215                )*
216
217                $(
218                    let from = $unclamped_from;
219                    let to = $unclamped_to;
220                    let diff = to - from;
221                    let $unclamped = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
222                )*
223
224                #[allow(clippy::needless_update)]
225                for assert_ranges!(@make_tuple (), $($clamped,)+ $($clamped_min,)* $($unclamped,)* ) in repeat(()) $(.zip($clamped))+ $(.zip($clamped_min))* $(.zip($unclamped))* {
226                    let color: $ty<$($ty_params),+> = $ty {
227                        $($clamped: $clamped.into(),)+
228                        $($clamped_min: $clamped_min.into(),)*
229                        $($unclamped: $unclamped.into(),)*
230                        ..$ty::default() //This prevents exhaustiveness checking
231                    };
232
233                    let clamped = color.clamp();
234
235                    let expected: $ty<$($ty_params),+> = $ty {
236                        $($clamped: $clamped_from.into(),)+
237                        $($clamped_min: $clamped_min_from.into(),)*
238                        $($unclamped: $unclamped.into(),)*
239                        ..$ty::default() //This prevents exhaustiveness checking
240                    };
241
242                    assert!(!color.is_within_bounds());
243                    assert_eq!(clamped, expected);
244                }
245
246                println!("ok")
247            }
248
249            {
250                print!("checking within clamp bounds... ");
251                $(
252                    let from = $clamped_from;
253                    let to = $clamped_to;
254                    let diff = to - from;
255                    let $clamped = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
256                )+
257
258                $(
259                    let from = $clamped_min_from;
260                    let to = $clamped_min_to;
261                    let diff = to - from;
262                    let $clamped_min = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
263                )*
264
265                $(
266                    let from = $unclamped_from;
267                    let to = $unclamped_to;
268                    let diff = to - from;
269                    let $unclamped = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
270                )*
271
272                #[allow(clippy::needless_update)]
273                for assert_ranges!(@make_tuple (), $($clamped,)+ $($clamped_min,)* $($unclamped,)* ) in repeat(()) $(.zip($clamped))+ $(.zip($clamped_min))* $(.zip($unclamped))* {
274                    let color: $ty<$($ty_params),+> = $ty {
275                        $($clamped: $clamped.into(),)+
276                        $($clamped_min: $clamped_min.into(),)*
277                        $($unclamped: $unclamped.into(),)*
278                        ..$ty::default() //This prevents exhaustiveness checking
279                    };
280
281                    let clamped = color.clamp();
282
283                    assert!(color.is_within_bounds());
284                    assert_eq!(clamped, color);
285                }
286
287                println!("ok")
288            }
289
290            {
291                print!("checking above clamp bounds... ");
292                $(
293                    let from = $clamped_from;
294                    let to = $clamped_to;
295                    let diff = to - from;
296                    let $clamped = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
297                )+
298
299                $(
300                    let from = $clamped_min_from;
301                    let to = $clamped_min_to;
302                    let diff = to - from;
303                    let $clamped_min = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
304                )*
305
306                $(
307                    let from = $unclamped_from;
308                    let to = $unclamped_to;
309                    let diff = to - from;
310                    let $unclamped = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
311                )*
312
313                #[allow(clippy::needless_update)]
314                for assert_ranges!(@make_tuple (), $($clamped,)+ $($clamped_min,)* $($unclamped,)* ) in repeat(()) $(.zip($clamped))+ $(.zip($clamped_min))* $(.zip($unclamped))* {
315                    let color: $ty<$($ty_params),+> = $ty {
316                        $($clamped: $clamped.into(),)+
317                        $($clamped_min: $clamped_min.into(),)*
318                        $($unclamped: $unclamped.into(),)*
319                        ..$ty::default() //This prevents exhaustiveness checking
320                    };
321
322                    let clamped = color.clamp();
323
324                    let expected: $ty<$($ty_params),+> = $ty {
325                        $($clamped: $clamped_to.into(),)+
326                        $($clamped_min: $clamped_min.into(),)*
327                        $($unclamped: $unclamped.into(),)*
328                        ..$ty::default() //This prevents exhaustiveness checking
329                    };
330
331                    assert!(!color.is_within_bounds());
332                    assert_eq!(clamped, expected);
333                }
334
335                println!("ok")
336            }
337        }
338    );
339}