// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::approxeq::ApproxEq; use crate::trig::Trig; use core::cmp::{Eq, PartialEq}; use core::hash::Hash; use core::iter::Sum; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}; use num_traits::real::Real; use num_traits::{Float, FloatConst, NumCast, One, Zero}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "bytemuck")] use bytemuck::{Zeroable, Pod}; /// An angle in radians #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct Angle { pub radians: T, } #[cfg(feature = "bytemuck")] unsafe impl Zeroable for Angle {} #[cfg(feature = "bytemuck")] unsafe impl Pod for Angle {} impl Angle { #[inline] pub fn radians(radians: T) -> Self { Angle { radians } } #[inline] pub fn get(self) -> T { self.radians } } impl Angle where T: Trig, { #[inline] pub fn degrees(deg: T) -> Self { Angle { radians: T::degrees_to_radians(deg), } } #[inline] pub fn to_degrees(self) -> T { T::radians_to_degrees(self.radians) } } impl Angle where T: Rem + Sub + Add + Zero + FloatConst + PartialOrd + Copy, { /// Returns this angle in the [0..2*PI[ range. pub fn positive(&self) -> Self { let two_pi = T::PI() + T::PI(); let mut a = self.radians % two_pi; if a < T::zero() { a = a + two_pi; } Angle::radians(a) } /// Returns this angle in the ]-PI..PI] range. pub fn signed(&self) -> Self { Angle::pi() - (Angle::pi() - *self).positive() } } impl Angle where T: Rem + Mul + Sub + Add + One + FloatConst + Copy, { /// Returns the shortest signed angle between two angles. /// /// Takes wrapping and signs into account. pub fn angle_to(&self, to: Self) -> Self { let two = T::one() + T::one(); let max = T::PI() * two; let d = (to.radians - self.radians) % max; Angle::radians(two * d % max - d) } /// Linear interpolation between two angles, using the shortest path. pub fn lerp(&self, other: Self, t: T) -> Self { *self + self.angle_to(other) * t } } impl Angle where T: Float, { /// Returns true if the angle is a finite number. #[inline] pub fn is_finite(self) -> bool { self.radians.is_finite() } } impl Angle where T: Real, { /// Returns (sin(self), cos(self)). pub fn sin_cos(self) -> (T, T) { self.radians.sin_cos() } } impl Angle where T: Zero, { pub fn zero() -> Self { Angle::radians(T::zero()) } } impl Angle where T: FloatConst + Add, { pub fn pi() -> Self { Angle::radians(T::PI()) } pub fn two_pi() -> Self { Angle::radians(T::PI() + T::PI()) } pub fn frac_pi_2() -> Self { Angle::radians(T::FRAC_PI_2()) } pub fn frac_pi_3() -> Self { Angle::radians(T::FRAC_PI_3()) } pub fn frac_pi_4() -> Self { Angle::radians(T::FRAC_PI_4()) } } impl Angle where T: NumCast + Copy, { /// Cast from one numeric representation to another. #[inline] pub fn cast(&self) -> Angle { self.try_cast().unwrap() } /// Fallible cast from one numeric representation to another. pub fn try_cast(&self) -> Option> { NumCast::from(self.radians).map(|radians| Angle { radians }) } // Convenience functions for common casts. /// Cast angle to `f32`. #[inline] pub fn to_f32(&self) -> Angle { self.cast() } /// Cast angle `f64`. #[inline] pub fn to_f64(&self) -> Angle { self.cast() } } impl> Add for Angle { type Output = Self; fn add(self, other: Self) -> Self { Self::radians(self.radians + other.radians) } } impl> Add<&Self> for Angle { type Output = Self; fn add(self, other: &Self) -> Self { Self::radians(self.radians + other.radians) } } impl Sum for Angle { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl<'a, T: 'a + Add + Copy + Zero> Sum<&'a Self> for Angle { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), Add::add) } } impl> AddAssign for Angle { fn add_assign(&mut self, other: Angle) { self.radians += other.radians; } } impl> Sub> for Angle { type Output = Angle; fn sub(self, other: Angle) -> ::Output { Angle::radians(self.radians - other.radians) } } impl> SubAssign for Angle { fn sub_assign(&mut self, other: Angle) { self.radians -= other.radians; } } impl> Div> for Angle { type Output = T; #[inline] fn div(self, other: Angle) -> T { self.radians / other.radians } } impl> Div for Angle { type Output = Angle; #[inline] fn div(self, factor: T) -> Angle { Angle::radians(self.radians / factor) } } impl> DivAssign for Angle { fn div_assign(&mut self, factor: T) { self.radians /= factor; } } impl> Mul for Angle { type Output = Angle; #[inline] fn mul(self, factor: T) -> Angle { Angle::radians(self.radians * factor) } } impl> MulAssign for Angle { fn mul_assign(&mut self, factor: T) { self.radians *= factor; } } impl> Neg for Angle { type Output = Self; fn neg(self) -> Self { Angle::radians(-self.radians) } } impl> ApproxEq for Angle { #[inline] fn approx_epsilon() -> T { T::approx_epsilon() } #[inline] fn approx_eq_eps(&self, other: &Angle, approx_epsilon: &T) -> bool { self.radians.approx_eq_eps(&other.radians, approx_epsilon) } } #[test] fn wrap_angles() { use core::f32::consts::{FRAC_PI_2, PI}; assert!(Angle::radians(0.0).positive().approx_eq(&Angle::zero())); assert!(Angle::radians(FRAC_PI_2) .positive() .approx_eq(&Angle::frac_pi_2())); assert!(Angle::radians(-FRAC_PI_2) .positive() .approx_eq(&Angle::radians(3.0 * FRAC_PI_2))); assert!(Angle::radians(3.0 * FRAC_PI_2) .positive() .approx_eq(&Angle::radians(3.0 * FRAC_PI_2))); assert!(Angle::radians(5.0 * FRAC_PI_2) .positive() .approx_eq(&Angle::frac_pi_2())); assert!(Angle::radians(2.0 * PI) .positive() .approx_eq(&Angle::zero())); assert!(Angle::radians(-2.0 * PI) .positive() .approx_eq(&Angle::zero())); assert!(Angle::radians(PI).positive().approx_eq(&Angle::pi())); assert!(Angle::radians(-PI).positive().approx_eq(&Angle::pi())); assert!(Angle::radians(FRAC_PI_2) .signed() .approx_eq(&Angle::frac_pi_2())); assert!(Angle::radians(3.0 * FRAC_PI_2) .signed() .approx_eq(&-Angle::frac_pi_2())); assert!(Angle::radians(5.0 * FRAC_PI_2) .signed() .approx_eq(&Angle::frac_pi_2())); assert!(Angle::radians(2.0 * PI).signed().approx_eq(&Angle::zero())); assert!(Angle::radians(-2.0 * PI).signed().approx_eq(&Angle::zero())); assert!(Angle::radians(-PI).signed().approx_eq(&Angle::pi())); assert!(Angle::radians(PI).signed().approx_eq(&Angle::pi())); } #[test] fn lerp() { type A = Angle; let a = A::radians(1.0); let b = A::radians(2.0); assert!(a.lerp(b, 0.25).approx_eq(&Angle::radians(1.25))); assert!(a.lerp(b, 0.5).approx_eq(&Angle::radians(1.5))); assert!(a.lerp(b, 0.75).approx_eq(&Angle::radians(1.75))); assert!(a .lerp(b + A::two_pi(), 0.75) .approx_eq(&Angle::radians(1.75))); assert!(a .lerp(b - A::two_pi(), 0.75) .approx_eq(&Angle::radians(1.75))); assert!(a .lerp(b + A::two_pi() * 5.0, 0.75) .approx_eq(&Angle::radians(1.75))); } #[test] fn sum() { type A = Angle; let angles = [A::radians(1.0), A::radians(2.0), A::radians(3.0)]; let sum = A::radians(6.0); assert_eq!(angles.iter().sum::(), sum); }