euclid/
angle.rs

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