1use crate::approxeq::ApproxEq;
11use crate::trig::Trig;
12
13use core::cmp::{Eq, PartialEq};
14use core::hash::Hash;
15use core::iter::Sum;
16use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
17
18#[cfg(feature = "bytemuck")]
19use bytemuck::{Pod, Zeroable};
20use num_traits::real::Real;
21use num_traits::{Float, FloatConst, NumCast, One, Zero};
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub struct Angle<T> {
29 pub radians: T,
30}
31
32#[cfg(feature = "bytemuck")]
33unsafe impl<T: Zeroable> Zeroable for Angle<T> {}
34
35#[cfg(feature = "bytemuck")]
36unsafe impl<T: Pod> Pod for Angle<T> {}
37
38#[cfg(feature = "arbitrary")]
39impl<'a, T> arbitrary::Arbitrary<'a> for Angle<T>
40where
41 T: arbitrary::Arbitrary<'a>,
42{
43 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
45 Ok(Angle {
46 radians: arbitrary::Arbitrary::arbitrary(u)?,
47 })
48 }
49
50 fn size_hint(depth: usize) -> (usize, Option<usize>) {
51 <T as arbitrary::Arbitrary>::size_hint(depth)
52 }
53}
54
55impl<T> Angle<T> {
56 #[inline]
57 pub fn radians(radians: T) -> Self {
58 Angle { radians }
59 }
60
61 #[inline]
62 pub fn get(self) -> T {
63 self.radians
64 }
65}
66
67impl<T> Angle<T>
68where
69 T: Trig,
70{
71 #[inline]
72 pub fn degrees(deg: T) -> Self {
73 Angle {
74 radians: T::degrees_to_radians(deg),
75 }
76 }
77
78 #[inline]
79 pub fn to_degrees(self) -> T {
80 T::radians_to_degrees(self.radians)
81 }
82}
83
84impl<T> Angle<T>
85where
86 T: Rem<Output = T> + Sub<Output = T> + Add<Output = T> + Zero + FloatConst + PartialOrd + Copy,
87{
88 pub fn positive(&self) -> Self {
90 let two_pi = T::PI() + T::PI();
91 let mut a = self.radians % two_pi;
92 if a < T::zero() {
93 a = a + two_pi;
94 }
95 Angle::radians(a)
96 }
97
98 pub fn signed(&self) -> Self {
100 Angle::pi() - (Angle::pi() - *self).positive()
101 }
102}
103
104impl<T> Angle<T>
105where
106 T: Rem<Output = T>
107 + Mul<Output = T>
108 + Sub<Output = T>
109 + Add<Output = T>
110 + One
111 + FloatConst
112 + Copy,
113{
114 pub fn angle_to(&self, to: Self) -> Self {
118 let two = T::one() + T::one();
119 let max = T::PI() * two;
120 let d = (to.radians - self.radians) % max;
121
122 Angle::radians(two * d % max - d)
123 }
124
125 pub fn lerp(&self, other: Self, t: T) -> Self {
127 *self + self.angle_to(other) * t
128 }
129}
130
131impl<T> Angle<T>
132where
133 T: Float,
134{
135 #[inline]
137 pub fn is_finite(self) -> bool {
138 self.radians.is_finite()
139 }
140}
141
142impl<T> Angle<T>
143where
144 T: Real,
145{
146 pub fn sin_cos(self) -> (T, T) {
148 self.radians.sin_cos()
149 }
150}
151
152impl<T> Angle<T>
153where
154 T: Zero,
155{
156 pub fn zero() -> Self {
157 Angle::radians(T::zero())
158 }
159}
160
161impl<T> Angle<T>
162where
163 T: FloatConst + Add<Output = T>,
164{
165 pub fn pi() -> Self {
166 Angle::radians(T::PI())
167 }
168
169 pub fn two_pi() -> Self {
170 Angle::radians(T::PI() + T::PI())
171 }
172
173 pub fn frac_pi_2() -> Self {
174 Angle::radians(T::FRAC_PI_2())
175 }
176
177 pub fn frac_pi_3() -> Self {
178 Angle::radians(T::FRAC_PI_3())
179 }
180
181 pub fn frac_pi_4() -> Self {
182 Angle::radians(T::FRAC_PI_4())
183 }
184}
185
186impl<T> Angle<T>
187where
188 T: NumCast + Copy,
189{
190 #[inline]
192 pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> {
193 self.try_cast().unwrap()
194 }
195
196 pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> {
198 NumCast::from(self.radians).map(|radians| Angle { radians })
199 }
200
201 #[inline]
205 pub fn to_f32(&self) -> Angle<f32> {
206 self.cast()
207 }
208
209 #[inline]
211 pub fn to_f64(&self) -> Angle<f64> {
212 self.cast()
213 }
214}
215
216impl<T: Add<T, Output = T>> Add for Angle<T> {
217 type Output = Self;
218 fn add(self, other: Self) -> Self {
219 Self::radians(self.radians + other.radians)
220 }
221}
222
223impl<T: Copy + Add<T, Output = T>> Add<&Self> for Angle<T> {
224 type Output = Self;
225 fn add(self, other: &Self) -> Self {
226 Self::radians(self.radians + other.radians)
227 }
228}
229
230impl<T: Add + Zero> Sum for Angle<T> {
231 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
232 iter.fold(Self::zero(), Add::add)
233 }
234}
235
236impl<'a, T: 'a + Add + Copy + Zero> Sum<&'a Self> for Angle<T> {
237 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
238 iter.fold(Self::zero(), Add::add)
239 }
240}
241
242impl<T: AddAssign<T>> AddAssign for Angle<T> {
243 fn add_assign(&mut self, other: Angle<T>) {
244 self.radians += other.radians;
245 }
246}
247
248impl<T: Sub<T, Output = T>> Sub<Angle<T>> for Angle<T> {
249 type Output = Angle<T>;
250 fn sub(self, other: Angle<T>) -> <Self as Sub>::Output {
251 Angle::radians(self.radians - other.radians)
252 }
253}
254
255impl<T: SubAssign<T>> SubAssign for Angle<T> {
256 fn sub_assign(&mut self, other: Angle<T>) {
257 self.radians -= other.radians;
258 }
259}
260
261impl<T: Div<T, Output = T>> Div<Angle<T>> for Angle<T> {
262 type Output = T;
263 #[inline]
264 fn div(self, other: Angle<T>) -> T {
265 self.radians / other.radians
266 }
267}
268
269impl<T: Div<T, Output = T>> Div<T> for Angle<T> {
270 type Output = Angle<T>;
271 #[inline]
272 fn div(self, factor: T) -> Angle<T> {
273 Angle::radians(self.radians / factor)
274 }
275}
276
277impl<T: DivAssign<T>> DivAssign<T> for Angle<T> {
278 fn div_assign(&mut self, factor: T) {
279 self.radians /= factor;
280 }
281}
282
283impl<T: Mul<T, Output = T>> Mul<T> for Angle<T> {
284 type Output = Angle<T>;
285 #[inline]
286 fn mul(self, factor: T) -> Angle<T> {
287 Angle::radians(self.radians * factor)
288 }
289}
290
291impl<T: MulAssign<T>> MulAssign<T> for Angle<T> {
292 fn mul_assign(&mut self, factor: T) {
293 self.radians *= factor;
294 }
295}
296
297impl<T: Neg<Output = T>> Neg for Angle<T> {
298 type Output = Self;
299 fn neg(self) -> Self {
300 Angle::radians(-self.radians)
301 }
302}
303
304impl<T: ApproxEq<T>> ApproxEq<T> for Angle<T> {
305 #[inline]
306 fn approx_epsilon() -> T {
307 T::approx_epsilon()
308 }
309
310 #[inline]
311 fn approx_eq_eps(&self, other: &Angle<T>, approx_epsilon: &T) -> bool {
312 self.radians.approx_eq_eps(&other.radians, approx_epsilon)
313 }
314}
315
316#[test]
317fn wrap_angles() {
318 use core::f32::consts::{FRAC_PI_2, PI};
319
320 assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero()));
321 assert!(Angle::radians(FRAC_PI_2)
322 .positive()
323 .approx_eq(&Angle::frac_pi_2()));
324 assert!(Angle::radians(-FRAC_PI_2)
325 .positive()
326 .approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
327 assert!(Angle::radians(3.0 * FRAC_PI_2)
328 .positive()
329 .approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
330 assert!(Angle::radians(5.0 * FRAC_PI_2)
331 .positive()
332 .approx_eq(&Angle::frac_pi_2()));
333 assert!(Angle::radians(2.0 * PI)
334 .positive()
335 .approx_eq(&Angle::zero()));
336 assert!(Angle::radians(-2.0 * PI)
337 .positive()
338 .approx_eq(&Angle::zero()));
339 assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi()));
340 assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi()));
341
342 assert!(Angle::radians(FRAC_PI_2)
343 .signed()
344 .approx_eq(&Angle::frac_pi_2()));
345 assert!(Angle::radians(3.0 * FRAC_PI_2)
346 .signed()
347 .approx_eq(&-Angle::frac_pi_2()));
348 assert!(Angle::radians(5.0 * FRAC_PI_2)
349 .signed()
350 .approx_eq(&Angle::frac_pi_2()));
351 assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero()));
352 assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero()));
353 assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi()));
354 assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi()));
355}
356
357#[test]
358fn lerp() {
359 type A = Angle<f32>;
360
361 let a = A::radians(1.0);
362 let b = A::radians(2.0);
363 assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25)));
364 assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5)));
365 assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75)));
366 assert!(a
367 .lerp(b + A::two_pi(), 0.75)
368 .approx_eq(&Angle::radians(1.75)));
369 assert!(a
370 .lerp(b - A::two_pi(), 0.75)
371 .approx_eq(&Angle::radians(1.75)));
372 assert!(a
373 .lerp(b + A::two_pi() * 5.0, 0.75)
374 .approx_eq(&Angle::radians(1.75)));
375}
376
377#[test]
378fn sum() {
379 type A = Angle<f32>;
380 let angles = [A::radians(1.0), A::radians(2.0), A::radians(3.0)];
381 let sum = A::radians(6.0);
382 assert_eq!(angles.iter().sum::<A>(), sum);
383}