1use crate::{
4 bool_mask::HasBoolMask,
5 num::{Real, Round},
6};
7
8#[cfg(feature = "wide")]
9mod wide;
10
11pub trait HalfRotation {
13 #[must_use]
15 fn half_rotation() -> Self;
16}
17
18pub trait FullRotation {
20 #[must_use]
22 fn full_rotation() -> Self;
23}
24
25pub trait RealAngle: Real {
28 #[must_use]
30 fn radians_to_degrees(self) -> Self;
31
32 #[must_use]
34 fn degrees_to_radians(self) -> Self;
35}
36
37pub trait AngleEq: HasBoolMask {
39 #[must_use]
41 fn angle_eq(&self, other: &Self) -> Self::Mask;
42}
43
44pub trait SignedAngle {
47 #[must_use]
49 fn normalize_signed_angle(self) -> Self;
50}
51
52pub trait UnsignedAngle {
54 #[must_use]
56 fn normalize_unsigned_angle(self) -> Self;
57}
58
59pub trait FromAngle<T> {
61 fn from_angle(angle: T) -> Self;
63}
64
65impl<T> FromAngle<T> for T {
66 #[inline]
67 fn from_angle(angle: Self) -> Self {
68 angle
69 }
70}
71
72pub trait IntoAngle<T> {
74 fn into_angle(self) -> T;
76}
77
78impl<T, U> IntoAngle<U> for T
79where
80 U: FromAngle<T>,
81{
82 #[inline]
83 fn into_angle(self) -> U {
84 U::from_angle(self)
85 }
86}
87
88macro_rules! impl_angle_float {
89 ($($ty: ident),+) => {
90 $(
91 impl HalfRotation for $ty {
92 #[inline]
93 fn half_rotation() -> Self {
94 180.0
95 }
96 }
97
98 impl FullRotation for $ty {
99 #[inline]
100 fn full_rotation() -> Self {
101 360.0
102 }
103 }
104
105 impl RealAngle for $ty {
106 #[inline]
107 fn degrees_to_radians(self) -> Self {
108 self.to_radians()
109 }
110
111 #[inline]
112 fn radians_to_degrees(self) -> Self {
113 self.to_degrees()
114 }
115 }
116
117 impl AngleEq for $ty {
118 #[inline]
119 fn angle_eq(&self, other: &Self) -> bool {
120 self.normalize_unsigned_angle() == other.normalize_unsigned_angle()
121 }
122 }
123
124 impl SignedAngle for $ty {
125 #[inline]
126 fn normalize_signed_angle(self) -> Self {
127 self - Round::ceil(((self + 180.0) / 360.0) - 1.0) * 360.0
128 }
129 }
130
131 impl UnsignedAngle for $ty {
132 #[inline]
133 fn normalize_unsigned_angle(self) -> Self {
134 self - (Round::floor(self / 360.0) * 360.0)
135 }
136 }
137 )+
138 };
139}
140
141macro_rules! impl_from_angle_float {
142 ($ty: ident to $other_ty: ident) => {
143 impl FromAngle<$other_ty> for $ty {
144 #[inline]
145 fn from_angle(angle: $other_ty) -> Self {
146 angle as $ty
147 }
148 }
149 };
150}
151
152macro_rules! impl_from_angle_u8 {
153 ($($float_ty: ident),*) => {
154 $(
155 impl FromAngle<u8> for $float_ty {
156 #[inline]
157 fn from_angle(angle: u8) -> Self {
158 (angle as $float_ty / 256.0) * Self::full_rotation()
159 }
160 }
161
162 impl FromAngle<$float_ty> for u8 {
163 #[inline]
164 fn from_angle(angle: $float_ty) -> Self {
165 let normalized = angle.normalize_unsigned_angle() / $float_ty::full_rotation();
166 let rounded = (normalized * 256.0).round();
167
168 if rounded > 255.5 {
169 0
170 } else {
171 rounded as u8
172 }
173 }
174 }
175 )*
176 };
177}
178
179impl_angle_float!(f32, f64);
180impl_from_angle_float!(f32 to f64);
181impl_from_angle_float!(f64 to f32);
182impl_from_angle_u8!(f32, f64);
183
184impl HalfRotation for u8 {
185 #[inline]
186 fn half_rotation() -> Self {
187 128
188 }
189}
190
191impl AngleEq for u8 {
192 #[inline]
193 fn angle_eq(&self, other: &Self) -> bool {
194 self == other
195 }
196}
197
198impl UnsignedAngle for u8 {
199 #[inline]
200 fn normalize_unsigned_angle(self) -> Self {
201 self
202 }
203}
204
205#[cfg(test)]
206mod test {
207 use crate::RgbHue;
208
209 #[test]
210 fn f32_to_u8() {
211 let hue_f32 = RgbHue::new(180.0f32);
212 let hue_u8 = hue_f32.into_format::<u8>();
213 assert_eq!(hue_u8, RgbHue::new(128u8));
214 }
215
216 #[test]
217 fn u8_to_f32() {
218 let hue_f32 = RgbHue::new(128u8);
219 let hue_u8 = hue_f32.into_format::<f32>();
220 assert_eq!(hue_u8, RgbHue::new(180.0f32));
221 }
222}