1#[cfg(any(feature = "approx", feature = "random"))]
4use core::ops::Mul;
5
6use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
7
8#[cfg(feature = "approx")]
9use approx::{AbsDiffEq, RelativeEq, UlpsEq};
10
11#[cfg(feature = "random")]
12use rand::{
13 distributions::{
14 uniform::{SampleBorrow, SampleUniform, Uniform, UniformSampler},
15 Distribution, Standard,
16 },
17 Rng,
18};
19
20#[cfg(feature = "approx")]
21use crate::{angle::HalfRotation, num::Zero};
22
23#[cfg(feature = "random")]
24use crate::angle::FullRotation;
25
26use crate::{
27 angle::{AngleEq, FromAngle, RealAngle, SignedAngle, UnsignedAngle},
28 num::Trigonometry,
29};
30
31macro_rules! make_hues {
32 ($($(#[$doc:meta])+ struct $name:ident; $iter_name:ident)+) => ($(
33 $(#[$doc])+
34 #[derive(Clone, Copy, Debug, Default)]
41 #[cfg_attr(feature = "serializing", derive(Serialize, Deserialize))]
42 #[repr(C)]
43 pub struct $name<T = f32>(T);
44
45 impl<T> $name<T> {
46 #[inline]
52 pub const fn new(angle: T) -> Self {
53 Self(angle)
54 }
55
56 pub fn into_inner(self) -> T {
61 self.0
62 }
63
64 pub fn into_format<U>(self) -> $name<U>
66 where
67 U: FromAngle<T>,
68 {
69 $name(U::from_angle(self.0))
70 }
71
72 pub fn from_format<U>(hue: $name<U>) -> Self
74 where
75 T: FromAngle<U>,
76 {
77 hue.into_format()
78 }
79 }
80
81 impl<T: RealAngle> $name<T> {
82 #[inline]
84 pub fn from_degrees(degrees: T) -> Self {
85 Self::new(degrees)
86 }
87
88 #[inline]
90 pub fn from_radians(radians: T) -> Self {
91 Self(T::radians_to_degrees(radians))
92 }
93
94 #[inline]
96 pub fn into_raw_degrees(self) -> T {
97 self.0
98 }
99
100 #[inline]
102 pub fn into_raw_radians(self) -> T {
103 T::degrees_to_radians(self.0)
104 }
105 }
106
107 impl<T: RealAngle + SignedAngle> $name<T> {
108 #[inline]
110 pub fn into_degrees(self) -> T {
111 self.0.normalize_signed_angle()
112 }
113
114 #[inline]
116 pub fn into_radians(self) -> T {
117 T::degrees_to_radians(self.0.normalize_signed_angle())
118 }
119 }
120
121 impl<T: RealAngle + UnsignedAngle> $name<T> {
122 #[inline]
124 pub fn into_positive_degrees(self) -> T {
125 self.0.normalize_unsigned_angle()
126 }
127
128 #[inline]
130 pub fn into_positive_radians(self) -> T {
131 T::degrees_to_radians(self.0.normalize_unsigned_angle())
132 }
133 }
134
135 impl<T: RealAngle + Trigonometry> $name<T> {
136 #[inline(always)]
140 pub fn from_cartesian(a: T, b: T) -> Self where T: Add<T, Output = T> + Neg<Output = T> {
141 let hue_rad = T::from_f64(core::f64::consts::PI) + T::atan2(-b, -a);
146 Self::from_radians(hue_rad)
147 }
148
149 #[inline(always)]
155 pub fn into_cartesian(self) -> (T, T) {
156 let (b, a) = self.into_raw_radians().sin_cos();
157 (a, b) }
159 }
160
161 impl<T> $name<&T> {
162 #[inline(always)]
164 pub fn copied(&self) -> $name<T>
165 where
166 T: Copy,
167 {
168 $name(*self.0)
169 }
170
171 #[inline(always)]
173 pub fn cloned(&self) -> $name<T>
174 where
175 T: Clone,
176 {
177 $name(self.0.clone())
178 }
179 }
180
181 impl<T> $name<&mut T> {
182 #[inline(always)]
184 pub fn set(&mut self, value: $name<T>) {
185 *self.0 = value.0;
186 }
187
188 #[inline(always)]
190 pub fn as_ref(&self) -> $name<&T> {
191 $name(&*self.0)
192 }
193
194 #[inline(always)]
196 pub fn copied(&self) -> $name<T>
197 where
198 T: Copy,
199 {
200 $name(*self.0)
201 }
202
203 #[inline(always)]
205 pub fn cloned(&self) -> $name<T>
206 where
207 T: Clone,
208 {
209 $name(self.0.clone())
210 }
211 }
212
213 impl<C> $name<C> {
214 #[inline(always)]
216 pub fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter where &'a Self: IntoIterator {
217 self.into_iter()
218 }
219
220 #[inline(always)]
222 pub fn iter_mut<'a>(&'a mut self) -> <&'a mut Self as IntoIterator>::IntoIter where &'a mut Self: IntoIterator {
223 self.into_iter()
224 }
225
226 #[inline(always)]
228 pub fn get<'a, I, T>(&'a self, index: I) -> Option<$name<&<I as core::slice::SliceIndex<[T]>>::Output>>
229 where
230 T: 'a,
231 C: AsRef<[T]>,
232 I: core::slice::SliceIndex<[T]> + Clone,
233 {
234 self.0.as_ref().get(index).map($name)
235 }
236
237 #[inline(always)]
239 pub fn get_mut<'a, I, T>(&'a mut self, index: I) -> Option<$name<&mut <I as core::slice::SliceIndex<[T]>>::Output>>
240 where
241 T: 'a,
242 C: AsMut<[T]>,
243 I: core::slice::SliceIndex<[T]> + Clone,
244 {
245 self.0.as_mut().get_mut(index).map($name)
246 }
247 }
248
249 #[cfg(feature = "alloc")]
250 impl<T> $name<alloc::vec::Vec<T>> {
251 pub fn with_capacity(capacity: usize) -> Self {
253 Self(alloc::vec::Vec::with_capacity(capacity))
254 }
255
256 pub fn push(&mut self, value: $name<T>) {
258 self.0.push(value.0);
259 }
260
261 pub fn pop(&mut self) -> Option<$name<T>> {
263 self.0.pop().map($name)
264 }
265
266 pub fn clear(&mut self) {
268 self.0.clear();
269 }
270
271 pub fn drain<R>(&mut self, range: R) -> $iter_name<alloc::vec::Drain<T>>
273 where
274 R: core::ops::RangeBounds<usize> + Clone,
275 {
276 $iter_name(self.0.drain(range))
277 }
278 }
279
280 impl<T> From<T> for $name<T> {
281 #[inline]
282 fn from(degrees: T) -> $name<T> {
283 $name(degrees)
284 }
285 }
286
287 impl From<$name<f64>> for f64 {
288 #[inline]
289 fn from(hue: $name<f64>) -> f64 {
290 hue.0.normalize_signed_angle()
291 }
292 }
293
294 impl From<$name<f32>> for f64 {
295 #[inline]
296 fn from(hue: $name<f32>) -> f64 {
297 hue.0.normalize_signed_angle() as f64
298 }
299 }
300
301 impl From<$name<f32>> for f32 {
302 #[inline]
303 fn from(hue: $name<f32>) -> f32 {
304 hue.0.normalize_signed_angle()
305 }
306 }
307
308 impl From<$name<f64>> for f32 {
309 #[inline]
310 fn from(hue: $name<f64>) -> f32 {
311 hue.0.normalize_signed_angle() as f32
312 }
313 }
314
315 impl From<$name<u8>> for u8 {
316 #[inline]
317 fn from(hue: $name<u8>) -> u8 {
318 hue.0
319 }
320 }
321
322 impl<T> PartialEq for $name<T> where T: AngleEq<Mask = bool> + PartialEq {
323 #[inline]
324 fn eq(&self, other: &$name<T>) -> bool {
325 self.0.angle_eq(&other.0)
326 }
327 }
328
329 impl<T> PartialEq<T> for $name<T> where T: AngleEq<Mask = bool> + PartialEq {
330 #[inline]
331 fn eq(&self, other: &T) -> bool {
332 self.0.angle_eq(other)
333 }
334 }
335
336 impl<T> Eq for $name<T> where T: AngleEq<Mask = bool> + Eq {}
337
338
339 #[cfg(feature = "approx")]
340 impl<T> AbsDiffEq for $name<T>
341 where
342 T: RealAngle + SignedAngle + Zero + AngleEq<Mask = bool> + Sub<Output = T> + AbsDiffEq + Clone,
343 T::Epsilon: HalfRotation + Mul<Output = T::Epsilon>,
344 {
345 type Epsilon = T::Epsilon;
346
347 fn default_epsilon() -> Self::Epsilon {
348 T::default_epsilon() * T::Epsilon::half_rotation()
355 }
356
357 fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
358 T::abs_diff_eq(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon)
359 }
360 fn abs_diff_ne(&self, other: &Self, epsilon: T::Epsilon) -> bool {
361 T::abs_diff_ne(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon)
362 }
363 }
364
365 #[cfg(feature = "approx")]
366 impl<T> RelativeEq for $name<T>
367 where
368 T: RealAngle + SignedAngle + Zero + AngleEq<Mask = bool> + Sub<Output = T> + Clone + RelativeEq,
369 T::Epsilon: HalfRotation + Mul<Output = T::Epsilon>,
370 {
371 fn default_max_relative() -> Self::Epsilon {
372 T::default_max_relative()
373 }
374
375 fn relative_eq(
376 &self,
377 other: &Self,
378 epsilon: T::Epsilon,
379 max_relative: T::Epsilon,
380 ) -> bool {
381 T::relative_eq(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon, max_relative)
382 }
383 fn relative_ne(
384 &self,
385 other: &Self,
386 epsilon: Self::Epsilon,
387 max_relative: Self::Epsilon,
388 ) -> bool {
389 T::relative_ne(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon, max_relative)
390 }
391 }
392
393 #[cfg(feature = "approx")]
394 impl<T> UlpsEq for $name<T>
395 where
396 T: RealAngle + SignedAngle + Zero + AngleEq<Mask = bool> + Sub<Output = T> + Clone + UlpsEq,
397 T::Epsilon: HalfRotation + Mul<Output = T::Epsilon>,
398 {
399 fn default_max_ulps() -> u32 {
400 T::default_max_ulps()
401 }
402
403 fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
404 T::ulps_eq(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon, max_ulps)
405 }
406 fn ulps_ne(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
407 T::ulps_ne(&self.clone().into_degrees(), &other.clone().into_degrees(), epsilon, max_ulps)
408 }
409 }
410
411 impl<T: Add<Output=T>> Add<$name<T>> for $name<T> {
412 type Output = $name<T>;
413
414 #[inline]
415 fn add(self, other: $name<T>) -> $name<T> {
416 $name(self.0 + other.0)
417 }
418 }
419
420 impl<T: Add<Output=T>> Add<T> for $name<T> {
421 type Output = $name<T>;
422
423 #[inline]
424 fn add(self, other: T) -> $name<T> {
425 $name(self.0 + other)
426 }
427 }
428
429 impl Add<$name<f32>> for f32 {
430 type Output = $name<f32>;
431
432 #[inline]
433 fn add(self, other: $name<f32>) -> $name<f32> {
434 $name(self + other.0)
435 }
436 }
437
438 impl Add<$name<f64>> for f64 {
439 type Output = $name<f64>;
440
441 #[inline]
442 fn add(self, other: $name<f64>) -> $name<f64> {
443 $name(self + other.0)
444 }
445 }
446
447 impl<T: AddAssign> AddAssign<$name<T>> for $name<T> {
448 #[inline]
449 fn add_assign(&mut self, other: $name<T>) {
450 self.0 += other.0;
451 }
452 }
453
454 impl<T: AddAssign> AddAssign<T> for $name<T> {
455 #[inline]
456 fn add_assign(&mut self, other: T) {
457 self.0 += other;
458 }
459 }
460
461 impl AddAssign<$name<f32>> for f32 {
462 #[inline]
463 fn add_assign(&mut self, other: $name<f32>) {
464 *self += other.0;
465 }
466 }
467
468 impl AddAssign<$name<f64>> for f64 {
469 #[inline]
470 fn add_assign(&mut self, other: $name<f64>){
471 *self += other.0;
472 }
473 }
474
475 impl<T: $crate::num::SaturatingAdd<Output=T>> $crate::num::SaturatingAdd<$name<T>> for $name<T> {
476 type Output = $name<T>;
477
478 #[inline]
479 fn saturating_add(self, other: $name<T>) -> $name<T> {
480 $name(self.0.saturating_add(other.0))
481 }
482 }
483
484 impl<T: $crate::num::SaturatingAdd<Output=T>> $crate::num::SaturatingAdd<T> for $name<T> {
485 type Output = $name<T>;
486
487 #[inline]
488 fn saturating_add(self, other: T) -> $name<T> {
489 $name(self.0.saturating_add(other))
490 }
491 }
492
493 impl<T: Sub<Output=T>> Sub<$name<T>> for $name<T> {
494 type Output = $name<T>;
495
496 #[inline]
497 fn sub(self, other: $name<T>) -> $name<T> {
498 $name(self.0 - other.0)
499 }
500 }
501
502 impl<T: Sub<Output=T>> Sub<T> for $name<T> {
503 type Output = $name<T>;
504
505 #[inline]
506 fn sub(self, other: T) -> $name<T> {
507 $name(self.0 - other)
508 }
509 }
510
511 impl Sub<$name<f32>> for f32 {
512 type Output = $name<f32>;
513
514 #[inline]
515 fn sub(self, other: $name<f32>) -> $name<f32> {
516 $name(self - other.0)
517 }
518 }
519
520 impl Sub<$name<f64>> for f64 {
521 type Output = $name<f64>;
522
523 #[inline]
524 fn sub(self, other: $name<f64>) -> $name<f64> {
525 $name(self - other.0)
526 }
527 }
528
529 impl<T: SubAssign> SubAssign<$name<T>> for $name<T> {
530 #[inline]
531 fn sub_assign(&mut self, other: $name<T>) {
532 self.0 -= other.0;
533 }
534 }
535
536 impl<T: SubAssign> SubAssign<T> for $name<T> {
537 #[inline]
538 fn sub_assign(&mut self, other: T) {
539 self.0 -= other;
540 }
541 }
542
543 impl SubAssign<$name<f32>> for f32 {
544 #[inline]
545 fn sub_assign(&mut self, other: $name<f32>) {
546 *self -= other.0;
547 }
548 }
549
550 impl SubAssign<$name<f64>> for f64 {
551 #[inline]
552 fn sub_assign(&mut self, other: $name<f64>){
553 *self -= other.0;
554 }
555 }
556
557 impl<T: $crate::num::SaturatingSub<Output=T>> $crate::num::SaturatingSub<$name<T>> for $name<T> {
558 type Output = $name<T>;
559
560 #[inline]
561 fn saturating_sub(self, other: $name<T>) -> $name<T> {
562 $name(self.0.saturating_sub(other.0))
563 }
564 }
565
566 impl<T: $crate::num::SaturatingSub<Output=T>> $crate::num::SaturatingSub<T> for $name<T> {
567 type Output = $name<T>;
568
569 #[inline]
570 fn saturating_sub(self, other: T) -> $name<T> {
571 $name(self.0.saturating_sub(other))
572 }
573 }
574
575 impl<C, T> Extend<T> for $name<C> where C: Extend<T> {
576 #[inline(always)]
577 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
578 self.0.extend(iter);
579 }
580 }
581
582 impl<T, const N: usize> IntoIterator for $name<[T; N]> {
583 type Item = $name<T>;
584 type IntoIter = $iter_name<core::array::IntoIter<T, N>>;
585
586 #[inline(always)]
587 fn into_iter(self) -> Self::IntoIter {
588 $iter_name(IntoIterator::into_iter(self.0))
589 }
590 }
591
592 impl<'a, T> IntoIterator for $name<&'a [T]> {
593 type Item = $name<&'a T>;
594 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
595
596 #[inline(always)]
597 fn into_iter(self) -> Self::IntoIter {
598 $iter_name(self.0.into_iter())
599 }
600 }
601
602 impl<'a, T> IntoIterator for $name<&'a mut [T]> {
603 type Item = $name<&'a mut T>;
604 type IntoIter = $iter_name<core::slice::IterMut<'a, T>>;
605
606 #[inline(always)]
607 fn into_iter(self) -> Self::IntoIter {
608 $iter_name(self.0.into_iter())
609 }
610 }
611
612 #[cfg(feature = "alloc")]
613 impl<T> IntoIterator for $name<alloc::vec::Vec<T>> {
614 type Item = $name<T>;
615 type IntoIter = $iter_name<alloc::vec::IntoIter<T>>;
616
617 #[inline(always)]
618 fn into_iter(self) -> Self::IntoIter {
619 $iter_name(self.0.into_iter())
620 }
621 }
622
623 impl<'a, T, const N: usize> IntoIterator for &'a $name<[T; N]> {
624 type Item = $name<&'a T>;
625 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
626
627 #[inline(always)]
628 fn into_iter(self) -> Self::IntoIter {
629 $iter_name((&self.0).into_iter())
630 }
631 }
632
633 impl<'a, 'b, T> IntoIterator for &'a $name<&'b [T]> {
634 type Item = $name<&'a T>;
635 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
636
637 #[inline(always)]
638 fn into_iter(self) -> Self::IntoIter {
639 $iter_name(self.0.into_iter())
640 }
641 }
642
643 impl<'a, 'b, T> IntoIterator for &'a $name<&'b mut [T]> {
644 type Item = $name<&'a T>;
645 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
646
647 #[inline(always)]
648 fn into_iter(self) -> Self::IntoIter {
649 $iter_name((&*self.0).into_iter())
650 }
651 }
652
653 #[cfg(feature = "alloc")]
654 impl<'a, T> IntoIterator for &'a $name<alloc::vec::Vec<T>> {
655 type Item = $name<&'a T>;
656 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
657
658 #[inline(always)]
659 fn into_iter(self) -> Self::IntoIter {
660 $iter_name((&self.0).into_iter())
661 }
662 }
663
664 #[cfg(feature = "alloc")]
665 impl<'a, T> IntoIterator for &'a $name<alloc::boxed::Box<[T]>> {
666 type Item = $name<&'a T>;
667 type IntoIter = $iter_name<core::slice::Iter<'a, T>>;
668
669 #[inline(always)]
670 fn into_iter(self) -> Self::IntoIter {
671 $iter_name((&self.0).into_iter())
672 }
673 }
674
675 impl<'a, T, const N: usize> IntoIterator for &'a mut $name<[T; N]> {
676 type Item = $name<&'a mut T>;
677 type IntoIter = $iter_name<core::slice::IterMut<'a, T>>;
678
679 #[inline(always)]
680 fn into_iter(self) -> Self::IntoIter {
681 $iter_name((&mut self.0).into_iter())
682 }
683 }
684
685 impl<'a, 'b, T> IntoIterator for &'a mut $name<&'b mut [T]> {
686 type Item = $name<&'a mut T>;
687 type IntoIter = $iter_name<core::slice::IterMut<'a, T>>;
688
689 #[inline(always)]
690 fn into_iter(self) -> Self::IntoIter {
691 $iter_name(self.0.into_iter())
692 }
693 }
694
695 #[cfg(feature = "alloc")]
696 impl<'a, T> IntoIterator for &'a mut $name<alloc::vec::Vec<T>> {
697 type Item = $name<&'a mut T>;
698 type IntoIter = $iter_name<core::slice::IterMut<'a, T>>;
699
700 #[inline(always)]
701 fn into_iter(self) -> Self::IntoIter {
702 $iter_name((&mut self.0).into_iter())
703 }
704 }
705
706 #[cfg(feature = "alloc")]
707 impl<'a, T> IntoIterator for &'a mut $name<alloc::boxed::Box<[T]>> {
708 type Item = $name<&'a mut T>;
709 type IntoIter = $iter_name<core::slice::IterMut<'a, T>>;
710
711 #[inline(always)]
712 fn into_iter(self) -> Self::IntoIter {
713 $iter_name((&mut *self.0).into_iter())
714 }
715 }
716
717 #[doc = concat!("Iterator over [`", stringify!($name), "`] values.")]
718 pub struct $iter_name<I>(I);
719
720 impl<I> Iterator for $iter_name<I>
721 where
722 I: Iterator,
723 {
724 type Item = $name<I::Item>;
725
726 #[inline(always)]
727 fn next(&mut self) -> Option<Self::Item> {
728 self.0.next().map($name)
729 }
730
731 #[inline(always)]
732 fn size_hint(&self) -> (usize, Option<usize>) {
733 self.0.size_hint()
734 }
735
736 #[inline(always)]
737 fn count(self) -> usize {
738 self.0.count()
739 }
740 }
741
742 impl<I> DoubleEndedIterator for $iter_name<I>
743 where
744 I: DoubleEndedIterator,
745 {
746 #[inline(always)]
747 fn next_back(&mut self) -> Option<Self::Item> {
748 self.0.next_back().map($name)
749 }
750 }
751
752 impl<I> ExactSizeIterator for $iter_name<I>
753 where
754 I: ExactSizeIterator,
755 {
756 #[inline(always)]
757 fn len(&self) -> usize {
758 self.0.len()
759 }
760 }
761
762 #[cfg(feature = "random")]
763 impl<T> Distribution<$name<T>> for Standard
764 where
765 T: RealAngle + FullRotation + Mul<Output = T>,
766 Standard: Distribution<T>,
767 {
768 #[inline(always)]
769 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $name<T> {
770 $name::from_degrees(rng.gen() * T::full_rotation())
771 }
772 }
773
774 #[cfg(feature = "bytemuck")]
775 unsafe impl<T: bytemuck::Zeroable> bytemuck::Zeroable for $name<T> {}
776 #[cfg(feature = "bytemuck")]
777 unsafe impl<T: bytemuck::Pod> bytemuck::Pod for $name<T> {}
778 )+)
779}
780
781make_hues! {
782 struct LabHue; LabHueIter
788
789 struct LuvHue; LuvHueIter
791
792 struct RgbHue; RgbHueIter
797
798 struct OklabHue; OklabHueIter
802
803 struct Cam16Hue; Cam16HueIter
807}
808
809macro_rules! impl_uniform {
810 ( $uni_ty: ident , $base_ty: ident) => {
811 #[doc = concat!("Sample [`", stringify!($base_ty), "`] uniformly.")]
812 #[cfg(feature = "random")]
813 pub struct $uni_ty<T>
814 where
815 T: SampleUniform,
816 {
817 hue: Uniform<T>,
818 }
819
820 #[cfg(feature = "random")]
821 impl<T> SampleUniform for $base_ty<T>
822 where
823 T: RealAngle
824 + UnsignedAngle
825 + FullRotation
826 + Add<Output = T>
827 + Mul<Output = T>
828 + PartialOrd
829 + Clone
830 + SampleUniform,
831 {
832 type Sampler = $uni_ty<T>;
833 }
834
835 #[cfg(feature = "random")]
836 impl<T> UniformSampler for $uni_ty<T>
837 where
838 T: RealAngle
839 + UnsignedAngle
840 + FullRotation
841 + Add<Output = T>
842 + Mul<Output = T>
843 + PartialOrd
844 + Clone
845 + SampleUniform,
846 {
847 type X = $base_ty<T>;
848
849 fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
850 where
851 B1: SampleBorrow<Self::X> + Sized,
852 B2: SampleBorrow<Self::X> + Sized,
853 {
854 let low = low_b.borrow().clone();
855 let normalized_low = $base_ty::into_positive_degrees(low.clone());
856 let high = high_b.borrow().clone();
857 let normalized_high = $base_ty::into_positive_degrees(high.clone());
858
859 let normalized_high = if normalized_low >= normalized_high && low.0 < high.0 {
860 normalized_high + T::full_rotation()
861 } else {
862 normalized_high
863 };
864
865 $uni_ty {
866 hue: Uniform::new(normalized_low, normalized_high),
867 }
868 }
869
870 fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
871 where
872 B1: SampleBorrow<Self::X> + Sized,
873 B2: SampleBorrow<Self::X> + Sized,
874 {
875 let low = low_b.borrow().clone();
876 let normalized_low = $base_ty::into_positive_degrees(low.clone());
877 let high = high_b.borrow().clone();
878 let normalized_high = $base_ty::into_positive_degrees(high.clone());
879
880 let normalized_high = if normalized_low >= normalized_high && low.0 < high.0 {
881 normalized_high + T::full_rotation()
882 } else {
883 normalized_high
884 };
885
886 $uni_ty {
887 hue: Uniform::new_inclusive(normalized_low, normalized_high),
888 }
889 }
890
891 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $base_ty<T> {
892 $base_ty::from(self.hue.sample(rng) * T::full_rotation())
893 }
894 }
895 };
896}
897
898impl_uniform!(UniformLabHue, LabHue);
899impl_uniform!(UniformRgbHue, RgbHue);
900impl_uniform!(UniformLuvHue, LuvHue);
901impl_uniform!(UniformOklabHue, OklabHue);
902impl_uniform!(UniformCam16Hue, Cam16Hue);
903
904#[cfg(test)]
905mod test {
906 #[cfg(feature = "approx")]
907 mod math {
908 use crate::{
909 angle::{SignedAngle, UnsignedAngle},
910 OklabHue, RgbHue,
911 };
912
913 #[test]
914 fn oklabhue_ab_roundtrip() {
915 for degree in [0.0_f64, 90.0, 30.0, 330.0, 120.0, 240.0] {
916 let hue = OklabHue::from_degrees(degree);
917 let (a, b) = hue.into_cartesian();
918 let roundtrip_hue = OklabHue::from_cartesian(a * 10000.0, b * 10000.0);
919 assert_abs_diff_eq!(roundtrip_hue, hue);
920 }
921 }
922
923 #[test]
924 fn normalize_angle_0_360() {
925 let inp = [
926 -1000.0_f32,
927 -900.0,
928 -360.5,
929 -360.0,
930 -359.5,
931 -240.0,
932 -180.5,
933 -180.0,
934 -179.5,
935 -90.0,
936 -0.5,
937 0.0,
938 0.5,
939 90.0,
940 179.5,
941 180.0,
942 180.5,
943 240.0,
944 359.5,
945 360.0,
946 360.5,
947 900.0,
948 1000.0,
949 ];
950
951 let expected = [
952 80.0_f32, 180.0, 359.5, 0.0, 0.5, 120.0, 179.5, 180.0, 180.5, 270.0, 359.5, 0.0,
953 0.5, 90.0, 179.5, 180.0, 180.5, 240.0, 359.5, 0.0, 0.5, 180.0, 280.0,
954 ];
955
956 let result: Vec<f32> = inp
957 .iter()
958 .map(|x| (*x).normalize_unsigned_angle())
959 .collect();
960 for (res, exp) in result.iter().zip(expected.iter()) {
961 assert_relative_eq!(res, exp);
962 }
963 }
964
965 #[test]
966 fn normalize_angle_180_180() {
967 let inp = [
968 -1000.0_f32,
969 -900.0,
970 -360.5,
971 -360.0,
972 -359.5,
973 -240.0,
974 -180.5,
975 -180.0,
976 -179.5,
977 -90.0,
978 -0.5,
979 0.0,
980 0.5,
981 90.0,
982 179.5,
983 180.0,
984 180.5,
985 240.0,
986 359.5,
987 360.0,
988 360.5,
989 900.0,
990 1000.0,
991 ];
992
993 let expected = [
994 80.0, 180.0, -0.5, 0.0, 0.5, 120.0, 179.5, 180.0, -179.5, -90.0, -0.5, 0.0, 0.5,
995 90.0, 179.5, 180.0, -179.5, -120.0, -0.5, 0.0, 0.5, 180.0, -80.0,
996 ];
997
998 let result: Vec<f32> = inp.iter().map(|x| (*x).normalize_signed_angle()).collect();
999 for (res, exp) in result.iter().zip(expected.iter()) {
1000 assert_relative_eq!(res, exp);
1001 }
1002 }
1003
1004 #[test]
1005 fn float_conversion() {
1006 for i in -180..180 {
1007 let hue = RgbHue::from(4.0 * i as f32);
1008
1009 let degs = hue.into_degrees();
1010 assert!(degs > -180.0 && degs <= 180.0);
1011
1012 let pos_degs = hue.into_positive_degrees();
1013 assert!((0.0..360.0).contains(&pos_degs));
1014
1015 assert_relative_eq!(RgbHue::from(degs), RgbHue::from(pos_degs));
1016 }
1017 }
1018 }
1019
1020 #[cfg(feature = "serializing")]
1021 mod serde {
1022 use crate::RgbHue;
1023
1024 #[test]
1025 fn serialize() {
1026 let serialized = ::serde_json::to_string(&RgbHue::from_degrees(10.2)).unwrap();
1027
1028 assert_eq!(serialized, "10.2");
1029 }
1030
1031 #[test]
1032 fn deserialize() {
1033 let deserialized: RgbHue = ::serde_json::from_str("10.2").unwrap();
1034
1035 assert_eq!(deserialized, RgbHue::from_degrees(10.2));
1036 }
1037 }
1038}