diff --git a/src/lib.rs b/src/lib.rs index cc8b63ad..c99cd80c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ pub use vector::{ }; pub use rect::{Rect, TypedRect, rect}; -pub use rotation::{TypedRotation2D, Rotation2D, TypedRotation3D, Rotation3D}; +pub use rotation::{TypedRotation2D, Rotation2D, TypedRotation3D, Rotation3D, Angle}; pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D}; #[cfg(feature = "unstable")] pub use side_offsets::SideOffsets2DSimdI32; pub use size::{Size2D, TypedSize2D, size2}; @@ -105,18 +105,6 @@ mod vector; #[derive(Clone, Copy)] pub struct UnknownUnit; -/// Unit for angles in radians. -pub struct Rad; - -/// Unit for angles in degrees. -pub struct Deg; - -/// A value in radians. -pub type Radians = Length; - -/// A value in Degrees. -pub type Degrees = Length; - /// Temporary alias to facilitate the transition to the new naming scheme #[deprecated] pub type Matrix2D = Transform2D; @@ -136,3 +124,7 @@ pub type TypedMatrix4D = TypedTransform3D; /// Temporary alias to facilitate the transition to the new naming scheme #[deprecated] pub type ScaleFactor = TypedScale; + +/// Temporary alias to facilitate the transition to the new naming scheme +#[deprecated] +pub use Angle as Radians; diff --git a/src/rotation.rs b/src/rotation.rs index 489e4788..39f7d233 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -10,11 +10,106 @@ use approxeq::ApproxEq; use num_traits::{Float, One, Zero}; use std::fmt; -use std::ops::{Add, Neg, Mul, Sub, Div}; +use std::ops::{Add, Neg, Mul, Sub, Div, AddAssign, SubAssign, MulAssign, DivAssign}; use std::marker::PhantomData; use trig::Trig; use {TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D, Vector3D, point2, point3, vec3}; -use {TypedTransform3D, TypedTransform2D, UnknownUnit, Radians}; +use {TypedTransform3D, TypedTransform2D, UnknownUnit}; + +/// An angle in radians +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Hash)] +pub struct Angle { + pub radians: T, +} + +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> Add for Angle { + type Output = Angle; + fn add(self, other: Angle) -> Angle { + Angle::radians(self.radians + other.radians) + } +} + +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; + } +} + define_matrix! { /// A transform that can represent rotations in 2d, represented as an angle in radians. @@ -29,15 +124,15 @@ pub type Rotation2D = TypedRotation2D; impl TypedRotation2D { #[inline] /// Creates a rotation from an angle in radians. - pub fn new(angle: Radians) -> Self { + pub fn new(angle: Angle) -> Self { TypedRotation2D { - angle: angle.0, + angle: angle.radians, _unit: PhantomData, } } pub fn radians(angle: T) -> Self { - Self::new(Radians::new(angle)) + Self::new(Angle::radians(angle)) } /// Creates the identity rotation. @@ -49,9 +144,9 @@ impl TypedRotation2D { impl TypedRotation2D where T: Clone { - /// Returns self.angle as a strongly typed `Radians`. - pub fn get_angle(&self) -> Radians { - Radians::new(self.angle.clone()) + /// Returns self.angle as a strongly typed `Angle`. + pub fn get_angle(&self) -> Angle { + Angle::radians(self.angle.clone()) } } @@ -207,34 +302,34 @@ where T: Copy + Clone + } /// Creates a rotation around a given axis. - pub fn around_axis(axis: TypedVector3D, angle: Radians) -> Self { + pub fn around_axis(axis: TypedVector3D, angle: Angle) -> Self { let axis = axis.normalize(); let two = T::one() + T::one(); - let (sin, cos) = Float::sin_cos(angle.get() / two); + let (sin, cos) = Float::sin_cos(angle.radians / two); Self::quaternion(axis.x * sin, axis.y * sin, axis.z * sin, cos) } /// Creates a rotation around the x axis. - pub fn around_x(angle: Radians) -> Self { + pub fn around_x(angle: Angle) -> Self { let zero = Zero::zero(); let two = T::one() + T::one(); - let (sin, cos) = Float::sin_cos(angle.get() / two); + let (sin, cos) = Float::sin_cos(angle.radians / two); Self::quaternion(sin, zero, zero, cos) } /// Creates a rotation around the y axis. - pub fn around_y(angle: Radians) -> Self { + pub fn around_y(angle: Angle) -> Self { let zero = Zero::zero(); let two = T::one() + T::one(); - let (sin, cos) = Float::sin_cos(angle.get() / two); + let (sin, cos) = Float::sin_cos(angle.radians / two); Self::quaternion(zero, sin, zero, cos) } /// Creates a rotation around the z axis. - pub fn around_z(angle: Radians) -> Self { + pub fn around_z(angle: Angle) -> Self { let zero = Zero::zero(); let two = T::one() + T::one(); - let (sin, cos) = Float::sin_cos(angle.get() / two); + let (sin, cos) = Float::sin_cos(angle.radians / two); Self::quaternion(zero, zero, sin, cos) } @@ -245,7 +340,7 @@ where T: Copy + Clone + /// - Roll (also calld bank) is a rotation around the x axis. /// - Pitch (also calld bearing) is a rotation around the y axis. /// - Yaw (also calld heading) is a rotation around the z axis. - pub fn euler(roll: Radians, pitch: Radians, yaw: Radians) -> Self { + pub fn euler(roll: Angle, pitch: Angle, yaw: Angle) -> Self { let half = T::one() / (T::one() + T::one()); let (sy, cy) = Float::sin_cos(half * yaw.get()); @@ -532,9 +627,9 @@ fn simple_rotation_2d() { fn simple_rotation_3d_in_2d() { use std::f32::consts::{PI, FRAC_PI_2}; let ri = Rotation3D::identity(); - let r90 = Rotation3D::around_z(Radians::new(FRAC_PI_2)); - let rm90 = Rotation3D::around_z(Radians::new(-FRAC_PI_2)); - let r180 = Rotation3D::around_z(Radians::new(PI)); + let r90 = Rotation3D::around_z(Angle::radians(FRAC_PI_2)); + let rm90 = Rotation3D::around_z(Angle::radians(-FRAC_PI_2)); + let r180 = Rotation3D::around_z(Angle::radians(PI)); assert!(ri.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(1.0, 2.0))); assert!(r90.rotate_point2d(&point2(1.0, 2.0)).approx_eq(&point2(-2.0, 1.0))); @@ -551,9 +646,9 @@ fn simple_rotation_3d_in_2d() { #[test] fn pre_post() { use std::f32::consts::{FRAC_PI_2}; - let r1 = Rotation3D::around_x(Radians::new(FRAC_PI_2)); - let r2 = Rotation3D::around_y(Radians::new(FRAC_PI_2)); - let r3 = Rotation3D::around_z(Radians::new(FRAC_PI_2)); + let r1 = Rotation3D::around_x(Angle::radians(FRAC_PI_2)); + let r2 = Rotation3D::around_y(Angle::radians(FRAC_PI_2)); + let r3 = Rotation3D::around_z(Angle::radians(FRAC_PI_2)); let t1 = r1.to_transform(); let t2 = r2.to_transform(); @@ -578,15 +673,15 @@ fn to_transform3d() { use std::f32::consts::{PI, FRAC_PI_2}; let rotations = [ Rotation3D::identity(), - Rotation3D::around_x(Radians::new(FRAC_PI_2)), - Rotation3D::around_x(Radians::new(-FRAC_PI_2)), - Rotation3D::around_x(Radians::new(PI)), - Rotation3D::around_y(Radians::new(FRAC_PI_2)), - Rotation3D::around_y(Radians::new(-FRAC_PI_2)), - Rotation3D::around_y(Radians::new(PI)), - Rotation3D::around_z(Radians::new(FRAC_PI_2)), - Rotation3D::around_z(Radians::new(-FRAC_PI_2)), - Rotation3D::around_z(Radians::new(PI)), + Rotation3D::around_x(Angle::radians(FRAC_PI_2)), + Rotation3D::around_x(Angle::radians(-FRAC_PI_2)), + Rotation3D::around_x(Angle::radians(PI)), + Rotation3D::around_y(Angle::radians(FRAC_PI_2)), + Rotation3D::around_y(Angle::radians(-FRAC_PI_2)), + Rotation3D::around_y(Angle::radians(PI)), + Rotation3D::around_z(Angle::radians(FRAC_PI_2)), + Rotation3D::around_z(Angle::radians(-FRAC_PI_2)), + Rotation3D::around_z(Angle::radians(PI)), ]; let points = [ @@ -638,13 +733,13 @@ fn around_axis() { use std::f32::consts::{PI, FRAC_PI_2}; // Two sort of trivial cases: - let r1 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Radians::new(PI)); - let r2 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Radians::new(FRAC_PI_2)); + let r1 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Angle::radians(PI)); + let r2 = Rotation3D::around_axis(vec3(1.0, 1.0, 0.0), Angle::radians(FRAC_PI_2)); assert!(r1.rotate_point3d(&point3(1.0, 2.0, 0.0)).approx_eq(&point3(2.0, 1.0, 0.0))); assert!(r2.rotate_point3d(&point3(1.0, 0.0, 0.0)).approx_eq(&point3(0.5, 0.5, -0.5.sqrt()))); // A more arbitray test (made up with numpy): - let r3 = Rotation3D::around_axis(vec3(0.5, 1.0, 2.0), Radians::new(2.291288)); + let r3 = Rotation3D::around_axis(vec3(0.5, 1.0, 2.0), Angle::radians(2.291288)); assert!(r3.rotate_point3d(&point3(1.0, 0.0, 0.0)).approx_eq(&point3(-0.58071821, 0.81401868, -0.01182979))); } @@ -658,8 +753,8 @@ fn from_euler() { // of transforming a point rather than the values of each qauetrnions. let p = point3(1.0, 2.0, 3.0); - let angle = Radians::new(FRAC_PI_2); - let zero = Radians::new(0.0); + let angle = Angle::radians(FRAC_PI_2); + let zero = Angle::radians(0.0); // roll let roll_re = Rotation3D::euler(angle, zero, zero); diff --git a/src/transform2d.rs b/src/transform2d.rs index 2685e223..5fbfccc8 100644 --- a/src/transform2d.rs +++ b/src/transform2d.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::{UnknownUnit, Radians}; +use super::{UnknownUnit, Angle}; use num::{One, Zero}; use point::TypedPoint2D; use vector::{TypedVector2D, vec2}; @@ -249,7 +249,7 @@ where T: Copy + Clone + } /// Returns a rotation transform. - pub fn create_rotation(theta: Radians) -> Self { + pub fn create_rotation(theta: Angle) -> Self { let _0 = Zero::zero(); let cos = theta.get().cos(); let sin = theta.get().sin(); @@ -262,13 +262,13 @@ where T: Copy + Clone + /// Applies a rotation after self's transformation and returns the resulting transform. #[cfg_attr(feature = "unstable", must_use)] - pub fn post_rotate(&self, theta: Radians) -> Self { + pub fn post_rotate(&self, theta: Angle) -> Self { self.post_mul(&TypedTransform2D::create_rotation(theta)) } /// Applies a rotation after self's transformation and returns the resulting transform. #[cfg_attr(feature = "unstable", must_use)] - pub fn pre_rotate(&self, theta: Radians) -> Self { + pub fn pre_rotate(&self, theta: Angle) -> Self { self.pre_mul(&TypedTransform2D::create_rotation(theta)) } @@ -394,13 +394,13 @@ mod test { use super::*; use approxeq::ApproxEq; use point::Point2D; - use Radians; + use Angle; use std::f32::consts::FRAC_PI_2; type Mat = Transform2D; - fn rad(v: f32) -> Radians { Radians::new(v) } + fn rad(v: f32) -> Angle { Angle::radians(v) } #[test] pub fn test_translation() { diff --git a/src/transform3d.rs b/src/transform3d.rs index f29e8d6b..ebac4af9 100644 --- a/src/transform3d.rs +++ b/src/transform3d.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::{UnknownUnit, Radians}; +use super::{UnknownUnit, Angle}; use approxeq::ApproxEq; use trig::Trig; use point::{TypedPoint2D, TypedPoint3D, point2, point3}; @@ -511,7 +511,7 @@ where T: Copy + Clone + /// Create a 3d rotation transform from an angle / axis. /// The supplied axis must be normalized. - pub fn create_rotation(x: T, y: T, z: T, theta: Radians) -> Self { + pub fn create_rotation(x: T, y: T, z: T, theta: Angle) -> Self { let (_0, _1): (T, T) = (Zero::zero(), One::one()); let _2 = _1 + _1; @@ -548,20 +548,20 @@ where T: Copy + Clone + /// Returns a transform with a rotation applied after self's transformation. #[cfg_attr(feature = "unstable", must_use)] - pub fn post_rotate(&self, x: T, y: T, z: T, theta: Radians) -> Self { + pub fn post_rotate(&self, x: T, y: T, z: T, theta: Angle) -> Self { self.post_mul(&TypedTransform3D::create_rotation(x, y, z, theta)) } /// Returns a transform with a rotation applied before self's transformation. #[cfg_attr(feature = "unstable", must_use)] - pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Radians) -> Self { + pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Angle) -> Self { self.pre_mul(&TypedTransform3D::create_rotation(x, y, z, theta)) } /// Create a 2d skew transform. /// /// See https://drafts.csswg.org/css-transforms/#funcdef-skew - pub fn create_skew(alpha: Radians, beta: Radians) -> Self { + pub fn create_skew(alpha: Angle, beta: Angle) -> Self { let (_0, _1): (T, T) = (Zero::zero(), One::one()); let (sx, sy) = (beta.get().tan(), alpha.get().tan()); TypedTransform3D::row_major( @@ -696,7 +696,7 @@ mod tests { use approxeq::ApproxEq; use transform2d::Transform2D; use point::{Point2D, Point3D}; - use Radians; + use Angle; use super::*; use std::f32::consts::{FRAC_PI_2, PI}; @@ -704,7 +704,7 @@ mod tests { type Mf32 = Transform3D; // For convenience. - fn rad(v: f32) -> Radians { Radians::new(v) } + fn rad(v: f32) -> Angle { Angle::radians(v) } #[test] pub fn test_translation() { diff --git a/src/trig.rs b/src/trig.rs index 5434fe93..3e342c19 100644 --- a/src/trig.rs +++ b/src/trig.rs @@ -14,6 +14,8 @@ pub trait Trig { fn cos(self) -> Self; fn tan(self) -> Self; fn fast_atan2(y: Self, x: Self) -> Self; + fn degrees_to_radians(deg: Self) -> Self; + fn radians_to_degrees(rad: Self) -> Self; } macro_rules! trig { @@ -50,6 +52,16 @@ macro_rules! trig { result } + + #[inline] + fn degrees_to_radians(deg: Self) -> Self { + deg.to_radians() + } + + #[inline] + fn radians_to_degrees(rad: Self) -> Self { + rad.to_degrees() + } } ) } diff --git a/src/vector.rs b/src/vector.rs index 667f9dc2..ed9ac56d 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -14,7 +14,7 @@ use point::{TypedPoint2D, TypedPoint3D, point2, point3}; use size::{TypedSize2D, size2}; use scale::TypedScale; use trig::Trig; -use Radians; +use Angle; use num::*; use num_traits::{Float, NumCast, Signed}; use std::fmt; @@ -131,8 +131,8 @@ impl TypedVector2D { impl TypedVector2D where T: Trig + Copy + Sub { /// Returns the angle between this vector and the x axis between -PI and PI. - pub fn angle_from_x_axis(&self) -> Radians { - Radians::new(Trig::fast_atan2(self.y, self.x)) + pub fn angle_from_x_axis(&self) -> Angle { + Angle::radians(Trig::fast_atan2(self.y, self.x)) } }