Use angle types in appropriate locations

This commit is contained in:
Brendan Zabarauskas 2013-09-04 15:29:53 +10:00
parent 9c65b38231
commit 6117fef58b
5 changed files with 70 additions and 49 deletions

View file

@ -13,10 +13,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // 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; use std::num::Zero;
#[deriving(Clone, Eq, Ord, Zero)] struct Rad<S> { s: S } #[deriving(Clone, Eq, Ord, Zero)] pub struct Rad<S> { s: S }
#[deriving(Clone, Eq, Ord, Zero)] struct Deg<S> { s: S } #[deriving(Clone, Eq, Ord, Zero)] pub struct Deg<S> { s: S }
#[inline] pub fn rad<S: Clone + Float>(s: S) -> Rad<S> { Rad { s: s } } #[inline] pub fn rad<S: Clone + Float>(s: S) -> Rad<S> { Rad { s: s } }
#[inline] pub fn deg<S: Clone + Float>(s: S) -> Deg<S> { Deg { s: s } } #[inline] pub fn deg<S: Clone + Float>(s: S) -> Deg<S> { Deg { s: s } }
@ -33,6 +38,25 @@ impl<S: Clone + Float> ToDeg<S> for Deg<S> { #[inline] fn to_deg(&self) -> Deg<S
impl<S: Clone + Float> Neg<Rad<S>> for Rad<S> { #[inline] fn neg(&self) -> Rad<S> { rad(-self.s) } } impl<S: Clone + Float> Neg<Rad<S>> for Rad<S> { #[inline] fn neg(&self) -> Rad<S> { rad(-self.s) } }
impl<S: Clone + Float> Neg<Deg<S>> for Deg<S> { #[inline] fn neg(&self) -> Deg<S> { deg(-self.s) } } impl<S: Clone + Float> Neg<Deg<S>> for Deg<S> { #[inline] fn neg(&self) -> Deg<S> { deg(-self.s) } }
/// Private utility functions for converting to/from scalars
trait ScalarConv<S> {
fn from(s: S) -> Self;
fn s<'a>(&'a self) -> &'a S;
fn mut_s<'a>(&'a mut self) -> &'a mut S;
}
impl<S: Clone + Float> ScalarConv<S> for Rad<S> {
#[inline] fn from(s: S) -> Rad<S> { 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<S: Clone + Float> ScalarConv<S> for Deg<S> {
#[inline] fn from(s: S) -> Deg<S> { 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 pub trait Angle
< <
S: Clone + Float S: Clone + Float
@ -43,21 +67,19 @@ pub trait Angle
+ Neg<Self> + Neg<Self>
+ ToRad<S> + ToRad<S>
+ ToDeg<S> + ToDeg<S>
+ ScalarConv<S>
{ {
fn from_s(s: S) -> Self;
fn from<A: Angle<S>>(theta: A) -> Self; fn from<A: Angle<S>>(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 neg_self(&mut self) { *self = -*self }
#[inline] fn add_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 { Angle::from_s(*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 div_a(&self, other: Self) -> S { *self.s() / *other.s() }
#[inline] fn rem_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 mul_s(&self, s: S) -> Self { ScalarConv::from(*self.s() * s) }
#[inline] fn div_s(&self, s: S) -> Self { Angle::from_s(*self.s() / s) } #[inline] fn div_s(&self, s: S) -> Self { ScalarConv::from(*self.s() / s) }
#[inline] fn rem_s(&self, s: S) -> Self { Angle::from_s(*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 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() } #[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 sin(&self) -> S { self.s().sin() }
#[inline] fn cos(&self) -> S { self.s().cos() } #[inline] fn cos(&self) -> S { self.s().cos() }
#[inline] fn tan(&self) -> S { self.s().tan() } #[inline] fn tan(&self) -> S { self.s().tan() }
#[inline] fn sin_cos(&self) -> (S, S) { self.s().sin_cos() }
} }
#[inline] fn sin<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.sin() } #[inline] pub fn sin<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.sin() }
#[inline] fn cos<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.cos() } #[inline] pub fn cos<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.cos() }
#[inline] fn tan<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.tan() } #[inline] pub fn tan<S: Clone + Float, A: Angle<S>>(theta: A) -> S { theta.tan() }
#[inline] pub fn sin_cos<S: Clone + Float, A: Angle<S>>(theta: A) -> (S, S) { theta.sin_cos() }
impl<S: Clone + Float> Angle<S> for Rad<S> { impl<S: Clone + Float> Angle<S> for Rad<S> {
#[inline] fn from_s(s: S) -> Rad<S> { rad(s) }
#[inline] fn from<A: Angle<S>>(theta: A) -> Rad<S> { theta.to_rad() } #[inline] fn from<A: Angle<S>>(theta: A) -> Rad<S> { 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<S: Clone + Float> Angle<S> for Deg<S> { impl<S: Clone + Float> Angle<S> for Deg<S> {
#[inline] fn from_s(s: S) -> Deg<S> { deg(s) }
#[inline] fn from<A: Angle<S>>(theta: A) -> Deg<S> { theta.to_deg() } #[inline] fn from<A: Angle<S>>(theta: A) -> Deg<S> { 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 { 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_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.asin())) } #[inline] fn asin_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.asin())) }
#[inline] fn acos_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.acos())) } #[inline] fn acos_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.acos())) }
#[inline] fn atan_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.atan())) } #[inline] fn atan_<A: Angle<Self>>(&self) -> A { Angle::from(rad(self.atan())) }
#[inline] fn atan2_<A: Angle<Self>>(&self, other: &Self) -> A { Angle::from(rad(self.atan2(other))) } #[inline] fn atan2_<A: Angle<Self>>(&self, other: &Self) -> A { Angle::from(rad(self.atan2(other))) }
} }
#[inline] fn asin<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.asin_() } #[inline] pub fn asin<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.asin_() }
#[inline] fn acos<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.acos_() } #[inline] pub fn acos<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.acos_() }
#[inline] fn atan<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.atan_() } #[inline] pub fn atan<S: Clone + ScalarTrig, A: Angle<S>>(s: S) -> A { s.atan_() }
#[inline] fn atan2<S: Clone + ScalarTrig, A: Angle<S>>(a: S, b: S) -> A { a.atan2_(&b) } #[inline] pub fn atan2<S: Clone + ScalarTrig, A: Angle<S>>(a: S, b: S) -> A { a.atan2_(&b) }
impl ScalarTrig for f32; impl<S: Clone + Float> ScalarTrig for S;
impl ScalarTrig for f64;
impl ScalarTrig for float;
impl<S: Clone + Float> ToStr for Rad<S> { fn to_str(&self) -> ~str { fmt!("%? rad", self.s) } } impl<S: Clone + Float> ToStr for Rad<S> { fn to_str(&self) -> ~str { fmt!("%? rad", self.s) } }
impl<S: Clone + Float> ToStr for Deg<S> { fn to_str(&self) -> ~str { fmt!("%?°", self.s) } } impl<S: Clone + Float> ToStr for Deg<S> { fn to_str(&self) -> ~str { fmt!("%?°", self.s) } }

View file

@ -15,9 +15,10 @@
//! Column major, square matrix types and traits. //! 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 quaternion::{Quat, ToQuat};
use vector::*; use vector::*;
use util::half; use util::half;
@ -75,9 +76,9 @@ impl<S: Clone + Num> Mat2<S> {
impl<S: Clone + Float> Mat2<S> { impl<S: Clone + Float> Mat2<S> {
#[inline] #[inline]
pub fn from_angle(radians: S) -> Mat2<S> { pub fn from_angle(theta: Rad<S>) -> Mat2<S> {
let cos_theta = cos(radians.clone()); let cos_theta = cos(theta.clone());
let sin_theta = sin(radians.clone()); let sin_theta = sin(theta.clone());
Mat2::new(cos_theta.clone(), -sin_theta.clone(), Mat2::new(cos_theta.clone(), -sin_theta.clone(),
sin_theta.clone(), cos_theta.clone()) sin_theta.clone(), cos_theta.clone())

View file

@ -15,6 +15,7 @@
use std::num::{zero, one}; use std::num::{zero, one};
use angle::{Angle, Rad, rad, tan};
use matrix::Mat4; use matrix::Mat4;
use util::two; use util::two;
@ -24,8 +25,8 @@ use util::two;
/// ///
/// This is the equivalent of the gluPerspective function, the algorithm of which /// This is the equivalent of the gluPerspective function, the algorithm of which
/// can be found [here](http://www.opengl.org/wiki/GluPerspective_code). /// can be found [here](http://www.opengl.org/wiki/GluPerspective_code).
pub fn perspective<S: Clone + Float>(fovy: S, aspectRatio: S, near: S, far: S) -> Mat4<S> { pub fn perspective<S: Clone + Float>(fovy: Rad<S>, aspectRatio: S, near: S, far: S) -> Mat4<S> {
let ymax = near * (fovy / two::<S>()).to_radians().tan(); let ymax = near * tan(fovy.div_s(two::<S>()));
let xmax = ymax * aspectRatio; let xmax = ymax * aspectRatio;
frustum(-xmax, xmax, -ymax, ymax, near, far) frustum(-xmax, xmax, -ymax, ymax, near, far)
@ -101,7 +102,7 @@ pub trait Projection<S> {
/// A perspective projection based on a vertical field-of-view angle. /// A perspective projection based on a vertical field-of-view angle.
#[deriving(Clone, Eq)] #[deriving(Clone, Eq)]
pub struct PerspectiveFov<S> { pub struct PerspectiveFov<S> {
fovy: S, //radians fovy: Rad<S>,
aspect: S, aspect: S,
near: S, near: S,
far: S, far: S,
@ -110,8 +111,8 @@ pub struct PerspectiveFov<S> {
impl<S: Clone + Float> PerspectiveFov<S> { impl<S: Clone + Float> PerspectiveFov<S> {
pub fn to_perspective(&self) -> Result<Perspective<S>, ~str> { pub fn to_perspective(&self) -> Result<Perspective<S>, ~str> {
do self.if_valid { do self.if_valid {
let angle = self.fovy / two::<S>(); let angle = self.fovy.div_s(two::<S>());
let ymax = self.near * angle.tan(); let ymax = self.near * tan(angle);
let xmax = ymax * self.aspect; let xmax = ymax * self.aspect;
Perspective { Perspective {
@ -128,10 +129,10 @@ impl<S: Clone + Float> PerspectiveFov<S> {
impl<S: Clone + Float> Projection<S> for PerspectiveFov<S> { impl<S: Clone + Float> Projection<S> for PerspectiveFov<S> {
fn if_valid<U:Clone>(&self, f: &fn() -> U) -> Result<U, ~str> { fn if_valid<U:Clone>(&self, f: &fn() -> U) -> Result<U, ~str> {
let frac_pi_2: S = Real::frac_pi_2(); let half_turn: Rad<S> = rad(Real::frac_pi_2());
cond! ( cond! (
(self.fovy < zero()) { Err(fmt!("The vertical field of view cannot be below zero, found: %?", self.fovy)) } (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.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.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)) } (self.far < zero()) { Err(fmt!("The far plane distance cannot be below zero, found: %?", self.far)) }

View file

@ -15,9 +15,10 @@
use std::num::{zero, one, sqrt}; use std::num::{zero, one, sqrt};
use util::two; use angle::{Angle, Rad, acos, cos, sin};
use matrix::{Mat3, ToMat3}; use matrix::{Mat3, ToMat3};
use vector::{Vec3, Vector, EuclideanVector}; use vector::{Vec3, Vector, EuclideanVector};
use util::two;
/// A quaternion in scalar/vector form /// A quaternion in scalar/vector form
#[deriving(Clone, Eq)] #[deriving(Clone, Eq)]
@ -190,14 +191,14 @@ impl<S: Clone + Float> Quat<S> {
// stay within the domain of acos() // stay within the domain of acos()
let robust_dot = dot.clamp(&-one::<S>(), &one::<S>()); let robust_dot = dot.clamp(&-one::<S>(), &one::<S>());
let theta_0 = robust_dot.acos(); // the angle between the quaternions let theta: Rad<S> = acos(robust_dot.clone()); // the angle between the quaternions
let theta = theta_0 * amount; // the fraction of theta specified by `amount` let theta: Rad<S> = theta.mul_s(amount); // the fraction of theta specified by `amount`
let q = other.sub_q(&self.mul_s(robust_dot)) let q = other.sub_q(&self.mul_s(robust_dot))
.normalize(); .normalize();
self.mul_s(theta.cos()) self.mul_s(cos(theta.clone()))
.add_q(&q.mul_s(theta.sin())) .add_q(&q.mul_s(sin(theta)))
} }
} }
} }

View file

@ -13,10 +13,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::num::{Zero, zero, One, one}; use std::num::{Zero, zero, One, one, sqrt};
use std::num::{sqrt, atan2};
use array::*; use angle::{Rad, atan2};
use array::Array;
/// A 2-dimensional vector. /// A 2-dimensional vector.
#[deriving(Eq, Clone, Zero)] #[deriving(Eq, Clone, Zero)]
@ -193,7 +193,7 @@ pub trait EuclideanVector
} }
/// The angle between the vector and `other`. /// The angle between the vector and `other`.
fn angle(&self, other: &Self) -> S; fn angle(&self, other: &Self) -> Rad<S>;
/// Returns a vector with the same direction, but with a `length` (or /// Returns a vector with the same direction, but with a `length` (or
/// `norm`) of `1`. /// `norm`) of `1`.
@ -239,14 +239,14 @@ pub trait EuclideanVector
impl<S: Clone + Float> EuclideanVector<S, [S, ..2]> for Vec2<S> { impl<S: Clone + Float> EuclideanVector<S, [S, ..2]> for Vec2<S> {
#[inline] #[inline]
fn angle(&self, other: &Vec2<S>) -> S { fn angle(&self, other: &Vec2<S>) -> Rad<S> {
atan2(self.perp_dot(other), self.dot(other)) atan2(self.perp_dot(other), self.dot(other))
} }
} }
impl<S: Clone + Float> EuclideanVector<S, [S, ..3]> for Vec3<S> { impl<S: Clone + Float> EuclideanVector<S, [S, ..3]> for Vec3<S> {
#[inline] #[inline]
fn angle(&self, other: &Vec3<S>) -> S { fn angle(&self, other: &Vec3<S>) -> Rad<S> {
atan2(self.cross(other).length(), self.dot(other)) atan2(self.cross(other).length(), self.dot(other))
} }
} }