From 6117fef58b30b7209ac1a2ac2d0d97604cc291de Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 4 Sep 2013 15:29:53 +1000 Subject: [PATCH] Use angle types in appropriate locations --- src/cgmath/angle.rs | 70 +++++++++++++++++++++++++--------------- src/cgmath/matrix.rs | 11 ++++--- src/cgmath/projection.rs | 15 +++++---- src/cgmath/quaternion.rs | 11 ++++--- src/cgmath/vector.rs | 12 +++---- 5 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/cgmath/angle.rs b/src/cgmath/angle.rs index be3fce5..1667442 100644 --- a/src/cgmath/angle.rs +++ b/src/cgmath/angle.rs @@ -13,10 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Angle units for type-safe, self-documenting code. + +pub use std::num::{sinh, cosh, tanh}; +pub use std::num::{asinh, acosh, atanh}; + use std::num::Zero; -#[deriving(Clone, Eq, Ord, Zero)] struct Rad { s: S } -#[deriving(Clone, Eq, Ord, Zero)] struct Deg { s: S } +#[deriving(Clone, Eq, Ord, Zero)] pub struct Rad { s: S } +#[deriving(Clone, Eq, Ord, Zero)] pub struct Deg { s: S } #[inline] pub fn rad(s: S) -> Rad { Rad { s: s } } #[inline] pub fn deg(s: S) -> Deg { Deg { s: s } } @@ -33,6 +38,25 @@ impl ToDeg for Deg { #[inline] fn to_deg(&self) -> Deg Neg> for Rad { #[inline] fn neg(&self) -> Rad { rad(-self.s) } } impl Neg> for Deg { #[inline] fn neg(&self) -> Deg { deg(-self.s) } } +/// Private utility functions for converting to/from scalars +trait ScalarConv { + fn from(s: S) -> Self; + fn s<'a>(&'a self) -> &'a S; + fn mut_s<'a>(&'a mut self) -> &'a mut S; +} + +impl ScalarConv for Rad { + #[inline] fn from(s: S) -> Rad { rad(s) } + #[inline] fn s<'a>(&'a self) -> &'a S { &'a self.s } + #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &'a mut self.s } +} + +impl ScalarConv for Deg { + #[inline] fn from(s: S) -> Deg { deg(s) } + #[inline] fn s<'a>(&'a self) -> &'a S { &'a self.s } + #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &'a mut self.s } +} + pub trait Angle < S: Clone + Float @@ -43,21 +67,19 @@ pub trait Angle + Neg + ToRad + ToDeg ++ ScalarConv { - fn from_s(s: S) -> Self; fn from>(theta: A) -> Self; - fn s<'a>(&'a self) -> &'a S; - fn mut_s<'a>(&'a mut self) -> &'a mut S; #[inline] fn neg_self(&mut self) { *self = -*self } - #[inline] fn add_a(&self, other: Self) -> Self { Angle::from_s(*self.s() + *other.s()) } - #[inline] fn sub_a(&self, other: Self) -> Self { Angle::from_s(*self.s() - *other.s()) } + #[inline] fn add_a(&self, other: Self) -> Self { ScalarConv::from(*self.s() + *other.s()) } + #[inline] fn sub_a(&self, other: Self) -> Self { ScalarConv::from(*self.s() - *other.s()) } #[inline] fn div_a(&self, other: Self) -> S { *self.s() / *other.s() } #[inline] fn rem_a(&self, other: Self) -> S { *self.s() % *other.s() } - #[inline] fn mul_s(&self, s: S) -> Self { Angle::from_s(*self.s() * s) } - #[inline] fn div_s(&self, s: S) -> Self { Angle::from_s(*self.s() / s) } - #[inline] fn rem_s(&self, s: S) -> Self { Angle::from_s(*self.s() % s) } + #[inline] fn mul_s(&self, s: S) -> Self { ScalarConv::from(*self.s() * s) } + #[inline] fn div_s(&self, s: S) -> Self { ScalarConv::from(*self.s() / s) } + #[inline] fn rem_s(&self, s: S) -> Self { ScalarConv::from(*self.s() % s) } #[inline] fn add_self_a(&mut self, other: Self) { *self.mut_s() = *self.s() + *other.s() } #[inline] fn sub_self_a(&mut self, other: Self) { *self.mut_s() = *self.s() - *other.s() } @@ -68,41 +90,37 @@ pub trait Angle #[inline] fn sin(&self) -> S { self.s().sin() } #[inline] fn cos(&self) -> S { self.s().cos() } #[inline] fn tan(&self) -> S { self.s().tan() } + #[inline] fn sin_cos(&self) -> (S, S) { self.s().sin_cos() } } -#[inline] fn sin>(theta: A) -> S { theta.sin() } -#[inline] fn cos>(theta: A) -> S { theta.cos() } -#[inline] fn tan>(theta: A) -> S { theta.tan() } +#[inline] pub fn sin>(theta: A) -> S { theta.sin() } +#[inline] pub fn cos>(theta: A) -> S { theta.cos() } +#[inline] pub fn tan>(theta: A) -> S { theta.tan() } +#[inline] pub fn sin_cos>(theta: A) -> (S, S) { theta.sin_cos() } impl Angle for Rad { - #[inline] fn from_s(s: S) -> Rad { rad(s) } #[inline] fn from>(theta: A) -> Rad { theta.to_rad() } - #[inline] fn s<'a>(&'a self) -> &'a S { &'a self.s } - #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &'a mut self.s } } impl Angle for Deg { - #[inline] fn from_s(s: S) -> Deg { deg(s) } #[inline] fn from>(theta: A) -> Deg { theta.to_deg() } - #[inline] fn s<'a>(&'a self) -> &'a S { &'a self.s } - #[inline] fn mut_s<'a>(&'a mut self) -> &'a mut S { &'a mut self.s } } pub trait ScalarTrig: Clone + Float { + // These need underscores so that they don't conflict with the methods + // defined in the `std::num::Trigonometric` trait. #[inline] fn asin_>(&self) -> A { Angle::from(rad(self.asin())) } #[inline] fn acos_>(&self) -> A { Angle::from(rad(self.acos())) } #[inline] fn atan_>(&self) -> A { Angle::from(rad(self.atan())) } #[inline] fn atan2_>(&self, other: &Self) -> A { Angle::from(rad(self.atan2(other))) } } -#[inline] fn asin>(s: S) -> A { s.asin_() } -#[inline] fn acos>(s: S) -> A { s.acos_() } -#[inline] fn atan>(s: S) -> A { s.atan_() } -#[inline] fn atan2>(a: S, b: S) -> A { a.atan2_(&b) } +#[inline] pub fn asin>(s: S) -> A { s.asin_() } +#[inline] pub fn acos>(s: S) -> A { s.acos_() } +#[inline] pub fn atan>(s: S) -> A { s.atan_() } +#[inline] pub fn atan2>(a: S, b: S) -> A { a.atan2_(&b) } -impl ScalarTrig for f32; -impl ScalarTrig for f64; -impl ScalarTrig for float; +impl ScalarTrig for S; impl ToStr for Rad { fn to_str(&self) -> ~str { fmt!("%? rad", self.s) } } impl ToStr for Deg { fn to_str(&self) -> ~str { fmt!("%?°", self.s) } } diff --git a/src/cgmath/matrix.rs b/src/cgmath/matrix.rs index e6715fa..7ac8223 100644 --- a/src/cgmath/matrix.rs +++ b/src/cgmath/matrix.rs @@ -15,9 +15,10 @@ //! Column major, square matrix types and traits. -use std::num::{Zero, zero, One, one, sin, cos}; +use std::num::{Zero, zero, One, one}; -use array::*; +use angle::{Rad, sin, cos}; +use array::Array; use quaternion::{Quat, ToQuat}; use vector::*; use util::half; @@ -75,9 +76,9 @@ impl Mat2 { impl Mat2 { #[inline] - pub fn from_angle(radians: S) -> Mat2 { - let cos_theta = cos(radians.clone()); - let sin_theta = sin(radians.clone()); + pub fn from_angle(theta: Rad) -> Mat2 { + let cos_theta = cos(theta.clone()); + let sin_theta = sin(theta.clone()); Mat2::new(cos_theta.clone(), -sin_theta.clone(), sin_theta.clone(), cos_theta.clone()) diff --git a/src/cgmath/projection.rs b/src/cgmath/projection.rs index 366d2ae..621330d 100644 --- a/src/cgmath/projection.rs +++ b/src/cgmath/projection.rs @@ -15,6 +15,7 @@ use std::num::{zero, one}; +use angle::{Angle, Rad, rad, tan}; use matrix::Mat4; use util::two; @@ -24,8 +25,8 @@ use util::two; /// /// This is the equivalent of the gluPerspective function, the algorithm of which /// can be found [here](http://www.opengl.org/wiki/GluPerspective_code). -pub fn perspective(fovy: S, aspectRatio: S, near: S, far: S) -> Mat4 { - let ymax = near * (fovy / two::()).to_radians().tan(); +pub fn perspective(fovy: Rad, aspectRatio: S, near: S, far: S) -> Mat4 { + let ymax = near * tan(fovy.div_s(two::())); let xmax = ymax * aspectRatio; frustum(-xmax, xmax, -ymax, ymax, near, far) @@ -101,7 +102,7 @@ pub trait Projection { /// A perspective projection based on a vertical field-of-view angle. #[deriving(Clone, Eq)] pub struct PerspectiveFov { - fovy: S, //radians + fovy: Rad, aspect: S, near: S, far: S, @@ -110,8 +111,8 @@ pub struct PerspectiveFov { impl PerspectiveFov { pub fn to_perspective(&self) -> Result, ~str> { do self.if_valid { - let angle = self.fovy / two::(); - let ymax = self.near * angle.tan(); + let angle = self.fovy.div_s(two::()); + let ymax = self.near * tan(angle); let xmax = ymax * self.aspect; Perspective { @@ -128,10 +129,10 @@ impl PerspectiveFov { impl Projection for PerspectiveFov { fn if_valid(&self, f: &fn() -> U) -> Result { - let frac_pi_2: S = Real::frac_pi_2(); + let half_turn: Rad = rad(Real::frac_pi_2()); cond! ( (self.fovy < zero()) { Err(fmt!("The vertical field of view cannot be below zero, found: %?", self.fovy)) } - (self.fovy > frac_pi_2) { Err(fmt!("The vertical field of view cannot be greater than a half turn, found: %?", self.fovy)) } + (self.fovy > half_turn) { Err(fmt!("The vertical field of view cannot be greater than a half turn, found: %?", self.fovy)) } (self.aspect < zero()) { Err(fmt!("The aspect ratio cannot be below zero, found: %?", self.aspect)) } (self.near < zero()) { Err(fmt!("The near plane distance cannot be below zero, found: %?", self.near)) } (self.far < zero()) { Err(fmt!("The far plane distance cannot be below zero, found: %?", self.far)) } diff --git a/src/cgmath/quaternion.rs b/src/cgmath/quaternion.rs index e48f7fa..7495755 100644 --- a/src/cgmath/quaternion.rs +++ b/src/cgmath/quaternion.rs @@ -15,9 +15,10 @@ use std::num::{zero, one, sqrt}; -use util::two; +use angle::{Angle, Rad, acos, cos, sin}; use matrix::{Mat3, ToMat3}; use vector::{Vec3, Vector, EuclideanVector}; +use util::two; /// A quaternion in scalar/vector form #[deriving(Clone, Eq)] @@ -190,14 +191,14 @@ impl Quat { // stay within the domain of acos() let robust_dot = dot.clamp(&-one::(), &one::()); - let theta_0 = robust_dot.acos(); // the angle between the quaternions - let theta = theta_0 * amount; // the fraction of theta specified by `amount` + let theta: Rad = acos(robust_dot.clone()); // the angle between the quaternions + let theta: Rad = theta.mul_s(amount); // the fraction of theta specified by `amount` let q = other.sub_q(&self.mul_s(robust_dot)) .normalize(); - self.mul_s(theta.cos()) - .add_q(&q.mul_s(theta.sin())) + self.mul_s(cos(theta.clone())) + .add_q(&q.mul_s(sin(theta))) } } } diff --git a/src/cgmath/vector.rs b/src/cgmath/vector.rs index 0d4e13f..e09a235 100644 --- a/src/cgmath/vector.rs +++ b/src/cgmath/vector.rs @@ -13,10 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::num::{Zero, zero, One, one}; -use std::num::{sqrt, atan2}; +use std::num::{Zero, zero, One, one, sqrt}; -use array::*; +use angle::{Rad, atan2}; +use array::Array; /// A 2-dimensional vector. #[deriving(Eq, Clone, Zero)] @@ -193,7 +193,7 @@ pub trait EuclideanVector } /// The angle between the vector and `other`. - fn angle(&self, other: &Self) -> S; + fn angle(&self, other: &Self) -> Rad; /// Returns a vector with the same direction, but with a `length` (or /// `norm`) of `1`. @@ -239,14 +239,14 @@ pub trait EuclideanVector impl EuclideanVector for Vec2 { #[inline] - fn angle(&self, other: &Vec2) -> S { + fn angle(&self, other: &Vec2) -> Rad { atan2(self.perp_dot(other), self.dot(other)) } } impl EuclideanVector for Vec3 { #[inline] - fn angle(&self, other: &Vec3) -> S { + fn angle(&self, other: &Vec3) -> Rad { atan2(self.cross(other).length(), self.dot(other)) } }