Merge pull request #320 from bjz/the-grand-trait-unification

Unify traits into an algebraic heirachy
This commit is contained in:
Brendan Zabarauskas 2016-04-12 08:48:13 +10:00
commit 3cc33c4606
8 changed files with 213 additions and 248 deletions

View file

@ -13,19 +13,41 @@
// 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.
//! Computer graphics-centric math. //! A low-dimensional linear algebra library, targeted at computer graphics.
//! //!
//! This crate provides useful mathematical primitives and operations on them. //! # Trait overview
//! It is organized into one module per primitive. The core structures are
//! vectors and matrices. A strongly-typed interface is provided, to prevent
//! mixing units or violating mathematical invariants.
//! //!
//! Transformations are not usually done directly on matrices, but go through //! In order to make a clean, composable API, we divide operations into traits
//! transformation objects that can be converted to matrices. Rotations go //! that are roughly based on mathematical properties. The main ones that we
//! through the `Basis` types, which are guaranteed to be orthogonal matrices. //! concern ourselves with are listed below:
//! Despite this, one can directly create a limited rotation matrix using the //!
//! `look_at`, `from_angle`, `from_euler`, and `from_axis_angle` methods. //! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and
//! These are provided for convenience. //! matrices.
//! - `InnerSpace`: For types that have a dot (or inner) product - ie. vectors or
//! quaternions. This also allows for the definition of operations that are
//! based on the dot product, like finding the magnitude or normalizing.
//! - `EuclideanSpace`: Points in euclidean space, with an associated space of
//! displacement vectors.
//! - `Matrix`: Common operations for matrices of arbitrary dimensions.
//! - `SquareMatrix`: A special trait for matrices where the number of columns
//! equal the number of rows.
//!
//! Other traits are included for practical convenience, for example:
//!
//! - `Array`: For contiguous, indexable arrays of elements, specifically
//! vectors.
//! - `ElementWise`: For element-wise addition, subtraction, multiplication,
//! division, and remainder operations.
//!
//! # The prelude
//!
//! Importing each trait individually can become a chore, so we provide a
//! `prelude` module to allow you to import the main trait all at once. For
//! example:
//!
//! ```rust
//! use cgmath::prelude::*;
//! ```
extern crate num as rust_num; extern crate num as rust_num;
extern crate rustc_serialize; extern crate rustc_serialize;

View file

