Merge pull request #142 from aepsil0n/vector_cast

Type-cast a vector component-wise
This commit is contained in:
Corey Richardson 2014-11-20 10:46:30 -05:00
commit bcc4f11150
14 changed files with 149 additions and 80 deletions

View file

@ -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<S: BaseNum, V: Vector<S>, P: Point<S, V>> {
/// Create a new AABB using two points as opposing corners.

View file

@ -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)]

View file

@ -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

View file

@ -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};

View file

@ -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};

View file

@ -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<Self, Self> + Sub<Self, Self> +
Mul<Self, Self> + Div<Self, Self> + Rem<Self, Self> + Neg<Self> + 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>() -> T {
Zero::zero()
}
pub fn one<T: 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 {}

View file

@ -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<S: BaseFloat> Plane<S> {
// 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();

View file

@ -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.

View file

@ -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.

View file

@ -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<S: BaseFloat> Quaternion<S> {
/// The multiplicative identity, ie: `q = 1 + 0i + 0j + 0i`
#[inline]
pub fn identity() -> Quaternion<S> {
Quaternion::from_sv(one::<S>(), Vector3::zero())
Quaternion::from_sv(one::<S>(), zero())
}
/// The result of multiplying the quaternion a scalar

View file

@ -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<S> {
pub center: Point3<S>,

View file

@ -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<S: BaseFloat, V: Vector<S>, P: Point<S, V>, R: Rotation<S, V, P>> Transform
#[inline]
fn identity() -> Decomposed<S, V, R> {
Decomposed {
scale: num::one(),
scale: one(),
rot: Rotation::identity(),
disp: num::zero(),
disp: zero(),
}
}
@ -99,7 +99,7 @@ impl<S: BaseFloat, V: Vector<S>, P: Point<S, V>, R: Rotation<S, V, P>> Transform
let rot: R = Rotation::look_at(&center.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<S: BaseFloat, V: Vector<S>, P: Point<S, V>, R: Rotation<S, V, P>> Transform
}
fn invert(&self) -> Option<Decomposed<S, V, R>> {
if self.scale.approx_eq(&num::zero()) {
if self.scale.approx_eq(&zero()) {
None
} else {
let s = num::one::<S>() / self.scale;
let s = one::<S>() / 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<S>: Transform<S, Vector3<S>, Point3<S>>+ ToMatrix4<S> {}
impl<S: BaseFloat + 'static, R: Rotation3<S>> ToMatrix4<S> for Decomposed<S, Vector3<S>, R> {
fn to_matrix4(&self) -> Matrix4<S> {
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<S: BaseFloat + 'static> Transform<S, Vector3<S>, Point3<S>> for AffineMatri
#[inline]
fn transform_vector(&self, vec: &Vector3<S>) -> Vector3<S> {
self.mat.mul_v(&vec.extend(num::zero())).truncate()
self.mat.mul_v(&vec.extend(zero())).truncate()
}
#[inline]

View file

@ -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<f64> = Vector2::new(3.0, 4.0);
//! let b: Vector2<f64> = 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<f64> = Vector2::new(3.0, 6.0);
//! let b: Vector2<f64> = 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<S: BaseNum>: Array1<S>
+ Neg<Self>
+ Zero
+ One {
pub trait Vector<S: BaseNum>: Array1<S> + Zero + One + Neg<Self> {
/// 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<S> { $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<T: NumCast>(&self) -> $Self<T> {
$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<S>) -> $Self<S> { self.sub_v(v) }
}
impl<S: BaseNum> Zero for $Self<S> {
#[inline] fn zero() -> $Self<S> { $Self::from_value(zero()) }
#[inline] fn is_zero(&self) -> bool { *self == zero() }
}
impl<S: BaseNum> Neg<$Self<S>> for $Self<S> {
#[inline] fn neg(&self) -> $Self<S> { $Self::new($(-self.$field),+) }
}
@ -328,10 +328,6 @@ macro_rules! vec(
#[inline] fn rem(&self, v: &$Self<S>) -> $Self<S> { self.rem_v(v) }
}
impl<S: BaseNum> One for $Self<S> {
#[inline] fn one() -> $Self<S> { $Self::from_value(one()) }
}
impl<S: BaseFloat> ApproxEq<S> for $Self<S> {
#[inline]
fn approx_eq_eps(&self, other: &$Self<S>, epsilon: &S) -> bool {

View file

@ -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));
}