// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Angle units for type-safe, self-documenting code. use std::fmt; use std::f64; use std::iter; use std::ops::*; use rand::{Rand, Rng}; use rand::distributions::range::SampleRange; use num_traits::cast; use structure::*; use approx::ApproxEq; use num::BaseFloat; /// An angle, in radians. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "eders", derive(Serialize, Deserialize))] pub struct Rad(pub S); /// An angle, in degrees. /// /// This type is marked as `#[repr(C)]`. #[repr(C)] #[derive(Copy, Clone, PartialEq, PartialOrd)] #[cfg_attr(feature = "eders", derive(Serialize, Deserialize))] pub struct Deg(pub S); impl From> for Deg where S: BaseFloat { #[inline] fn from(rad: Rad) -> Deg { Deg(rad.0 * cast(180.0 / f64::consts::PI).unwrap()) } } impl From> for Rad where S: BaseFloat { #[inline] fn from(deg: Deg) -> Rad { Rad(deg.0 * cast(f64::consts::PI / 180.0).unwrap()) } } macro_rules! impl_angle { ($Angle:ident, $fmt:expr, $full_turn:expr, $hi:expr) => { impl Zero for $Angle { #[inline] fn zero() -> $Angle { $Angle(S::zero()) } #[inline] fn is_zero(&self) -> bool { ulps_eq!(self, &Self::zero()) } } impl iter::Sum<$Angle> for $Angle { #[inline] fn sum>>(iter: I) -> $Angle { iter.fold($Angle::zero(), Add::add) } } impl<'a, S: 'a + BaseFloat> iter::Sum<&'a $Angle> for $Angle { #[inline] fn sum>>(iter: I) -> $Angle { iter.fold($Angle::zero(), Add::add) } } impl Angle for $Angle { type Unitless = S; #[inline] fn full_turn() -> $Angle { $Angle(cast($full_turn).unwrap()) } #[inline] fn sin(self) -> S { Rad::from(self).0.sin() } #[inline] fn cos(self) -> S { Rad::from(self).0.cos() } #[inline] fn tan(self) -> S { Rad::from(self).0.tan() } #[inline] fn sin_cos(self) -> (S, S) { Rad::from(self).0.sin_cos() } #[inline] fn asin(a: S) -> $Angle { Rad(a.asin()).into() } #[inline] fn acos(a: S) -> $Angle { Rad(a.acos()).into() } #[inline] fn atan(a: S) -> $Angle { Rad(a.atan()).into() } #[inline] fn atan2(a: S, b: S) -> $Angle { Rad(a.atan2(b)).into() } } impl Neg for $Angle { type Output = $Angle; #[inline] fn neg(self) -> $Angle { $Angle(-self.0) } } impl<'a, S: BaseFloat> Neg for &'a $Angle { type Output = $Angle; #[inline] fn neg(self) -> $Angle { $Angle(-self.0) } } impl_operator!( Add<$Angle > for $Angle { fn add(lhs, rhs) -> $Angle { $Angle(lhs.0 + rhs.0) } }); impl_operator!( Sub<$Angle > for $Angle { fn sub(lhs, rhs) -> $Angle { $Angle(lhs.0 - rhs.0) } }); impl_operator!( Div<$Angle > for $Angle { fn div(lhs, rhs) -> S { lhs.0 / rhs.0 } }); impl_operator!( Rem<$Angle > for $Angle { fn rem(lhs, rhs) -> $Angle { $Angle(lhs.0 % rhs.0) } }); impl_assignment_operator!( AddAssign<$Angle > for $Angle { fn add_assign(&mut self, other) { self.0 += other.0; } }); impl_assignment_operator!( SubAssign<$Angle > for $Angle { fn sub_assign(&mut self, other) { self.0 -= other.0; } }); impl_assignment_operator!( RemAssign<$Angle > for $Angle { fn rem_assign(&mut self, other) { self.0 %= other.0; } }); impl_operator!( Mul for $Angle { fn mul(lhs, scalar) -> $Angle { $Angle(lhs.0 * scalar) } }); impl_operator!( Div for $Angle { fn div(lhs, scalar) -> $Angle { $Angle(lhs.0 / scalar) } }); impl_assignment_operator!( MulAssign for $Angle { fn mul_assign(&mut self, scalar) { self.0 *= scalar; } }); impl_assignment_operator!( DivAssign for $Angle { fn div_assign(&mut self, scalar) { self.0 /= scalar; } }); impl ApproxEq for $Angle { type Epsilon = S::Epsilon; #[inline] fn default_epsilon() -> S::Epsilon { S::default_epsilon() } #[inline] fn default_max_relative() -> S::Epsilon { S::default_max_relative() } #[inline] fn default_max_ulps() -> u32 { S::default_max_ulps() } #[inline] fn relative_eq(&self, other: &Self, epsilon: S::Epsilon, max_relative: S::Epsilon) -> bool { S::relative_eq(&self.0, &other.0, epsilon, max_relative) } #[inline] fn ulps_eq(&self, other: &Self, epsilon: S::Epsilon, max_ulps: u32) -> bool { S::ulps_eq(&self.0, &other.0, epsilon, max_ulps) } } impl Rand for $Angle { #[inline] fn rand(rng: &mut R) -> $Angle { $Angle(rng.gen_range(cast(-$hi).unwrap(), cast($hi).unwrap())) } } impl fmt::Debug for $Angle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, $fmt, self.0) } } } } impl_angle!(Rad, "{:?} rad", f64::consts::PI * 2.0, f64::consts::PI); impl_angle!(Deg, "{:?}°", 360, 180);