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};
20use num_traits::real::Real;
21use num_traits::{Float, FloatConst, NumCast, One, Zero};
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25/// An angle in radians
26#[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    // This implementation could be derived, but the derive would require an `extern crate std`.
44    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    /// Returns this angle in the [0..2*PI[ range.
89    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    /// Returns this angle in the ]-PI..PI] range.
99    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    /// Returns the shortest signed angle between two angles.
115    ///
116    /// Takes wrapping and signs into account.
117    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    /// Linear interpolation between two angles, using the shortest path.
126    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    /// Returns `true` if the angle is a finite number.
136    #[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    /// Returns `(sin(self), cos(self))`.
147    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    /// Cast from one numeric representation to another.
191    #[inline]
192    pub fn cast<NewT: NumCast>(&self) -> Angle<NewT> {
193        self.try_cast().unwrap()
194    }
195
196    /// Fallible cast from one numeric representation to another.
197    pub fn try_cast<NewT: NumCast>(&self) -> Option<Angle<NewT>> {
198        NumCast::from(self.radians).map(|radians| Angle { radians })
199    }
200
201    // Convenience functions for common casts.
202
203    /// Cast angle to `f32`.
204    #[inline]
205    pub fn to_f32(&self) -> Angle<f32> {
206        self.cast()
207    }
208
209    /// Cast angle `f64`.
210    #[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}