palette/macros/
random.rs

1#[cfg(feature = "random")]
2#[cfg(test)]
3macro_rules! assert_uniform_distribution {
4    ($bins:expr) => {{
5        let bins = &$bins;
6
7        for (i, &bin) in bins.iter().enumerate() {
8            if bin < 5 {
9                panic!("{}[{}] < 5: {:?}", stringify!($bins), i, bins);
10            }
11        }
12        const P_LIMIT: f64 = 0.01; // Keeping it low to account for the RNG noise
13        let p_value = crate::random_sampling::test_utils::uniform_distribution_test(bins);
14        if p_value < P_LIMIT {
15            panic!(
16                "distribution of {} is not uniform enough (p-value {} < {}): {:?}",
17                stringify!($bins),
18                p_value,
19                P_LIMIT,
20                bins
21            );
22        }
23    }};
24}
25
26#[cfg(test)]
27macro_rules! test_uniform_distribution {
28    (
29        $ty:path $(as $base_ty:path)? {
30            $($component:ident: ($component_min:expr, $component_max:expr)),+$(,)?
31        },
32        min: $min:expr,
33        max: $max:expr$(,)?
34    ) => {
35        #[cfg(feature = "random")]
36        #[test]
37        fn uniform_distribution_rng_gen() {
38            use rand::Rng;
39
40            const BINS: usize = crate::random_sampling::test_utils::BINS;
41            const SAMPLES: usize = crate::random_sampling::test_utils::SAMPLES;
42
43            $(let mut $component = [0; BINS];)+
44
45            let mut rng = rand_mt::Mt::new(1234); // We want the same seed on every run to avoid random fails
46
47            for _ in 0..SAMPLES {
48                let color: $ty = rng.gen();
49                $(let color: $base_ty = crate::convert::IntoColorUnclamped::into_color_unclamped(color);)?
50
51                if $(color.$component < $component_min || color.$component > $component_max)||+ {
52                    continue;
53                }
54
55                $({
56                    let min: f32 = $component_min;
57                    let max: f32 = $component_max;
58                    let range = max - min;
59                    let normalized = (color.$component - min) / range;
60                    $component[((normalized * BINS as f32) as usize).min(BINS - 1)] += 1;
61                })+
62            }
63
64            $(assert_uniform_distribution!($component);)+
65        }
66
67        #[cfg(feature = "random")]
68        #[test]
69        fn uniform_distribution_uniform_sample() {
70            use rand::distributions::uniform::Uniform;
71            use rand::Rng;
72
73            const BINS: usize = crate::random_sampling::test_utils::BINS;
74            const SAMPLES: usize = crate::random_sampling::test_utils::SAMPLES;
75
76            $(let mut $component = [0; BINS];)+
77
78            let mut rng = rand_mt::Mt::new(1234); // We want the same seed on every run to avoid random fails
79            let uniform_sampler = Uniform::new($min, $max);
80
81            for _ in 0..SAMPLES {
82                let color: $ty = rng.sample(&uniform_sampler);
83                $(let color: $base_ty = crate::convert::IntoColorUnclamped::into_color_unclamped(color);)?
84
85                if $(color.$component < $component_min || color.$component > $component_max)||+ {
86                    continue;
87                }
88
89                $({
90                    let min: f32 = $component_min;
91                    let max: f32 = $component_max;
92                    let range = max - min;
93                    let normalized = (color.$component - min) / range;
94                    $component[((normalized * BINS as f32) as usize).min(BINS - 1)] += 1;
95                })+
96            }
97
98            $(assert_uniform_distribution!($component);)+
99        }
100
101        #[cfg(feature = "random")]
102        #[test]
103        fn uniform_distribution_uniform_sample_inclusive() {
104            use rand::distributions::uniform::Uniform;
105            use rand::Rng;
106
107            const BINS: usize = crate::random_sampling::test_utils::BINS;
108            const SAMPLES: usize = crate::random_sampling::test_utils::SAMPLES;
109
110            $(let mut $component = [0; BINS];)+
111
112            let mut rng = rand_mt::Mt::new(1234); // We want the same seed on every run to avoid random fails
113            let uniform_sampler = Uniform::new_inclusive($min, $max);
114
115            for _ in 0..SAMPLES {
116                let color: $ty = rng.sample(&uniform_sampler);
117                $(let color: $base_ty = crate::convert::IntoColorUnclamped::into_color_unclamped(color);)?
118
119                if $(color.$component < $component_min || color.$component > $component_max)||+ {
120                    continue;
121                }
122
123                $({
124                    let min: f32 = $component_min;
125                    let max: f32 = $component_max;
126                    let range = max - min;
127                    let normalized = (color.$component - min) / range;
128                    $component[((normalized * BINS as f32) as usize).min(BINS - 1)] += 1;
129                })+
130            }
131
132            $(assert_uniform_distribution!($component);)+
133        }
134    };
135}
136
137macro_rules! __apply_map_fn {
138    ($value: expr) => {
139        $value
140    };
141    ($value: expr, $map_fn: expr) => {
142        $map_fn($value)
143    };
144}
145
146macro_rules! impl_rand_traits_cartesian {
147    (
148        $uniform_ty: ident,
149        $ty: ident
150        {$($component: ident $(=> [$map_fn: expr])?),+}
151        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
152        $(where $($where: tt)+)?
153    ) => {
154        impl_rand_traits_cartesian!(
155            $uniform_ty,
156            $ty<>
157            {$($component $( => [$map_fn])?),+}
158            $(phantom: $phantom : PhantomData<$phantom_ty>)?
159            $(where $($where)+)?);
160    };
161    (
162        $uniform_ty: ident,
163        $ty: ident <$($ty_param: ident),*>
164        {$($component: ident $(=> [$map_fn: expr])?),+}
165        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
166        $(where $($where: tt)+)?
167    ) => {
168        #[cfg(feature = "random")]
169        impl<$($ty_param,)* T> rand::distributions::Distribution<$ty<$($ty_param,)* T>> for rand::distributions::Standard
170        where
171            rand::distributions::Standard: rand::distributions::Distribution<T>,
172            $($($where)+)?
173        {
174            #[allow(clippy::redundant_closure_call)]
175            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
176                $ty {
177                    $($component: __apply_map_fn!(rng.gen::<T>() $(, $map_fn)?),)+
178                    $($phantom: core::marker::PhantomData,)?
179                }
180            }
181        }
182
183        /// Samples colors uniformly.
184        #[cfg(feature = "random")]
185        pub struct $uniform_ty<$($ty_param,)* T>
186        where
187            T: rand::distributions::uniform::SampleUniform,
188        {
189            $($component: rand::distributions::uniform::Uniform<T>,)+
190            $($phantom: core::marker::PhantomData<$phantom_ty>,)?
191        }
192
193        #[cfg(feature = "random")]
194        impl<$($ty_param,)* T> rand::distributions::uniform::SampleUniform for $ty<$($ty_param,)* T>
195        where
196            T: rand::distributions::uniform::SampleUniform + Clone,
197        {
198            type Sampler = $uniform_ty<$($ty_param,)* T>;
199        }
200
201        #[cfg(feature = "random")]
202        impl<$($ty_param,)* T> rand::distributions::uniform::UniformSampler for $uniform_ty<$($ty_param,)* T>
203        where
204            T: rand::distributions::uniform::SampleUniform + Clone,
205        {
206            type X = $ty<$($ty_param,)* T>;
207
208            fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
209            where
210                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
211                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
212            {
213                let low = low_b.borrow();
214                let high = high_b.borrow();
215
216                Self {
217                    $($component: rand::distributions::uniform::Uniform::new::<_, T>(low.$component.clone(), high.$component.clone()),)+
218                    $($phantom: core::marker::PhantomData,)?
219                }
220            }
221
222            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
223            where
224                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
225                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
226            {
227                let low = low_b.borrow();
228                let high = high_b.borrow();
229
230                Self {
231                    $($component: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(low.$component.clone(), high.$component.clone()),)+
232                    $($phantom: core::marker::PhantomData,)?
233                }
234            }
235
236            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
237                use rand::distributions::Distribution;
238
239                $ty {
240                    $($component: self.$component.sample(rng),)+
241                    $($phantom: core::marker::PhantomData,)?
242                }
243            }
244        }
245    };
246}
247
248macro_rules! impl_rand_traits_cylinder {
249    (
250        $uniform_ty: ident,
251        $ty: ident
252        {
253            hue: $hue_uniform_ty: ident => $hue_ty: ident,
254            height: $height: ident $(=> [$height_map_fn: expr])?,
255            radius: $radius: ident $(=> [$radius_map_fn: expr])?
256        }
257        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
258        $(where $($where: tt)+)?
259    ) => {
260        impl_rand_traits_cylinder!(
261            $uniform_ty,
262            $ty<>
263            {
264                hue: $hue_uniform_ty => $hue_ty,
265                height: $height $(=> [$height_map_fn])?,
266                radius: $radius $(=> [$radius_map_fn])?
267            }
268            $(phantom: $phantom : PhantomData<$phantom_ty>)?
269            $(where $($where)+)?);
270    };
271    (
272        $uniform_ty: ident,
273        $ty: ident <$($ty_param: ident),*>
274        {
275            hue: $hue_uniform_ty: ident => $hue_ty: ident,
276            height: $height: ident $(=> [$height_map_fn: expr])?,
277            radius: $radius: ident $(=> [$radius_map_fn: expr])?
278        }
279        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
280        $(where $($where: tt)+)?
281    ) => {
282        #[cfg(feature = "random")]
283        impl<$($ty_param,)* T> rand::distributions::Distribution<$ty<$($ty_param,)* T>> for rand::distributions::Standard
284        where
285            T: crate::num::Sqrt,
286            rand::distributions::Standard: rand::distributions::Distribution<T> + rand::distributions::Distribution<$hue_ty<T>>,
287            $($($where)+)?
288        {
289            #[allow(clippy::redundant_closure_call)]
290            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
291                $ty {
292                    hue: rng.gen::<$hue_ty<T>>(),
293                    $height: __apply_map_fn!(rng.gen::<T>() $(, $height_map_fn)?),
294                    $radius: __apply_map_fn!(rng.gen::<T>().sqrt() $(, $radius_map_fn)?),
295                    $($phantom: core::marker::PhantomData,)?
296                }
297            }
298        }
299
300        /// Samples colors uniformly.
301        #[cfg(feature = "random")]
302        pub struct $uniform_ty<$($ty_param,)* T>
303        where
304            T: rand::distributions::uniform::SampleUniform,
305        {
306            hue: crate::hues::$hue_uniform_ty<T>,
307            $height: rand::distributions::uniform::Uniform<T>,
308            $radius: rand::distributions::uniform::Uniform<T>,
309            $($phantom: core::marker::PhantomData<$phantom_ty>,)?
310        }
311
312        #[cfg(feature = "random")]
313        impl<$($ty_param,)* T> rand::distributions::uniform::SampleUniform for $ty<$($ty_param,)* T>
314        where
315            T: crate::num::Sqrt + core::ops::Mul<Output = T> + Clone + rand::distributions::uniform::SampleUniform,
316            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
317            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
318        {
319            type Sampler = $uniform_ty<$($ty_param,)* T>;
320        }
321
322        #[cfg(feature = "random")]
323        impl<$($ty_param,)* T> rand::distributions::uniform::UniformSampler for $uniform_ty<$($ty_param,)* T>
324        where
325            T: crate::num::Sqrt + core::ops::Mul<Output = T> + Clone + rand::distributions::uniform::SampleUniform,
326            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
327            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
328        {
329            type X = $ty<$($ty_param,)* T>;
330
331            fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
332            where
333                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
334                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
335            {
336                let low = low_b.borrow().clone();
337                let high = high_b.borrow().clone();
338
339                $uniform_ty {
340                    $height: rand::distributions::uniform::Uniform::new::<_, T>(low.$height, high.$height),
341                    $radius: rand::distributions::uniform::Uniform::new::<_, T>(
342                        low.$radius.clone() * low.$radius,
343                        high.$radius.clone() * high.$radius,
344                    ),
345                    hue: crate::hues::$hue_uniform_ty::new(low.hue, high.hue),
346                    $($phantom: core::marker::PhantomData,)?
347                }
348            }
349
350            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
351            where
352                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
353                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
354            {
355                let low = low_b.borrow().clone();
356                let high = high_b.borrow().clone();
357
358                $uniform_ty {
359                    $height: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(low.$height, high.$height),
360                    $radius: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(
361                        low.$radius.clone() * low.$radius,
362                        high.$radius.clone() * high.$radius,
363                    ),
364                    hue: crate::hues::$hue_uniform_ty::new_inclusive(low.hue, high.hue),
365                    $($phantom: core::marker::PhantomData,)?
366                }
367            }
368
369            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
370                use rand::distributions::Distribution;
371
372                $ty {
373                    $height: self.$height.sample(rng),
374                    $radius: self.$radius.sample(rng).sqrt(),
375                    hue: self.hue.sample(rng),
376                    $($phantom: core::marker::PhantomData,)?
377                }
378            }
379        }
380    };
381}
382
383macro_rules! impl_rand_traits_hsv_cone {
384    (
385        $uniform_ty: ident,
386        $ty: ident
387        {
388            hue: $hue_uniform_ty: ident => $hue_ty: ident,
389            height: $height: ident,
390            radius: $radius: ident
391        }
392        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
393        $(where $($where: tt)+)?
394    ) => {
395        impl_rand_traits_hsv_cone!(
396            $uniform_ty,
397            $ty<>
398            {
399                hue: $hue_uniform_ty => $hue_ty,
400                height: $height,
401                radius: $radius
402            }
403            $(phantom: $phantom : PhantomData<$phantom_ty>)?
404            $(where $($where)+)?);
405    };
406    (
407        $uniform_ty: ident,
408        $ty: ident <$($ty_param: ident),*>
409        {
410            hue: $hue_uniform_ty: ident => $hue_ty: ident,
411            height: $height: ident,
412            radius: $radius: ident
413        }
414        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
415        $(where $($where: tt)+)?
416    ) => {
417        #[cfg(feature = "random")]
418        impl<$($ty_param,)* T> rand::distributions::Distribution<$ty<$($ty_param,)* T>> for rand::distributions::Standard
419        where
420            T: crate::num::Cbrt + crate::num::Sqrt,
421            rand::distributions::Standard: rand::distributions::Distribution<T> + rand::distributions::Distribution<$hue_ty<T>>,
422        {
423            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
424                let hue = rng.gen::<$hue_ty<T>>();
425                let crate::random_sampling::HsvSample { saturation: $radius, value: $height } =
426                    crate::random_sampling::sample_hsv(rng.gen(), rng.gen());
427
428                $ty {
429                    hue,
430                    $radius,
431                    $height,
432                    $($phantom: core::marker::PhantomData,)?
433                }
434            }
435        }
436
437        /// Samples colors uniformly.
438        #[cfg(feature = "random")]
439        pub struct $uniform_ty<$($ty_param,)* T>
440        where
441            T: rand::distributions::uniform::SampleUniform,
442        {
443            hue: crate::hues::$hue_uniform_ty<T>,
444            u1: rand::distributions::uniform::Uniform<T>,
445            u2: rand::distributions::uniform::Uniform<T>,
446            $($phantom: core::marker::PhantomData<$phantom_ty>,)?
447        }
448
449        #[cfg(feature = "random")]
450        impl<$($ty_param,)* T> rand::distributions::uniform::SampleUniform for $ty<$($ty_param,)* T>
451        where
452            T: crate::num::Cbrt + crate::num::Sqrt + crate::num::Powi + Clone + rand::distributions::uniform::SampleUniform,
453            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
454            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
455        {
456            type Sampler = $uniform_ty<$($ty_param,)* T>;
457        }
458
459        #[cfg(feature = "random")]
460        impl<$($ty_param,)* T> rand::distributions::uniform::UniformSampler for $uniform_ty<$($ty_param,)* T>
461        where
462            T: crate::num::Cbrt + crate::num::Sqrt + crate::num::Powi + Clone + rand::distributions::uniform::SampleUniform,
463            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
464            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
465        {
466            type X = $ty<$($ty_param,)* T>;
467
468            fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
469            where
470                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
471                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
472            {
473                let low = low_b.borrow().clone();
474                let high = high_b.borrow().clone();
475
476                let (r1_min, r2_min) =
477                    crate::random_sampling::invert_hsv_sample(crate::random_sampling::HsvSample {
478                        value: low.$height,
479                        saturation: low.$radius,
480                    });
481                let (r1_max, r2_max) =
482                    crate::random_sampling::invert_hsv_sample(crate::random_sampling::HsvSample {
483                        value: high.$height,
484                        saturation: high.$radius,
485                    });
486
487                $uniform_ty {
488                    hue: crate::hues::$hue_uniform_ty::new(low.hue, high.hue),
489                    u1: rand::distributions::uniform::Uniform::new::<_, T>(r1_min, r1_max),
490                    u2: rand::distributions::uniform::Uniform::new::<_, T>(r2_min, r2_max),
491                    $($phantom: core::marker::PhantomData,)?
492                }
493            }
494
495            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
496            where
497                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
498                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
499            {
500                let low = low_b.borrow().clone();
501                let high = high_b.borrow().clone();
502
503                let (r1_min, r2_min) =
504                    crate::random_sampling::invert_hsv_sample(crate::random_sampling::HsvSample {
505                        value: low.$height,
506                        saturation: low.$radius,
507                    });
508                let (r1_max, r2_max) =
509                    crate::random_sampling::invert_hsv_sample(crate::random_sampling::HsvSample {
510                        value: high.$height,
511                        saturation: high.$radius,
512                    });
513
514                $uniform_ty {
515                    hue: crate::hues::$hue_uniform_ty::new_inclusive(low.hue, high.hue),
516                    u1: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(r1_min, r1_max),
517                    u2: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(r2_min, r2_max),
518                    $($phantom: core::marker::PhantomData,)?
519                }
520            }
521
522            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
523                use rand::distributions::Distribution;
524
525                let hue = self.hue.sample(rng);
526                let crate::random_sampling::HsvSample { saturation: $radius, value: $height } =
527                    crate::random_sampling::sample_hsv(self.u1.sample(rng), self.u2.sample(rng));
528
529                $ty {
530                    hue,
531                    $radius,
532                    $height,
533                    $($phantom: core::marker::PhantomData,)?
534                }
535            }
536        }
537    }
538}
539
540macro_rules! impl_rand_traits_hsl_bicone {
541    (
542        $uniform_ty: ident,
543        $ty: ident
544        {
545            hue: $hue_uniform_ty: ident => $hue_ty: ident,
546            height: $height: ident $(=> [$height_map_fn: expr, $height_unmap_fn: expr])?,
547            radius: $radius: ident $(=> [$radius_map_fn: expr, $radius_unmap_fn: expr])?
548        }
549        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
550        $(where $($where: tt)+)?
551    ) => {
552        impl_rand_traits_hsl_bicone!(
553            $uniform_ty,
554            $ty<>
555            {
556                hue: $hue_uniform_ty => $hue_ty,
557                height: $height $(=> [$height_map_fn, $height_unmap_fn])?,
558                radius: $radius $(=> [$radius_map_fn, $radius_unmap_fn])?
559            }
560            $(phantom: $phantom : PhantomData<$phantom_ty>)?
561            $(where $($where)+)?);
562    };
563    (
564        $uniform_ty: ident,
565        $ty: ident <$($ty_param: ident),*>
566        {
567            hue: $hue_uniform_ty: ident => $hue_ty: ident,
568            height: $height: ident $(=> [$height_map_fn: expr, $height_unmap_fn: expr])?,
569            radius: $radius: ident $(=> [$radius_map_fn: expr, $radius_unmap_fn: expr])?
570        }
571        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
572        $(where $($where: tt)+)?
573    ) => {
574        #[cfg(feature = "random")]
575        impl<$($ty_param,)* T> rand::distributions::Distribution<$ty<$($ty_param,)* T>> for rand::distributions::Standard
576        where
577            T: crate::num::Real + crate::num::One + crate::num::Cbrt + crate::num::Sqrt + crate::num::Arithmetics + crate::num::PartialCmp + Clone,
578            T::Mask: crate::bool_mask::LazySelect<T> + Clone,
579            rand::distributions::Standard: rand::distributions::Distribution<T> + rand::distributions::Distribution<$hue_ty<T>>,
580        {
581            #[allow(clippy::redundant_closure_call)]
582            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
583                let hue = rng.gen::<$hue_ty<T>>();
584                let crate::random_sampling::HslSample { saturation, lightness } =
585                    crate::random_sampling::sample_hsl(rng.gen(), rng.gen());
586
587                $ty {
588                    hue,
589                    $radius: __apply_map_fn!(saturation $(, $radius_map_fn)?),
590                    $height: __apply_map_fn!(lightness $(, $height_map_fn)?),
591                    $($phantom: core::marker::PhantomData,)?
592                }
593            }
594        }
595
596        /// Samples colors uniformly.
597        #[cfg(feature = "random")]
598        pub struct $uniform_ty<$($ty_param,)* T>
599        where
600            T: rand::distributions::uniform::SampleUniform,
601        {
602            hue: crate::hues::$hue_uniform_ty<T>,
603            u1: rand::distributions::uniform::Uniform<T>,
604            u2: rand::distributions::uniform::Uniform<T>,
605            $($phantom: core::marker::PhantomData<$phantom_ty>,)?
606        }
607
608        #[cfg(feature = "random")]
609        impl<$($ty_param,)* T> rand::distributions::uniform::SampleUniform for $ty<$($ty_param,)* T>
610        where
611            T: crate::num::Real + crate::num::One + crate::num::Cbrt + crate::num::Sqrt + crate::num::Powi + crate::num::Arithmetics + crate::num::PartialCmp + Clone + rand::distributions::uniform::SampleUniform,
612            T::Mask: crate::bool_mask::LazySelect<T> + Clone,
613            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
614            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
615        {
616            type Sampler = $uniform_ty<$($ty_param,)* T>;
617        }
618
619        #[cfg(feature = "random")]
620        impl<$($ty_param,)* T> rand::distributions::uniform::UniformSampler for $uniform_ty<$($ty_param,)* T>
621        where
622            T: crate::num::Real + crate::num::One + crate::num::Cbrt + crate::num::Sqrt + crate::num::Powi + crate::num::Arithmetics + crate::num::PartialCmp + Clone + rand::distributions::uniform::SampleUniform,
623            T::Mask: crate::bool_mask::LazySelect<T> + Clone,
624            $hue_ty<T>: rand::distributions::uniform::SampleBorrow<$hue_ty<T>>,
625            crate::hues::$hue_uniform_ty<T>: rand::distributions::uniform::UniformSampler<X = $hue_ty<T>>,
626        {
627            type X = $ty<$($ty_param,)* T>;
628
629            #[allow(clippy::redundant_closure_call)]
630            fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
631            where
632                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
633                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
634            {
635                let low = low_b.borrow().clone();
636                let high = high_b.borrow().clone();
637
638                let (r1_min, r2_min) =
639                    crate::random_sampling::invert_hsl_sample(crate::random_sampling::HslSample {
640                        lightness: __apply_map_fn!(low.$height $(, $radius_unmap_fn)?),
641                        saturation: __apply_map_fn!(low.$radius $(, $height_unmap_fn)?),
642                    });
643                let (r1_max, r2_max) =
644                    crate::random_sampling::invert_hsl_sample(crate::random_sampling::HslSample {
645                        lightness: __apply_map_fn!(high.$height $(, $radius_unmap_fn)?),
646                        saturation: __apply_map_fn!(high.$radius $(, $height_unmap_fn)?),
647                    });
648
649                $uniform_ty {
650                    hue: crate::hues::$hue_uniform_ty::new(low.hue, high.hue),
651                    u1: rand::distributions::uniform::Uniform::new::<_, T>(r1_min, r1_max),
652                    u2: rand::distributions::uniform::Uniform::new::<_, T>(r2_min, r2_max),
653                    $($phantom: core::marker::PhantomData,)?
654                }
655            }
656
657            #[allow(clippy::redundant_closure_call)]
658            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
659            where
660                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
661                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
662            {
663                let low = low_b.borrow().clone();
664                let high = high_b.borrow().clone();
665
666                let (r1_min, r2_min) =
667                    crate::random_sampling::invert_hsl_sample(crate::random_sampling::HslSample {
668                        lightness: __apply_map_fn!(low.$height $(, $radius_unmap_fn)?),
669                        saturation: __apply_map_fn!(low.$radius $(, $height_unmap_fn)?),
670                    });
671                let (r1_max, r2_max) =
672                    crate::random_sampling::invert_hsl_sample(crate::random_sampling::HslSample {
673                        lightness: __apply_map_fn!(high.$height $(, $radius_unmap_fn)?),
674                        saturation: __apply_map_fn!(high.$radius $(, $height_unmap_fn)?),
675                    });
676
677                $uniform_ty {
678                    hue: crate::hues::$hue_uniform_ty::new_inclusive(low.hue, high.hue),
679                    u1: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(r1_min, r1_max),
680                    u2: rand::distributions::uniform::Uniform::new_inclusive::<_, T>(r2_min, r2_max),
681                    $($phantom: core::marker::PhantomData,)?
682                }
683            }
684
685            #[allow(clippy::redundant_closure_call)]
686            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
687                use rand::distributions::Distribution;
688
689                let hue = self.hue.sample(rng);
690                let crate::random_sampling::HslSample { saturation, lightness } =
691                    crate::random_sampling::sample_hsl(self.u1.sample(rng), self.u2.sample(rng));
692
693                $ty {
694                    hue,
695                    $radius: __apply_map_fn!(saturation $(, $radius_map_fn)?),
696                    $height: __apply_map_fn!(lightness $(, $height_map_fn)?),
697                    $($phantom: core::marker::PhantomData,)?
698                }
699            }
700        }
701    }
702}
703
704macro_rules! impl_rand_traits_hwb_cone {
705    (
706        $uniform_ty: ident,
707        $ty: ident,
708        $hsv_uniform_ty: ident,
709        $hsv_ty: ident
710        {
711            height: $height: ident,
712            radius: $radius: ident
713        }
714        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
715        $(where $($where: tt)+)?
716    ) => {
717        impl_rand_traits_hwb_cone!(
718            $uniform_ty,
719            $ty<>,
720            $hsv_uniform_ty,
721            $hsv_ty
722            {
723                height: $height,
724                radius: $radius
725            }
726            $(phantom: $phantom : PhantomData<$phantom_ty>)?
727            $(where $($where)+)?);
728    };
729    (
730        $uniform_ty: ident,
731        $ty: ident <$($ty_param: ident),*>,
732        $hsv_uniform_ty: ident,
733        $hsv_ty: ident
734        {
735            height: $height: ident,
736            radius: $radius: ident
737        }
738        $(phantom: $phantom: ident : PhantomData<$phantom_ty: ident>)?
739        $(where $($where: tt)+)?
740    ) => {
741        #[cfg(feature = "random")]
742        impl<$($ty_param,)* T> rand::distributions::Distribution<$ty<$($ty_param,)* T>> for rand::distributions::Standard
743        where
744            rand::distributions::Standard: rand::distributions::Distribution<$hsv_ty<$($ty_param,)* T>>,
745            $ty<$($ty_param,)* T>: crate::convert::FromColorUnclamped<$hsv_ty<$($ty_param,)* T>>,
746        {
747            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
748                use crate::convert::FromColorUnclamped;
749                $ty::from_color_unclamped(rng.gen::<$hsv_ty<$($ty_param,)* T>>())
750            }
751        }
752
753        /// Samples colors uniformly.
754        #[cfg(feature = "random")]
755        pub struct $uniform_ty<$($ty_param,)* T>
756        where
757            T: rand::distributions::uniform::SampleUniform,
758        {
759            sampler: $hsv_uniform_ty<$($ty_param,)* T>,
760            $($phantom: core::marker::PhantomData<$phantom_ty>,)?
761        }
762
763        #[cfg(feature = "random")]
764        impl<$($ty_param,)* T> rand::distributions::uniform::SampleUniform for $ty<$($ty_param,)* T>
765        where
766            T: crate::num::MinMax + Clone + rand::distributions::uniform::SampleUniform,
767            $hsv_ty<$($ty_param,)* T>: crate::convert::FromColorUnclamped<$ty<$($ty_param,)* T>> + rand::distributions::uniform::SampleBorrow<$hsv_ty<$($ty_param,)* T>>,
768            $ty<$($ty_param,)* T>: crate::convert::FromColorUnclamped<$hsv_ty<$($ty_param,)* T>>,
769            $hsv_uniform_ty<$($ty_param,)* T>: rand::distributions::uniform::UniformSampler<X = $hsv_ty<$($ty_param,)* T>>,
770        {
771            type Sampler = $uniform_ty<$($ty_param,)* T>;
772        }
773
774        #[cfg(feature = "random")]
775        impl<$($ty_param,)* T> rand::distributions::uniform::UniformSampler for $uniform_ty<$($ty_param,)* T>
776        where
777            T: crate::num::MinMax + Clone + rand::distributions::uniform::SampleUniform,
778            $hsv_ty<$($ty_param,)* T>: crate::convert::FromColorUnclamped<$ty<$($ty_param,)* T>> + rand::distributions::uniform::SampleBorrow<$hsv_ty<$($ty_param,)* T>>,
779            $ty<$($ty_param,)* T>: crate::convert::FromColorUnclamped<$hsv_ty<$($ty_param,)* T>>,
780            $hsv_uniform_ty<$($ty_param,)* T>: rand::distributions::uniform::UniformSampler<X = $hsv_ty<$($ty_param,)* T>>,
781        {
782            type X = $ty<$($ty_param,)* T>;
783
784            fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
785            where
786                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
787                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
788            {
789                use crate::convert::FromColorUnclamped;
790                let low_input = $hsv_ty::from_color_unclamped(low_b.borrow().clone());
791                let high_input = $hsv_ty::from_color_unclamped(high_b.borrow().clone());
792
793                let (low_saturation, high_saturation) = low_input.saturation.min_max(high_input.saturation);
794                let (low_value, high_value) = low_input.value.min_max(high_input.value);
795
796                let low = $hsv_ty{
797                    hue: low_input.hue,
798                    $radius: low_saturation,
799                    $height: low_value,
800                    $($phantom: core::marker::PhantomData,)?
801                };
802                let high = $hsv_ty{
803                    hue: high_input.hue,
804                    $radius: high_saturation,
805                    $height: high_value,
806                    $($phantom: core::marker::PhantomData,)?
807                };
808
809                let sampler = $hsv_uniform_ty::<$($ty_param,)* T>::new(low, high);
810
811                $uniform_ty {
812                    sampler,
813                    $($phantom: core::marker::PhantomData,)?
814                }
815            }
816
817            fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
818            where
819                B1: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
820                B2: rand::distributions::uniform::SampleBorrow<Self::X> + Sized,
821            {
822                use crate::convert::FromColorUnclamped;
823                let low_input = $hsv_ty::from_color_unclamped(low_b.borrow().clone());
824                let high_input = $hsv_ty::from_color_unclamped(high_b.borrow().clone());
825
826                let (low_saturation, high_saturation) = low_input.saturation.min_max(high_input.saturation);
827                let (low_value, high_value) = low_input.value.min_max(high_input.value);
828
829                let low = $hsv_ty{
830                    hue: low_input.hue,
831                    $radius: low_saturation,
832                    $height: low_value,
833                    $($phantom: core::marker::PhantomData,)?
834                };
835                let high = $hsv_ty{
836                    hue: high_input.hue,
837                    $radius: high_saturation,
838                    $height: high_value,
839                    $($phantom: core::marker::PhantomData,)?
840                };
841
842                let sampler = $hsv_uniform_ty::<$($ty_param,)* T>::new_inclusive(low, high);
843
844                $uniform_ty {
845                    sampler,
846                    $($phantom: core::marker::PhantomData,)?
847                }
848            }
849
850            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $ty<$($ty_param,)* T> {
851                use crate::convert::FromColorUnclamped;
852                $ty::from_color_unclamped(self.sampler.sample(rng))
853            }
854        }
855    };
856}