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 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 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 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 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#[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() };
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() };
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() };
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() };
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() };
330
331 assert!(!color.is_within_bounds());
332 assert_eq!(clamped, expected);
333 }
334
335 println!("ok")
336 }
337 }
338 );
339}