1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::approxeq::ApproxEq;
11use crate::trig::Trig;
12use core::cmp::{Eq, PartialEq};
13use core::hash::Hash;
14use core::iter::Sum;
15use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
16use num_traits::real::Real;
17use num_traits::{Float, FloatConst, NumCast, One, Zero};
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20#[cfg(feature = "bytemuck")]
21use bytemuck::{Zeroable, Pod};
22
23/// An angle in radians
24#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
27pub struct Angle<T> {
28 pub radians: T,
29}
30
31#[cfg(feature = "bytemuck")]
32unsafe impl<T: Zeroable> Zeroable for Angle<T> {}
33
34#[cfg(feature = "bytemuck")]
35unsafe impl<T: Pod> Pod for Angle<T> {}
36
37impl<T> Angle<T> {
38 #[inline]
39 pub fn radians(radians: T) -> Self {
40 Angle { radians }
41 }
42
43 #[inline]
44 pub fn get(self) -> T {
45 self.radians
46 }
47}
48
49impl<T> Angle<T>
50where
51 T: Trig,
52{
53 #[inline]
54 pub fn degrees(deg: T) -> Self {
55 Angle {
56 radians: T::degrees_to_radians(deg),
57 }
58 }
59
60 #[inline]
61 pub fn to_degrees(self) -> T {
62 T::radians_to_degrees(self.radians)
63 }
64}
65
66impl<T> Angle<T>
67where
68 T: Rem<Output = T> + Sub<Output = T> + Add<Output = T> + Zero + FloatConst + PartialOrd + Copy,
69{
70 /// Returns this angle in the [0..2*PI[ range.
71 pub fn positive(&self) -> Self {
72 let two_pi: T = T::PI() + T::PI();
73 let mut a: T = self.radians % two_pi;
74 if a < T::zero() {
75 a = a + two_pi;
76 }
77 Angle::radians(a)
78 }
79
80 /// Returns this angle in the ]-PI..PI] range.
81 pub fn signed(&self) -> Self {
82 Angle::pi() - (Angle::pi() - *self).positive()
83 }
84}
85
86impl<T> Angle<T>
87where
88 T: Rem<Output = T>
89 + Mul<Output = T>
90 + Sub<Output = T>
91 + Add<Output = T>
92 + One
93 + FloatConst
94 + Copy,
95{
96 /// Returns the shortest signed angle between two angles.
97 ///
98 /// Takes wrapping and signs into account.
99 pub fn angle_to(&self, to: Self) -> Self {
100 let two: T = T::one() + T::one();
101 let max: T = T::PI() * two;
102 let d: T = (to.radians - self.radians) % max;
103
104 Angle::radians(two * d % max - d)
105 }
106
107 /// Linear interpolation between two angles, using the shortest path.
108 pub fn lerp(&self, other: Self, t: T) -> Self {
109 *self + self.angle_to(other) * t
110 }
111}
112
113impl<T> Angle<T>
114where
115 T: Float,
116{
117 /// Returns true if the angle is a finite number.
118 #[inline]
119 pub fn is_finite(self) -> bool {
120 self.radians.is_finite()
121 }
122}
123
124impl<T> Angle<T>
125where
126 T: Real,
127{
128 /// Returns (sin(self), cos(self)).
129 pub fn sin_cos(self) -> (T, T) {
130 self.radians.sin_cos()
131 }
132}
133
134impl<T> Angle<T>
135where
136 T: Zero,
137{
138 pub fn zero() -> Self {
139 Angle::radians(T::zero())
140 }
141}
142
143impl<T> Angle<T>
144where
145 T: FloatConst + Add<Output = T>,
146{
147 pub fn pi() -> Self {
148 Angle::radians(T::PI())
149 }
150
151 pub fn two_pi() -> Self {
152 Angle::radians(T::PI() + T::PI())
153 }
154
155 pub fn frac_pi_2() -> Self {
156 Angle::radians(T::FRAC_PI_2())
157 }
158
159 pub fn frac_pi_3() -> Self {
160 Angle::radians(T::FRAC_PI_3())
161 }
162
163 pub fn frac_pi_4() -> Self {
164 Angle::radians(T::FRAC_PI_4())
165 }
166}
167
168impl<T> Angle<T>
169where
170 T: NumCast + Copy,
171{
172 /// Cast from one numeric representation to another.
173 #[inline]
174 pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> {
175 self.try_cast().unwrap()
176 }
177
178 /// Fallible cast from one numeric representation to another.
179 pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> {
180 NumCast::from(self.radians).map(|radians| Angle { radians })
181 }
182
183 // Convenience functions for common casts.
184
185 /// Cast angle to `f32`.
186 #[inline]
187 pub fn to_f32(&self) -> Angle<f32> {
188 self.cast()
189 }
190
191 /// Cast angle `f64`.
192 #[inline]
193 pub fn to_f64(&self) -> Angle<f64> {
194 self.cast()
195 }
196}
197
198impl<T: Add<T, Output = T>> Add for Angle<T> {
199 type Output = Self;
200 fn add(self, other: Self) -> Self {
201 Self::radians(self.radians + other.radians)
202 }
203}
204
205impl<T: Copy + Add<T, Output = T>> Add<&Self> for Angle<T> {
206 type Output = Self;
207 fn add(self, other: &Self) -> Self {
208 Self::radians(self.radians + other.radians)
209 }
210}
211
212impl<T: Add + Zero> Sum for Angle<T> {
213 fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
214 iter.fold(Self::zero(), f:Add::add)
215 }
216}
217
218impl<'a, T: 'a + Add + Copy + Zero> Sum<&'a Self> for Angle<T> {
219 fn sum<I: Iterator<Item=&'a Self>>(iter: I) -> Self {
220 iter.fold(Self::zero(), f:Add::add)
221 }
222}
223
224impl<T: AddAssign<T>> AddAssign for Angle<T> {
225 fn add_assign(&mut self, other: Angle<T>) {
226 self.radians += other.radians;
227 }
228}
229
230impl<T: Sub<T, Output = T>> Sub<Angle<T>> for Angle<T> {
231 type Output = Angle<T>;
232 fn sub(self, other: Angle<T>) -> <Self as Sub>::Output {
233 Angle::radians(self.radians - other.radians)
234 }
235}
236
237impl<T: SubAssign<T>> SubAssign for Angle<T> {
238 fn sub_assign(&mut self, other: Angle<T>) {
239 self.radians -= other.radians;
240 }
241}
242
243impl<T: Div<T, Output = T>> Div<Angle<T>> for Angle<T> {
244 type Output = T;
245 #[inline]
246 fn div(self, other: Angle<T>) -> T {
247 self.radians / other.radians
248 }
249}
250
251impl<T: Div<T, Output = T>> Div<T> for Angle<T> {
252 type Output = Angle<T>;
253 #[inline]
254 fn div(self, factor: T) -> Angle<T> {
255 Angle::radians(self.radians / factor)
256 }
257}
258
259impl<T: DivAssign<T>> DivAssign<T> for Angle<T> {
260 fn div_assign(&mut self, factor: T) {
261 self.radians /= factor;
262 }
263}
264
265impl<T: Mul<T, Output = T>> Mul<T> for Angle<T> {
266 type Output = Angle<T>;
267 #[inline]
268 fn mul(self, factor: T) -> Angle<T> {
269 Angle::radians(self.radians * factor)
270 }
271}
272
273impl<T: MulAssign<T>> MulAssign<T> for Angle<T> {
274 fn mul_assign(&mut self, factor: T) {
275 self.radians *= factor;
276 }
277}
278
279impl<T: Neg<Output = T>> Neg for Angle<T> {
280 type Output = Self;
281 fn neg(self) -> Self {
282 Angle::radians(-self.radians)
283 }
284}
285
286impl<T: ApproxEq<T>> ApproxEq<T> for Angle<T> {
287 #[inline]
288 fn approx_epsilon() -> T {
289 T::approx_epsilon()
290 }
291
292 #[inline]
293 fn approx_eq_eps(&self, other: &Angle<T>, approx_epsilon: &T) -> bool {
294 self.radians.approx_eq_eps(&other.radians, approx_epsilon)
295 }
296}
297
298#[test]
299fn wrap_angles() {
300 use core::f32::consts::{FRAC_PI_2, PI};
301
302 assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero()));
303 assert!(Angle::radians(FRAC_PI_2)
304 .positive()
305 .approx_eq(&Angle::frac_pi_2()));
306 assert!(Angle::radians(-FRAC_PI_2)
307 .positive()
308 .approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
309 assert!(Angle::radians(3.0 * FRAC_PI_2)
310 .positive()
311 .approx_eq(&Angle::radians(3.0 * FRAC_PI_2)));
312 assert!(Angle::radians(5.0 * FRAC_PI_2)
313 .positive()
314 .approx_eq(&Angle::frac_pi_2()));
315 assert!(Angle::radians(2.0 * PI)
316 .positive()
317 .approx_eq(&Angle::zero()));
318 assert!(Angle::radians(-2.0 * PI)
319 .positive()
320 .approx_eq(&Angle::zero()));
321 assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi()));
322 assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi()));
323
324 assert!(Angle::radians(FRAC_PI_2)
325 .signed()
326 .approx_eq(&Angle::frac_pi_2()));
327 assert!(Angle::radians(3.0 * FRAC_PI_2)
328 .signed()
329 .approx_eq(&-Angle::frac_pi_2()));
330 assert!(Angle::radians(5.0 * FRAC_PI_2)
331 .signed()
332 .approx_eq(&Angle::frac_pi_2()));
333 assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero()));
334 assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero()));
335 assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi()));
336 assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi()));
337}
338
339#[test]
340fn lerp() {
341 type A = Angle<f32>;
342
343 let a: Angle = A::radians(1.0);
344 let b: Angle = A::radians(2.0);
345 assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25)));
346 assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5)));
347 assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75)));
348 assert!(a
349 .lerp(b + A::two_pi(), 0.75)
350 .approx_eq(&Angle::radians(1.75)));
351 assert!(a
352 .lerp(b - A::two_pi(), 0.75)
353 .approx_eq(&Angle::radians(1.75)));
354 assert!(a
355 .lerp(b + A::two_pi() * 5.0, 0.75)
356 .approx_eq(&Angle::radians(1.75)));
357}
358
359#[test]
360fn sum() {
361 type A = Angle<f32>;
362 let angles: [Angle; 3] = [A::radians(1.0), A::radians(2.0), A::radians(3.0)];
363 let sum: Angle = A::radians(6.0);
364 assert_eq!(angles.iter().sum::<A>(), sum);
365}
366