@ -13,8 +13,6 @@
// 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.
//! Column major, square matrix types and traits.
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::ops::*; use std::ops::*;
@ -29,9 +27,9 @@ use angle::{Angle, Rad};
use approx::ApproxEq; use approx::ApproxEq;
use array::Array; use array::Array;
use num::BaseFloat; use num::BaseFloat;
use point::{Point, Point3}; use point::{EuclideanSpace, Point3};
use quaternion::Quaternion; use quaternion::Quaternion;
use vector::{Vector, EuclideanVector}; use vector::{VectorSpace, InnerSpace};
use vector::{Vector2, Vector3, Vector4}; use vector::{Vector2, Vector3, Vector4};
/// A 2 x 2, column major matrix /// A 2 x 2, column major matrix
@ -40,7 +38,9 @@ use vector::{Vector2, Vector3, Vector4};
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Matrix2<S> { pub struct Matrix2<S> {
/// The first column of the matrix.
pub x: Vector2<S>, pub x: Vector2<S>,
/// The second column of the matrix.
pub y: Vector2<S>, pub y: Vector2<S>,
} }
@ -50,8 +50,11 @@ pub struct Matrix2<S> {
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Matrix3<S> { pub struct Matrix3<S> {
/// The first column of the matrix.
pub x: Vector3<S>, pub x: Vector3<S>,
/// The second column of the matrix.
pub y: Vector3<S>, pub y: Vector3<S>,
/// The third column of the matrix.
pub z: Vector3<S>, pub z: Vector3<S>,
} }
@ -61,9 +64,13 @@ pub struct Matrix3<S> {
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Matrix4<S> { pub struct Matrix4<S> {
/// The first column of the matrix.
pub x: Vector4<S>, pub x: Vector4<S>,
/// The second column of the matrix.
pub y: Vector4<S>, pub y: Vector4<S>,
/// The third column of the matrix.
pub z: Vector4<S>, pub z: Vector4<S>,
/// The fourth column of the matrix.
pub w: Vector4<S>, pub w: Vector4<S>,
} }
@ -248,41 +255,86 @@ impl<S: BaseFloat> Matrix4<S> {
} }
} }
impl<S: BaseFloat> VectorSpace for Matrix2<S> {
type Scalar = S;
#[inline]
fn zero() -> Matrix2<S> {
Matrix2::new(S::zero(), S::zero(),
S::zero(), S::zero())
}
}
impl<S: BaseFloat> VectorSpace for Matrix3<S> {
type Scalar = S;
#[inline]
fn zero() -> Matrix3<S> {
Matrix3::new(S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero())
}
}
impl<S: BaseFloat> VectorSpace for Matrix4<S> {
type Scalar = S;
#[inline]
fn zero() -> Matrix4<S> {
Matrix4::new(S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero())
}
}
/// A column-major matrix of arbitrary dimensions. /// A column-major matrix of arbitrary dimensions.
pub trait Matrix where ///
/// Because this is constrained to the `VectorSpace` trait, this means that
/// following operators are required to be implemented:
///
/// Matrix addition:
///
/// - `Add<Output = Self>`
/// - `Sub<Output = Self>`
/// - `Neg<Output = Self>`
///
/// Scalar multiplication:
///
/// - `Mul<Self::Scalar, Output = Self>`
/// - `Div<Self::Scalar, Output = Self>`
/// - `Rem<Self::Scalar, Output = Self>`
///
/// Note that matrix multiplication is not required for implementors of this
/// trait. This is due to the complexities of implementing these operators with
/// Rust's current type system. For the multiplication of square matrices,
/// see `SquareMatrix`.
pub trait Matrix: VectorSpace where
Self::Scalar: BaseFloat,
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Index<usize, Output = <Self as Matrix>::Column>, Self: Index<usize, Output = <Self as Matrix>::Column>,
Self: IndexMut<usize, Output = <Self as Matrix>::Column>, Self: IndexMut<usize, Output = <Self as Matrix>::Column>,
Self: ApproxEq<Epsilon = <Self as Matrix>::Element>, Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
Self: Add<Self, Output = Self>,
Self: Sub<Self, Output = Self>,
Self: Neg<Output = Self>,
Self: Mul<<Self as Matrix>::Element, Output = Self>,
Self: Div<<Self as Matrix>::Element, Output = Self>,
Self: Rem<<Self as Matrix>::Element, Output = Self>,
{ {
/// The type of the elements in the matrix.
type Element: BaseFloat;
/// The row vector of the matrix. /// The row vector of the matrix.
type Row: Array<Element = Self::Element>; type Row: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
/// The column vector of the matrix.
type Column: Array<Element = Self::Element>;
/// The type of the transposed matrix /// The column vector of the matrix.
type Transpose: Matrix<Element = Self::Element, Row = Self::Column, Column = Self::Row>; type Column: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
/// The result of transposing the matrix
type Transpose: Matrix<Scalar = Self::Scalar, Row = Self::Column, Column = Self::Row>;
/// Get the pointer to the first element of the array. /// Get the pointer to the first element of the array.
#[inline] #[inline]
fn as_ptr(&self) -> *const Self::Element { fn as_ptr(&self) -> *const Self::Scalar {
&self[0][0] &self[0][0]
} }
/// Get a mutable pointer to the first element of the array. /// Get a mutable pointer to the first element of the array.
#[inline] #[inline]
fn as_mut_ptr(&mut self) -> *mut Self::Element { fn as_mut_ptr(&mut self) -> *mut Self::Scalar {
&mut self[0][0] &mut self[0][0]
} }
@ -302,15 +354,14 @@ pub trait Matrix where
/// Swap the values at index `a` and `b` /// Swap the values at index `a` and `b`
fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize)); fn swap_elements(&mut self, a: (usize, usize), b: (usize, usize));
/// Create a matrix with all of the elements set to zero.
fn zero() -> Self;
/// Transpose this matrix, returning a new matrix. /// Transpose this matrix, returning a new matrix.
fn transpose(&self) -> Self::Transpose; fn transpose(&self) -> Self::Transpose;
} }
/// A column-major major matrix where the rows and column vectors are of the same dimensions. /// A column-major major matrix where the rows and column vectors are of the same dimensions.
pub trait SquareMatrix where pub trait SquareMatrix where
Self::Scalar: BaseFloat,
Self: Matrix< Self: Matrix<
// FIXME: Can be cleaned up once equality constraints in where clauses are implemented // FIXME: Can be cleaned up once equality constraints in where clauses are implemented
Column = <Self as SquareMatrix>::ColumnRow, Column = <Self as SquareMatrix>::ColumnRow,
@ -326,12 +377,12 @@ pub trait SquareMatrix where
/// This is used to constrain the column and rows to be of the same type in lieu of equality /// This is used to constrain the column and rows to be of the same type in lieu of equality
/// constraints being implemented for `where` clauses. Once those are added, this type will /// constraints being implemented for `where` clauses. Once those are added, this type will
/// likely go away. /// likely go away.
type ColumnRow: Array<Element = Self::Element>; type ColumnRow: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
/// Create a new diagonal matrix using the supplied value. /// Create a new diagonal matrix using the supplied value.
fn from_value(value: Self::Element) -> Self; fn from_value(value: Self::Scalar) -> Self;
/// Create a matrix from a non-uniform scale /// Create a matrix from a non-uniform scale
fn from_diagonal(diagonal: Self::Column) -> Self; fn from_diagonal(diagonal: Self::ColumnRow) -> Self;
/// The [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix). Multiplying this /// The [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix). Multiplying this
/// matrix with another has no effect. /// matrix with another has no effect.
@ -340,14 +391,14 @@ pub trait SquareMatrix where
/// Transpose this matrix in-place. /// Transpose this matrix in-place.
fn transpose_self(&mut self); fn transpose_self(&mut self);
/// Take the determinant of this matrix. /// Take the determinant of this matrix.
fn determinant(&self) -> Self::Element; fn determinant(&self) -> Self::Scalar;
/// Return a vector containing the diagonal of this matrix. /// Return a vector containing the diagonal of this matrix.
fn diagonal(&self) -> Self::Column; fn diagonal(&self) -> Self::ColumnRow;
/// Return the trace of this matrix. That is, the sum of the diagonal. /// Return the trace of this matrix. That is, the sum of the diagonal.
#[inline] #[inline]
fn trace(&self) -> Self::Element { self.diagonal().sum() } fn trace(&self) -> Self::Scalar { self.diagonal().sum() }
/// Invert this matrix, returning a new matrix. `m.mul_m(m.invert())` is /// Invert this matrix, returning a new matrix. `m.mul_m(m.invert())` is
/// the identity matrix. Returns `None` if this matrix is not invertible /// the identity matrix. Returns `None` if this matrix is not invertible
@ -363,7 +414,7 @@ pub trait SquareMatrix where
/// Test if this matrix is invertible. /// Test if this matrix is invertible.
#[inline] #[inline]
fn is_invertible(&self) -> bool { !self.determinant().approx_eq(&Self::Element::zero()) } fn is_invertible(&self) -> bool { !self.determinant().approx_eq(&Self::Scalar::zero()) }
/// Test if this matrix is the identity matrix. That is, it is diagonal /// Test if this matrix is the identity matrix. That is, it is diagonal
/// and every element in the diagonal is one. /// and every element in the diagonal is one.
@ -380,7 +431,6 @@ pub trait SquareMatrix where
} }
impl<S: BaseFloat> Matrix for Matrix2<S> { impl<S: BaseFloat> Matrix for Matrix2<S> {
type Element = S;
type Column = Vector2<S>; type Column = Vector2<S>;
type Row = Vector2<S>; type Row = Vector2<S>;
type Transpose = Matrix2<S>; type Transpose = Matrix2<S>;
@ -409,12 +459,6 @@ impl<S: BaseFloat> Matrix for Matrix2<S> {
unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) };
} }
#[inline]
fn zero() -> Matrix2<S> {
Matrix2::new(S::zero(), S::zero(),
S::zero(), S::zero())
}
fn transpose(&self) -> Matrix2<S> { fn transpose(&self) -> Matrix2<S> {
Matrix2::new(self[0][0], self[1][0], Matrix2::new(self[0][0], self[1][0],
self[0][1], self[1][1]) self[0][1], self[1][1])
@ -483,7 +527,6 @@ impl<S: BaseFloat> SquareMatrix for Matrix2<S> {
} }
impl<S: BaseFloat> Matrix for Matrix3<S> { impl<S: BaseFloat> Matrix for Matrix3<S> {
type Element = S;
type Column = Vector3<S>; type Column = Vector3<S>;
type Row = Vector3<S>; type Row = Vector3<S>;
type Transpose = Matrix3<S>; type Transpose = Matrix3<S>;
@ -514,13 +557,6 @@ impl<S: BaseFloat> Matrix for Matrix3<S> {
unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) };
} }
#[inline]
fn zero() -> Matrix3<S> {
Matrix3::new(S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero())
}
fn transpose(&self) -> Matrix3<S> { fn transpose(&self) -> Matrix3<S> {
Matrix3::new(self[0][0], self[1][0], self[2][0], Matrix3::new(self[0][0], self[1][0], self[2][0],
self[0][1], self[1][1], self[2][1], self[0][1], self[1][1], self[2][1],
@ -603,7 +639,6 @@ impl<S: BaseFloat> SquareMatrix for Matrix3<S> {
} }
impl<S: BaseFloat> Matrix for Matrix4<S> { impl<S: BaseFloat> Matrix for Matrix4<S> {
type Element = S;
type Column = Vector4<S>; type Column = Vector4<S>;
type Row = Vector4<S>; type Row = Vector4<S>;
type Transpose = Matrix4<S>; type Transpose = Matrix4<S>;
@ -636,14 +671,6 @@ impl<S: BaseFloat> Matrix for Matrix4<S> {
unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) };
} }
#[inline]
fn zero() -> Matrix4<S> {
Matrix4::new(S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero(),
S::zero(), S::zero(), S::zero(), S::zero())
}
fn transpose(&self) -> Matrix4<S> { fn transpose(&self) -> Matrix4<S> {
Matrix4::new(self[0][0], self[1][0], self[2][0], self[3][0], Matrix4::new(self[0][0], self[1][0], self[2][0], self[3][0],
self[0][1], self[1][1], self[2][1], self[3][1], self[0][1], self[1][1], self[2][1], self[3][1],

View file

@ -25,7 +25,6 @@ use rust_num::{One, Zero};
use approx::ApproxEq; use approx::ApproxEq;
use array::Array; use array::Array;
use matrix::Matrix;
use num::{BaseNum, BaseFloat}; use num::{BaseNum, BaseFloat};
use vector::*; use vector::*;
@ -79,7 +78,7 @@ impl<S: BaseNum> Point3<S> {
} }
/// Points in a [Euclidean space](https://en.wikipedia.org/wiki/Euclidean_space) /// Points in a [Euclidean space](https://en.wikipedia.org/wiki/Euclidean_space)
/// with an associated vector space, `Self::Vector`. /// with an associated space of displacement vectors.
/// ///
/// # Point-Vector distinction /// # Point-Vector distinction
/// ///
@ -116,34 +115,34 @@ impl<S: BaseNum> Point3<S> {
/// ## Converting between points and vectors /// ## Converting between points and vectors
/// ///
/// Points can be converted to and from displacement vectors using the /// Points can be converted to and from displacement vectors using the
/// `Point::{from_vec, to_vec}` methods. Note that under the hood these are /// `EuclideanSpace::{from_vec, to_vec}` methods. Note that under the hood these
/// implemented as inlined a type conversion, so should not have any performance /// are implemented as inlined a type conversion, so should not have any
/// implications. /// performance implications.
/// ///
/// ## References /// ## References
/// ///
/// - [CGAL 4.7 - 2D and 3D Linear Geometry Kernel: 3.1 Points and Vectors](http://doc.cgal.org/latest/Kernel_23/index.html#Kernel_23PointsandVectors) /// - [CGAL 4.7 - 2D and 3D Linear Geometry Kernel: 3.1 Points and Vectors](http://doc.cgal.org/latest/Kernel_23/index.html#Kernel_23PointsandVectors)
/// - [What is the difference between a point and a vector](http://math.stackexchange.com/q/645827) /// - [What is the difference between a point and a vector](http://math.stackexchange.com/q/645827)
/// ///
pub trait Point: Copy + Clone where pub trait EuclideanSpace: Copy + Clone where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Array<Element = <Self as Point>::Scalar>, Self: Array<Element = <Self as EuclideanSpace>::Scalar>,
Self: Add<<Self as Point>::Vector, Output = Self>, Self: Add<<Self as EuclideanSpace>::Diff, Output = Self>,
Self: Sub<Self, Output = <Self as Point>::Vector>, Self: Sub<Self, Output = <Self as EuclideanSpace>::Diff>,
Self: Mul<<Self as Point>::Scalar, Output = Self>, Self: Mul<<Self as EuclideanSpace>::Scalar, Output = Self>,
Self: Div<<Self as Point>::Scalar, Output = Self>, Self: Div<<Self as EuclideanSpace>::Scalar, Output = Self>,
Self: Rem<<Self as Point>::Scalar, Output = Self>, Self: Rem<<Self as EuclideanSpace>::Scalar, Output = Self>,
{ {
/// The associated scalar. /// The associated scalar over which the space is defined.
/// ///
/// Due to the equality constraints demanded by `Self::Vector`, this is effectively just an /// Due to the equality constraints demanded by `Self::Diff`, this is effectively just an
/// alias to `Self::Vector::Scalar`. /// alias to `Self::Diff::Scalar`.
type Scalar: BaseNum; type Scalar: BaseNum;
/// The associated space of displacement vectors. /// The associated space of displacement vectors.
type Vector: Vector<Scalar = Self::Scalar>; type Diff: VectorSpace<Scalar = Self::Scalar>;
/// The point at the origin of the Euclidean space. /// The point at the origin of the Euclidean space.
fn origin() -> Self; fn origin() -> Self;
@ -152,16 +151,16 @@ pub trait Point: Copy + Clone where
/// ///
/// This can be considered equivalent to the addition of the displacement /// This can be considered equivalent to the addition of the displacement
/// vector `v` to to `Self::origin()`. /// vector `v` to to `Self::origin()`.
fn from_vec(v: Self::Vector) -> Self; fn from_vec(v: Self::Diff) -> Self;
/// Convert a point to a displacement vector. /// Convert a point to a displacement vector.
/// ///
/// This can be seen as equivalent to the displacement vector from /// This can be seen as equivalent to the displacement vector from
/// `Self::origin()` to `self`. /// `Self::origin()` to `self`.
fn to_vec(self) -> Self::Vector; fn to_vec(self) -> Self::Diff;
/// This is a weird one, but its useful for plane calculations. /// This is a weird one, but its useful for plane calculations.
fn dot(self, v: Self::Vector) -> Self::Scalar; fn dot(self, v: Self::Diff) -> Self::Scalar;
} }
macro_rules! impl_point { macro_rules! impl_point {
@ -195,9 +194,9 @@ macro_rules! impl_point {
} }
} }
impl<S: BaseNum> Point for $PointN<S> { impl<S: BaseNum> EuclideanSpace for $PointN<S> {
type Scalar = S; type Scalar = S;
type Vector = $VectorN<S>; type Diff = $VectorN<S>;
#[inline] #[inline]
fn origin() -> $PointN<S> { fn origin() -> $PointN<S> {

View file

@ -10,7 +10,7 @@ pub use array::ElementWise;
pub use matrix::Matrix; pub use matrix::Matrix;
pub use matrix::SquareMatrix; pub use matrix::SquareMatrix;
pub use point::Point; pub use point::EuclideanSpace;
pub use rotation::Rotation; pub use rotation::Rotation;
pub use rotation::Rotation2; pub use rotation::Rotation2;
@ -20,5 +20,5 @@ pub use transform::Transform;
pub use transform::Transform2; pub use transform::Transform2;
pub use transform::Transform3; pub use transform::Transform3;
pub use vector::EuclideanVector; pub use vector::InnerSpace;
pub use vector::Vector; pub use vector::VectorSpace;

View file

@ -26,7 +26,7 @@ use matrix::{Matrix3, Matrix4};
use num::BaseFloat; use num::BaseFloat;
use point::Point3; use point::Point3;
use rotation::{Rotation, Rotation3, Basis3}; use rotation::{Rotation, Rotation3, Basis3};
use vector::{Vector3, Vector, EuclideanVector}; use vector::{Vector3, VectorSpace, InnerSpace};
/// A [quaternion](https://en.wikipedia.org/wiki/Quaternion) in scalar/vector /// A [quaternion](https://en.wikipedia.org/wiki/Quaternion) in scalar/vector
@ -36,7 +36,9 @@ use vector::{Vector3, Vector, EuclideanVector};
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Quaternion<S> { pub struct Quaternion<S> {
/// The scalar part of the quaternion.
pub s: S, pub s: S,
/// The vector part of the quaternion.
pub v: Vector3<S>, pub v: Vector3<S>,
} }
@ -54,62 +56,40 @@ impl<S: BaseFloat> Quaternion<S> {
Quaternion { s: s, v: v } Quaternion { s: s, v: v }
} }
/// The additive identity, ie: `q = 0 + 0i + 0j + 0i` /// The multiplicative identity.
#[inline]
pub fn zero() -> Quaternion<S> {
Quaternion::new(S::zero(), S::zero(), S::zero(), S::zero())
}
/// The multiplicative identity, ie: `q = 1 + 0i + 0j + 0i`
#[inline] #[inline]
pub fn one() -> Quaternion<S> { pub fn one() -> Quaternion<S> {
Quaternion::from_sv(S::one(), Vector3::zero()) Quaternion::from_sv(S::one(), Vector3::zero())
} }
/// The dot product of the quaternion and `q`.
#[inline]
pub fn dot(self, other: Quaternion<S>) -> S {
self.s * other.s + self.v.dot(other.v)
}
/// The conjugate of the quaternion. /// The conjugate of the quaternion.
#[inline] #[inline]
pub fn conjugate(self) -> Quaternion<S> { pub fn conjugate(self) -> Quaternion<S> {
Quaternion::from_sv(self.s, -self.v) Quaternion::from_sv(self.s, -self.v)
} }
/// The squared magnitude of the quaternion. This is useful for
/// magnitude comparisons where the exact magnitude does not need to be
/// calculated.
#[inline]
pub fn magnitude2(self) -> S {
self.s * self.s + self.v.magnitude2()
}
/// The magnitude of the quaternion
///
/// # Performance notes
///
/// For instances where the exact magnitude of the quaternion does not need
/// to be known, for example for quaternion-quaternion magnitude comparisons,
/// it is advisable to use the `magnitude2` method instead.
#[inline]
pub fn magnitude(self) -> S {
self.magnitude2().sqrt()
}
/// Normalize this quaternion, returning the new quaternion.
#[inline]
pub fn normalize(self) -> Quaternion<S> {
self * (S::one() / self.magnitude())
}
/// Do a normalized linear interpolation with `other`, by `amount`. /// Do a normalized linear interpolation with `other`, by `amount`.
pub fn nlerp(self, other: Quaternion<S>, amount: S) -> Quaternion<S> { pub fn nlerp(self, other: Quaternion<S>, amount: S) -> Quaternion<S> {
(self * (S::one() - amount) + other * amount).normalize() (self * (S::one() - amount) + other * amount).normalize()
} }
} }
impl<S: BaseFloat> VectorSpace for Quaternion<S> {
type Scalar = S;
#[inline]
fn zero() -> Quaternion<S> {
Quaternion::from_sv(S::zero(), Vector3::zero())
}
}
impl<S: BaseFloat> InnerSpace for Quaternion<S> {
#[inline]
fn dot(self, other: Quaternion<S>) -> S {
self.s * other.s + self.v.dot(other.v)
}
}
impl_operator!(<S: BaseFloat> Neg for Quaternion<S> { impl_operator!(<S: BaseFloat> Neg for Quaternion<S> {
fn neg(quat) -> Quaternion<S> { fn neg(quat) -> Quaternion<S> {
Quaternion::from_sv(-quat.s, -quat.v) Quaternion::from_sv(-quat.s, -quat.v)
@ -134,6 +114,15 @@ impl_assignment_operator!(<S: BaseFloat> DivAssign<S> for Quaternion<S> {
fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; } fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; }
}); });
impl_operator!(<S: BaseFloat> Rem<S> for Quaternion<S> {
fn rem(lhs, rhs) -> Quaternion<S> {
Quaternion::from_sv(lhs.s % rhs, lhs.v % rhs)
}
});
impl_assignment_operator!(<S: BaseFloat> RemAssign<S> for Quaternion<S> {
fn rem_assign(&mut self, scalar) { self.s %= scalar; self.v %= scalar; }
});
impl_operator!(<S: BaseFloat> Mul<Vector3<S> > for Quaternion<S> { impl_operator!(<S: BaseFloat> Mul<Vector3<S> > for Quaternion<S> {
fn mul(lhs, rhs) -> Vector3<S> {{ fn mul(lhs, rhs) -> Vector3<S> {{
let rhs = rhs.clone(); let rhs = rhs.clone();

View file

@ -20,29 +20,29 @@ use approx::ApproxEq;
use matrix::SquareMatrix; use matrix::SquareMatrix;
use matrix::{Matrix2, Matrix3}; use matrix::{Matrix2, Matrix3};
use num::BaseFloat; use num::BaseFloat;
use point::{Point, Point2, Point3}; use point::{EuclideanSpace, Point2, Point3};
use quaternion::Quaternion; use quaternion::Quaternion;
use vector::{EuclideanVector, Vector2, Vector3}; use vector::{InnerSpace, Vector2, Vector3};
/// A trait for a generic rotation. A rotation is a transformation that /// A trait for a generic rotation. A rotation is a transformation that
/// creates a circular motion, and preserves at least one point in the space. /// creates a circular motion, and preserves at least one point in the space.
pub trait Rotation<P: Point>: PartialEq + Sized where pub trait Rotation<P: EuclideanSpace>: PartialEq + Sized where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: ApproxEq<Epsilon = <P as Point>::Scalar>, Self: ApproxEq<Epsilon = <P as EuclideanSpace>::Scalar>,
<P as Point>::Scalar: BaseFloat, <P as EuclideanSpace>::Scalar: BaseFloat,
{ {
/// Create the identity transform (causes no transformation). /// Create the identity transform (causes no transformation).
fn one() -> Self; fn one() -> Self;
/// Create a rotation to a given direction with an 'up' vector /// Create a rotation to a given direction with an 'up' vector
fn look_at(dir: P::Vector, up: P::Vector) -> Self; fn look_at(dir: P::Diff, up: P::Diff) -> Self;
/// Create a shortest rotation to transform vector 'a' into 'b'. /// Create a shortest rotation to transform vector 'a' into 'b'.
/// Both given vectors are assumed to have unit length. /// Both given vectors are assumed to have unit length.
fn between_vectors(a: P::Vector, b: P::Vector) -> Self; fn between_vectors(a: P::Diff, b: P::Diff) -> Self;
/// Rotate a vector using this rotation. /// Rotate a vector using this rotation.
fn rotate_vector(&self, vec: P::Vector) -> P::Vector; fn rotate_vector(&self, vec: P::Diff) -> P::Diff;
/// Rotate a point using this rotation, by converting it to its /// Rotate a point using this rotation, by converting it to its
/// representation as a vector. /// representation as a vector.

View file

@ -27,24 +27,24 @@ use vector::*;
/// A trait representing an [affine /// A trait representing an [affine
/// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that /// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that
/// can be applied to points or vectors. An affine transformation is one which /// can be applied to points or vectors. An affine transformation is one which
pub trait Transform<P: Point>: Sized { pub trait Transform<P: EuclideanSpace>: Sized {
/// Create an identity transformation. That is, a transformation which /// Create an identity transformation. That is, a transformation which
/// does nothing. /// does nothing.
fn one() -> Self; fn one() -> Self;
/// Create a transformation that rotates a vector to look at `center` from /// Create a transformation that rotates a vector to look at `center` from
/// `eye`, using `up` for orientation. /// `eye`, using `up` for orientation.
fn look_at(eye: P, center: P, up: P::Vector) -> Self; fn look_at(eye: P, center: P, up: P::Diff) -> Self;
/// Transform a vector using this transform. /// Transform a vector using this transform.
fn transform_vector(&self, vec: P::Vector) -> P::Vector; fn transform_vector(&self, vec: P::Diff) -> P::Diff;
/// Transform a point using this transform. /// Transform a point using this transform.
fn transform_point(&self, point: P) -> P; fn transform_point(&self, point: P) -> P;
/// Transform a vector as a point using this transform. /// Transform a vector as a point using this transform.
#[inline] #[inline]
fn transform_as_point(&self, vec: P::Vector) -> P::Vector { fn transform_as_point(&self, vec: P::Diff) -> P::Diff {
self.transform_point(P::from_vec(vec)).to_vec() self.transform_point(P::from_vec(vec)).to_vec()
} }
@ -72,40 +72,40 @@ pub trait Transform<P: Point>: Sized {
/// A generic transformation consisting of a rotation, /// A generic transformation consisting of a rotation,
/// displacement vector and scale amount. /// displacement vector and scale amount.
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct Decomposed<V: Vector, R> { pub struct Decomposed<V: VectorSpace, R> {
pub scale: V::Scalar, pub scale: V::Scalar,
pub rot: R, pub rot: R,
pub disp: V, pub disp: V,
} }
impl<P: Point, R: Rotation<P>> Transform<P> for Decomposed<P::Vector, R> where impl<P: EuclideanSpace, R: Rotation<P>> Transform<P> for Decomposed<P::Diff, R> where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
<P as Point>::Scalar: BaseFloat, <P as EuclideanSpace>::Scalar: BaseFloat,
// FIXME: Investigate why this is needed! // FIXME: Investigate why this is needed!
<P as Point>::Vector: Vector, <P as EuclideanSpace>::Diff: VectorSpace,
{ {
#[inline] #[inline]
fn one() -> Decomposed<P::Vector, R> { fn one() -> Decomposed<P::Diff, R> {
Decomposed { Decomposed {
scale: <P as Point>::Scalar::one(), scale: <P as EuclideanSpace>::Scalar::one(),
rot: R::one(), rot: R::one(),
disp: P::Vector::zero(), disp: P::Diff::zero(),
} }
} }
#[inline] #[inline]
fn look_at(eye: P, center: P, up: P::Vector) -> Decomposed<P::Vector, R> { fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed<P::Diff, R> {
let rot = R::look_at(center - eye, up); let rot = R::look_at(center - eye, up);
let disp = rot.rotate_vector(P::origin() - eye); let disp = rot.rotate_vector(P::origin() - eye);
Decomposed { Decomposed {
scale: <P as Point>::Scalar::one(), scale: <P as EuclideanSpace>::Scalar::one(),
rot: rot, rot: rot,
disp: disp, disp: disp,
} }
} }
#[inline] #[inline]
fn transform_vector(&self, vec: P::Vector) -> P::Vector { fn transform_vector(&self, vec: P::Diff) -> P::Diff {
self.rot.rotate_vector(vec * self.scale) self.rot.rotate_vector(vec * self.scale)
} }
@ -114,7 +114,7 @@ impl<P: Point, R: Rotation<P>> Transform<P> for Decomposed<P::Vector, R> where
self.rot.rotate_point(point * self.scale) + self.disp self.rot.rotate_point(point * self.scale) + self.disp
} }
fn concat(&self, other: &Decomposed<P::Vector, R>) -> Decomposed<P::Vector, R> { fn concat(&self, other: &Decomposed<P::Diff, R>) -> Decomposed<P::Diff, R> {
Decomposed { Decomposed {
scale: self.scale * other.scale, scale: self.scale * other.scale,
rot: self.rot.concat(&other.rot), rot: self.rot.concat(&other.rot),
@ -122,11 +122,11 @@ impl<P: Point, R: Rotation<P>> Transform<P> for Decomposed<P::Vector, R> where
} }
} }
fn invert(&self) -> Option<Decomposed<P::Vector, R>> { fn invert(&self) -> Option<Decomposed<P::Diff, R>> {
if self.scale.approx_eq(&<P as Point>::Scalar::zero()) { if self.scale.approx_eq(&<P as EuclideanSpace>::Scalar::zero()) {
None None
} else { } else {
let s = <P as Point>::Scalar::one() / self.scale; let s = <P as EuclideanSpace>::Scalar::one() / self.scale;
let r = self.rot.invert(); let r = self.rot.invert();
let d = r.rotate_vector(self.disp.clone()) * -s; let d = r.rotate_vector(self.disp.clone()) * -s;
Some(Decomposed { Some(Decomposed {

View file

@ -13,80 +13,6 @@
// 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.
//! Types and traits for two, three, and four-dimensional vectors.
//!
//! ## Working with Vectors
//!
//! Vectors can be created in several different ways. There is, of course, the
//! traditional `new()` method, but unit vectors, zero vectors, and an one
//! vector are also provided:
//!
//! ```rust
//! use cgmath::{Vector, Vector2, Vector3, Vector4, vec3};
//!
//! assert_eq!(Vector2::new(1.0f64, 0.0f64), Vector2::unit_x());
//! assert_eq!(vec3(0.0f64, 0.0f64, 0.0f64), Vector3::zero());
//! ```
//!
//! Vectors can be manipulated with typical mathematical operations (addition,
//! subtraction, element-wise multiplication, element-wise division, negation)
//! using the built-in operators.
//!
//! ```rust
//! use cgmath::{Vector, Vector2, Vector3, Vector4};
//!
//! let a: Vector2<f64> = Vector2::new(3.0, 4.0);
//! let b: Vector2<f64> = Vector2::new(-3.0, -4.0);
//!
//! assert_eq!(a + b, Vector2::zero());
//! assert_eq!(-(a * 2.0), Vector2::new(-6.0, -8.0));
//!
//! // 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:
//! // let c = a + Vector3::new(1.0, 0.0, 2.0);
//!
//! // Instead, we need to convert the Vector2 to a Vector3 by "extending" it
//! // with the value for the last coordinate:
//! let c: Vector3<f64> = a.extend(0.0) + Vector3::new(1.0, 0.0, 2.0);
//!
//! // Similarly, we can "truncate" a Vector4 down to a Vector3:
//! let d: Vector3<f64> = c + Vector4::unit_x().truncate();
//!
//! assert_eq!(d, Vector3::new(5.0f64, 4.0f64, 2.0f64));
//! ```
//!
//! Vectors also provide methods for typical operations such as
//! [scalar multiplication](http://en.wikipedia.org/wiki/Scalar_multiplication),
//! [dot products](http://en.wikipedia.org/wiki/Dot_product),
//! and [cross products](http://en.wikipedia.org/wiki/Cross_product).
//!
//! ```rust
//! use cgmath::{Vector, EuclideanVector};
//! use cgmath::{Vector2, Vector3, Vector4};
//!
//! // 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), 0.0);
//!
//! // But there is also a top-level function:
//! assert_eq!(a.dot(b), cgmath::dot(a, b));
//!
//! // Cross products are defined for 3-dimensional vectors:
//! let e: Vector3<f64> = Vector3::unit_x();
//! let f: Vector3<f64> = Vector3::unit_y();
//! assert_eq!(e.cross(f), Vector3::unit_z());
//! ```
//!
//! Several other useful methods are provided as well. Vector fields can be
//! accessed using array syntax (i.e. `vector[0] == vector.x`), or by using
//! the methods provided by the [`Array`](../array/trait.Array.html) trait.
//! This trait also provides a `map()` method for applying arbitrary functions.
//!
//! The [`Vector`](../trait.Vector.html) trait presents the most general
//! features of the vectors, while [`EuclideanVector`]
//! (../array/trait.EuclideanVector.html) is more specific to Euclidean space.
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::ops::*; use std::ops::*;
@ -104,6 +30,8 @@ use num::{BaseNum, BaseFloat, PartialOrd};
/// together and [multiplied](https://en.wikipedia.org/wiki/Scalar_multiplication) /// together and [multiplied](https://en.wikipedia.org/wiki/Scalar_multiplication)
/// by scalars. /// by scalars.
/// ///
/// Examples include vectors, matrices, and quaternions.
///
/// # Required operators /// # Required operators
/// ///
/// ## Vector addition /// ## Vector addition
@ -143,16 +71,14 @@ use num::{BaseNum, BaseFloat, PartialOrd};
/// let upscaled_translation = translation * scale_factor; /// let upscaled_translation = translation * scale_factor;
/// let downscaled_translation = translation / scale_factor; /// let downscaled_translation = translation / scale_factor;
/// ``` /// ```
pub trait Vector: Copy + Clone where pub trait VectorSpace: Copy + Clone where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Array<Element = <Self as Vector>::Scalar>,
Self: Add<Self, Output = Self>, Self: Add<Self, Output = Self>,
Self: Sub<Self, Output = Self>, Self: Sub<Self, Output = Self>,
Self: Mul<<Self as Vector>::Scalar, Output = Self>, // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Div<<Self as Vector>::Scalar, Output = Self>, Self: Mul<<Self as VectorSpace>::Scalar, Output = Self>,
Self: Rem<<Self as Vector>::Scalar, Output = Self>, Self: Div<<Self as VectorSpace>::Scalar, Output = Self>,
Self: Rem<<Self as VectorSpace>::Scalar, Output = Self>,
{ {
/// The associated scalar. /// The associated scalar.
type Scalar: BaseNum; type Scalar: BaseNum;
@ -267,7 +193,7 @@ macro_rules! impl_vector {
} }
} }
impl<S: BaseNum> Vector for $VectorN<S> { impl<S: BaseNum> VectorSpace for $VectorN<S> {
type Scalar = S; type Scalar = S;
#[inline] #[inline]
@ -530,10 +456,12 @@ impl<S: BaseNum> Vector4<S> {
/// ///
/// The dot product allows for the definition of other useful operations, like /// The dot product allows for the definition of other useful operations, like
/// finding the magnitude of a vector or normalizing it. /// finding the magnitude of a vector or normalizing it.
pub trait EuclideanVector: Vector + Sized where ///
/// Examples include vectors and quaternions.
pub trait InnerSpace: VectorSpace + Sized where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
<Self as Vector>::Scalar: BaseFloat, <Self as VectorSpace>::Scalar: BaseFloat,
Self: ApproxEq<Epsilon = <Self as Vector>::Scalar>, Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
{ {
/// Vector dot (or inner) product. /// Vector dot (or inner) product.
fn dot(self, other: Self) -> Self::Scalar; fn dot(self, other: Self) -> Self::Scalar;
@ -593,13 +521,13 @@ pub trait EuclideanVector: Vector + Sized where
/// Dot product of two vectors. /// Dot product of two vectors.
#[inline] #[inline]
pub fn dot<V: EuclideanVector>(a: V, b: V) -> V::Scalar where pub fn dot<V: InnerSpace>(a: V, b: V) -> V::Scalar where
V::Scalar: BaseFloat, V::Scalar: BaseFloat,
{ {
V::dot(a, b) V::dot(a, b)
} }
impl<S: BaseFloat> EuclideanVector for Vector2<S> { impl<S: BaseFloat> InnerSpace for Vector2<S> {
#[inline] #[inline]
fn dot(self, other: Vector2<S>) -> S { fn dot(self, other: Vector2<S>) -> S {
Vector2::mul_element_wise(self, other).sum() Vector2::mul_element_wise(self, other).sum()
@ -611,7 +539,7 @@ impl<S: BaseFloat> EuclideanVector for Vector2<S> {
} }
} }
impl<S: BaseFloat> EuclideanVector for Vector3<S> { impl<S: BaseFloat> InnerSpace for Vector3<S> {
#[inline] #[inline]
fn dot(self, other: Vector3<S>) -> S { fn dot(self, other: Vector3<S>) -> S {
Vector3::mul_element_wise(self, other).sum() Vector3::mul_element_wise(self, other).sum()
@ -623,7 +551,7 @@ impl<S: BaseFloat> EuclideanVector for Vector3<S> {
} }
} }
impl<S: BaseFloat> EuclideanVector for Vector4<S> { impl<S: BaseFloat> InnerSpace for Vector4<S> {
#[inline] #[inline]
fn dot(self, other: Vector4<S>) -> S { fn dot(self, other: Vector4<S>) -> S {
Vector4::mul_element_wise(self, other).sum() Vector4::mul_element_wise(self, other).sum()