diff --git a/src/aabb.rs b/src/aabb.rs index 5fd5000..122e6bd 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -24,9 +24,9 @@ use point::{Point, Point2, Point3}; use vector::{Vector, Vector2, Vector3}; use ray::{Ray2}; use intersect::Intersect; -use num::{BaseNum, BaseFloat}; +use num::{zero, one, BaseNum, BaseFloat}; use std::fmt; -use std::num::{zero, one, Float}; +use std::num::Float; pub trait Aabb, P: Point> { /// Create a new AABB using two points as opposing corners. diff --git a/src/angle.rs b/src/angle.rs index 169fd8a..5105f8a 100644 --- a/src/angle.rs +++ b/src/angle.rs @@ -16,10 +16,10 @@ //! Angle units for type-safe, self-documenting code. use std::fmt; -use std::num::{One, one, Zero, zero, cast, Float}; +use std::num::{cast, Float}; use approx::ApproxEq; -use num::BaseFloat; +use num::{BaseFloat, One, one, Zero, zero}; /// An angle, in radians #[deriving(Clone, PartialEq, PartialOrd, Hash, Encodable, Decodable, Rand)] diff --git a/src/cgmath.rs b/src/cgmath.rs index 23b4ae4..e006098 100644 --- a/src/cgmath.rs +++ b/src/cgmath.rs @@ -77,7 +77,7 @@ pub use obb::{Obb2, Obb3}; pub use sphere::Sphere; pub use approx::ApproxEq; -pub use num::{PartialOrd, BaseNum, BaseInt, BaseFloat}; +pub use num::{BaseNum, BaseInt, BaseFloat, One, one, Zero, zero}; // Modules diff --git a/src/line.rs b/src/line.rs index d686078..84843ba 100644 --- a/src/line.rs +++ b/src/line.rs @@ -15,9 +15,7 @@ //! Line segments -use std::num::{Zero, zero, One, one}; - -use num::{BaseNum, BaseFloat}; +use num::{BaseNum, BaseFloat, Zero, zero, One, one}; use point::{Point, Point2, Point3}; use vector::{Vector, Vector2}; use ray::{Ray2}; diff --git a/src/matrix.rs b/src/matrix.rs index b1140ee..6d43707 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -17,12 +17,12 @@ use std::fmt; use std::mem; -use std::num::{Zero, zero, One, one, cast}; +use std::num::cast; use angle::{Rad, sin, cos, sin_cos}; use approx::ApproxEq; use array::{Array1, Array2, FixedArray}; -use num::{BaseFloat, BaseNum}; +use num::{BaseFloat, BaseNum, Zero, zero, One, one}; use point::{Point, Point3}; use quaternion::{Quaternion, ToQuaternion}; use vector::{Vector, EuclideanVector}; diff --git a/src/num.rs b/src/num.rs index c10cf67..19225bd 100644 --- a/src/num.rs +++ b/src/num.rs @@ -17,7 +17,7 @@ use approx::ApproxEq; use std::cmp; use std::fmt; -use std::num::{FloatMath, Int, Primitive}; +use std::num::{FloatMath, Int, NumCast, Float}; /// A trait providing a [partial ordering](http://mathworld.wolfram.com/PartialOrder.html). pub trait PartialOrd { @@ -57,21 +57,90 @@ macro_rules! partial_ord_float ( partial_ord_float!(f32) partial_ord_float!(f64) -/// Base numeric types with partial ordering -pub trait BaseNum: Primitive + PartialOrd + fmt::Show {} +/// Additive neutral element +pub trait Zero { + fn zero() -> Self; + fn is_zero(&self) -> bool; +} + +/// Multiplicative neutral element +pub trait One { + fn one() -> Self; +} + +/// Base numeric types with partial ordering +pub trait BaseNum: + Copy + NumCast + Clone + Add + Sub + + Mul + Div + Rem + Neg + PartialEq + + PartialOrd + cmp::PartialOrd + fmt::Show + Zero + One +{} + + +macro_rules! impl_basenum_int ( + ($T: ident) => ( + impl BaseNum for $T {} + impl Zero for $T { + fn zero() -> $T { + Int::zero() + } + + fn is_zero(&self) -> bool { + *self == Int::zero() + } + } + + impl One for $T { + fn one() -> $T { + Int::one() + } + } + ) +) + +impl_basenum_int!(i8) +impl_basenum_int!(i16) +impl_basenum_int!(i32) +impl_basenum_int!(i64) +impl_basenum_int!(u8) +impl_basenum_int!(u16) +impl_basenum_int!(u32) +impl_basenum_int!(u64) +impl_basenum_int!(int) +impl_basenum_int!(uint) + + +macro_rules! impl_basenum_float ( + ($T: ident) => ( + impl BaseNum for $T {} + impl Zero for $T { + fn zero() -> $T { + Float::zero() + } + + fn is_zero(&self) -> bool { + *self == Float::zero() + } + } + + impl One for $T { + fn one() -> $T { + Float::one() + } + } + ) +) + +impl_basenum_float!(f32) +impl_basenum_float!(f64) + +pub fn zero() -> T { + Zero::zero() +} + +pub fn one() -> T { + One::one() +} -impl BaseNum for i8 {} -impl BaseNum for i16 {} -impl BaseNum for i32 {} -impl BaseNum for i64 {} -impl BaseNum for int {} -impl BaseNum for u8 {} -impl BaseNum for u16 {} -impl BaseNum for u32 {} -impl BaseNum for u64 {} -impl BaseNum for uint {} -impl BaseNum for f32 {} -impl BaseNum for f64 {} /// Base integer types pub trait BaseInt : BaseNum + Int {} diff --git a/src/plane.rs b/src/plane.rs index 1b8fb2e..3efb1c0 100644 --- a/src/plane.rs +++ b/src/plane.rs @@ -14,11 +14,10 @@ // limitations under the License. use std::fmt; -use std::num::Zero; use approx::ApproxEq; use intersect::Intersect; -use num::BaseFloat; +use num::{BaseFloat, Zero, zero}; use point::{Point, Point3}; use ray::Ray3; use vector::{Vector3, Vector4}; @@ -80,7 +79,7 @@ impl Plane { // find the normal vector that is perpendicular to v1 and v2 let mut n = v0.cross(&v1); - if n.approx_eq(&Vector3::zero()) { None } + if n.approx_eq(&zero()) { None } else { // compute the normal and the distance to the plane n.normalize_self(); diff --git a/src/point.rs b/src/point.rs index 6667781..1b9aa87 100644 --- a/src/point.rs +++ b/src/point.rs @@ -19,11 +19,10 @@ use std::fmt; use std::mem; -use std::num::{one, zero}; use approx::ApproxEq; use array::{Array1, FixedArray}; -use num::{BaseNum, BaseFloat}; +use num::{BaseNum, BaseFloat, one, zero}; use vector::*; /// A point in 2-dimensional space. diff --git a/src/projection.rs b/src/projection.rs index 52d8932..6baaab7 100644 --- a/src/projection.rs +++ b/src/projection.rs @@ -13,12 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::num::{zero, one, cast}; +use std::num::cast; use angle::{Angle, tan, cot}; use frustum::Frustum; use matrix::{Matrix4, ToMatrix4}; -use num::BaseFloat; +use num::{BaseFloat, zero, one}; use plane::Plane; /// Create a perspective projection matrix. diff --git a/src/quaternion.rs b/src/quaternion.rs index 62c4a32..7ded708 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -15,13 +15,13 @@ use std::fmt; use std::mem; -use std::num::{zero, one, cast, Float}; +use std::num::{cast, Float}; use angle::{Angle, Rad, acos, sin, sin_cos, rad}; use approx::ApproxEq; use array::Array1; use matrix::{Matrix3, ToMatrix3, ToMatrix4, Matrix4}; -use num::BaseFloat; +use num::{BaseFloat, one, zero}; use point::Point3; use rotation::{Rotation, Rotation3, Basis3, ToBasis3}; use vector::{Vector3, Vector, EuclideanVector}; @@ -87,7 +87,7 @@ impl Quaternion { /// The multiplicative identity, ie: `q = 1 + 0i + 0j + 0i` #[inline] pub fn identity() -> Quaternion { - Quaternion::from_sv(one::(), Vector3::zero()) + Quaternion::from_sv(one::(), zero()) } /// The result of multiplying the quaternion a scalar diff --git a/src/sphere.rs b/src/sphere.rs index 4f61ce0..201473b 100644 --- a/src/sphere.rs +++ b/src/sphere.rs @@ -16,13 +16,11 @@ //! Bounding sphere use intersect::Intersect; -use num::BaseFloat; +use num::{BaseFloat, zero}; use point::{Point, Point3}; use ray::Ray3; use vector::Vector; -use std::num::zero; - #[deriving(Clone, PartialEq, Encodable, Decodable)] pub struct Sphere { pub center: Point3, diff --git a/src/transform.rs b/src/transform.rs index 9d5c62f..3bc4783 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -13,11 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{fmt, num}; +use std::fmt; use approx::ApproxEq; use matrix::{Matrix, Matrix4, ToMatrix4}; -use num::{BaseNum, BaseFloat}; +use num::{BaseNum, BaseFloat, zero, one}; use point::{Point, Point3}; use ray::Ray; use rotation::{Rotation, Rotation3}; @@ -87,9 +87,9 @@ impl, P: Point, R: Rotation> Transform #[inline] fn identity() -> Decomposed { Decomposed { - scale: num::one(), + scale: one(), rot: Rotation::identity(), - disp: num::zero(), + disp: zero(), } } @@ -99,7 +99,7 @@ impl, P: Point, R: Rotation> Transform let rot: R = Rotation::look_at(¢er.sub_p(eye), up); let disp: V = rot.rotate_vector(&origin.sub_p(eye)); Decomposed { - scale: num::one(), + scale: one(), rot: rot, disp: disp, } @@ -124,10 +124,10 @@ impl, P: Point, R: Rotation> Transform } fn invert(&self) -> Option> { - if self.scale.approx_eq(&num::zero()) { + if self.scale.approx_eq(&zero()) { None } else { - let s = num::one::() / self.scale; + let s = one::() / self.scale; let r = self.rot.invert(); let d = r.rotate_vector(&self.disp).mul_s(-s); Some(Decomposed { @@ -144,7 +144,7 @@ pub trait Transform3: Transform, Point3>+ ToMatrix4 {} impl> ToMatrix4 for Decomposed, R> { fn to_matrix4(&self) -> Matrix4 { let mut m = self.rot.to_matrix3().mul_s(self.scale.clone()).to_matrix4(); - m.w = self.disp.extend(num::one()); + m.w = self.disp.extend(one()); m } } @@ -177,7 +177,7 @@ impl Transform, Point3> for AffineMatri #[inline] fn transform_vector(&self, vec: &Vector3) -> Vector3 { - self.mat.mul_v(&vec.extend(num::zero())).truncate() + self.mat.mul_v(&vec.extend(zero())).truncate() } #[inline] diff --git a/src/vector.rs b/src/vector.rs index 9f3280c..8351b99 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -22,29 +22,27 @@ //! vector are also provided: //! //! ```rust -//! use cgmath::{Vector2, Vector3, Vector4}; +//! use cgmath::{Vector2, Vector3, Vector4, one, zero}; //! //! assert_eq!(Vector2::new(1.0f64, 0.0f64), Vector2::unit_x()); -//! assert_eq!(Vector3::new(0.0f64, 0.0f64, 0.0f64), Vector3::zero()); -//! assert_eq!(Vector4::from_value(1.0f64), Vector4::ident()); +//! assert_eq!(Vector3::new(0.0f64, 0.0f64, 0.0f64), zero()); +//! assert_eq!(Vector4::from_value(1.0f64), one()); //! ``` //! //! Vectors can be manipulated with typical mathematical operations (addition, //! subtraction, element-wise multiplication, element-wise division, negation) -//! using the built-in operators. The additive and multiplicative inverses -//! (zero and one) provided by the standard library's `Zero` and `One` are also -//! available: +//! using the built-in operators. The additive and multiplicative neutral +//! elements (zero and one) are also provided by this library //! //! ```rust -//! use std::num::{Zero, One}; -//! use cgmath::{Vector2, Vector3, Vector4}; +//! use cgmath::{Vector2, Vector3, Vector4, one, zero}; //! //! let a: Vector2 = Vector2::new(3.0, 4.0); //! let b: Vector2 = Vector2::new(-3.0, -4.0); //! -//! assert_eq!(a + b, Zero::zero()); +//! assert_eq!(a + b, zero()); //! assert_eq!(-(a * b), Vector2::new(9.0f64, 16.0f64)); -//! assert_eq!(a / One::one(), a); +//! assert_eq!(a / one(), a); //! //! // As with Rust's `int` and `f32` types, Vectors of different types cannot //! // be added and so on with impunity. The following will fail to compile: @@ -66,13 +64,12 @@ //! and [cross products](http://en.wikipedia.org/wiki/Cross_product). //! //! ```rust -//! use std::num::Zero; -//! use cgmath::{Vector, Vector2, Vector3, Vector4, dot}; +//! use cgmath::{Vector, Vector2, Vector3, Vector4, dot, zero}; //! //! // All vectors implement the dot product as a method: //! let a: Vector2 = Vector2::new(3.0, 6.0); //! let b: Vector2 = Vector2::new(-2.0, 1.0); -//! assert_eq!(a.dot(&b), Zero::zero()); +//! assert_eq!(a.dot(&b), zero()); //! //! // But there is also a top-level function: //! assert_eq!(a.dot(&b), dot(a, b)); @@ -101,20 +98,17 @@ use std::fmt; use std::mem; -use std::num::{Zero, zero, One, one}; +use std::num::NumCast; use angle::{Rad, atan2, acos}; use approx::ApproxEq; use array::{Array1, FixedArray}; -use num::{BaseNum, BaseFloat}; +use num::{BaseNum, BaseFloat, Zero, One, zero, one}; /// A trait that specifies a range of numeric operations for vectors. Not all /// of these make sense from a linear algebra point of view, but are included /// for pragmatic reasons. -pub trait Vector: Array1 - + Neg - + Zero - + One { +pub trait Vector: Array1 + Zero + One + Neg { /// Add a scalar to this vector, returning a new vector. fn add_s(&self, s: S) -> Self; /// Subtract a scalar from this vector, returning a new vector. @@ -202,14 +196,25 @@ macro_rules! vec( } } - impl<$S: BaseNum> $Self<$S> { - /// The additive identity of the vector. + impl<$S: Zero> Zero for $Self<$S> { #[inline] - pub fn zero() -> $Self<$S> { $Self::from_value(zero()) } + fn zero() -> $Self { $Self { $($field: zero()),+ } } - /// The multiplicative identity of the vector. #[inline] - pub fn ident() -> $Self<$S> { $Self::from_value(one()) } + fn is_zero(&self) -> bool { $((self.$field.is_zero()) )&&+ } + } + + impl<$S: One> One for $Self<$S> { + #[inline] + fn one() -> $Self<$S> { $Self { $($field: one()),+ } } + } + + impl<$S: NumCast + Copy> $Self<$S> { + /// Component-wise casting to another type + #[inline] + pub fn cast(&self) -> $Self { + $Self { $($field: NumCast::from(self.$field).unwrap()),+ } + } } impl<$S> FixedArray<[$S, ..$n]> for $Self<$S> { @@ -307,11 +312,6 @@ macro_rules! vec( #[inline] fn sub(&self, v: &$Self) -> $Self { self.sub_v(v) } } - impl Zero for $Self { - #[inline] fn zero() -> $Self { $Self::from_value(zero()) } - #[inline] fn is_zero(&self) -> bool { *self == zero() } - } - impl Neg<$Self> for $Self { #[inline] fn neg(&self) -> $Self { $Self::new($(-self.$field),+) } } @@ -328,10 +328,6 @@ macro_rules! vec( #[inline] fn rem(&self, v: &$Self) -> $Self { self.rem_v(v) } } - impl One for $Self { - #[inline] fn one() -> $Self { $Self::from_value(one()) } - } - impl ApproxEq for $Self { #[inline] fn approx_eq_eps(&self, other: &$Self, epsilon: &S) -> bool { diff --git a/tests/vector.rs b/tests/vector.rs index 0d74a7e..446b53f 100644 --- a/tests/vector.rs +++ b/tests/vector.rs @@ -14,7 +14,10 @@ // limitations under the License. #![feature(globs)] +#![feature(phase)] +#[phase(plugin)] +extern crate cgmath; extern crate cgmath; use cgmath::*; @@ -166,3 +169,10 @@ fn test_map() { assert_eq!(Vector3::new(7.12f64, 3.8f64, -6.98f64).map(|x| x.floor()), Vector3::new(7.0f64, 3.0f64, -7.0f64)); assert_eq!(Vector3::new(7.12f64, 3.8f64, -6.98f64).map(|x| x.max(0.0f64)), Vector3::new(7.12f64, 3.8f64, 0.0f64)); } + +#[test] +fn test_cast() { + assert_approx_eq!(Vector2::new(0.9f64, 1.5).cast(), Vector2::new(0.9f32, 1.5)); + assert_approx_eq!(Vector3::new(1.0f64, 2.4, -3.13).cast(), Vector3::new(1.0f32, 2.4, -3.13)); + assert_approx_eq!(Vector4::new(13.5f64, -4.6, -8.3, 2.41).cast(), Vector4::new(13.5f32, -4.6, -8.3, 2.41)); +}