Merge pull request #320 from bjz/the-grand-trait-unification
Unify traits into an algebraic heirachy
This commit is contained in:
commit
3cc33c4606
8 changed files with 213 additions and 248 deletions
44
src/lib.rs
44
src/lib.rs
|
@ -13,19 +13,41 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// 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.
|
||||
//! 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.
|
||||
//! # Trait overview
|
||||
//!
|
||||
//! Transformations are not usually done directly on matrices, but go through
|
||||
//! transformation objects that can be converted to matrices. Rotations go
|
||||
//! through the `Basis` types, which are guaranteed to be orthogonal matrices.
|
||||
//! Despite this, one can directly create a limited rotation matrix using the
|
||||
//! `look_at`, `from_angle`, `from_euler`, and `from_axis_angle` methods.
|
||||
//! These are provided for convenience.
|
||||
//! In order to make a clean, composable API, we divide operations into traits
|
||||
//! that are roughly based on mathematical properties. The main ones that we
|
||||
//! concern ourselves with are listed below:
|
||||
//!
|
||||
//! - `VectorSpace`: Specifies the main operators for vectors, quaternions, and
|
||||
//! 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 rustc_serialize;
|
||||
|
|
143
src/matrix.rs
143
src/matrix.rs
|
@ -13,8 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Column major, square matrix types and traits.
|
||||
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::*;
|
||||
|
@ -29,9 +27,9 @@ use angle::{Angle, Rad};
|
|||
use approx::ApproxEq;
|
||||
use array::Array;
|
||||
use num::BaseFloat;
|
||||
use point::{Point, Point3};
|
||||
use point::{EuclideanSpace, Point3};
|
||||
use quaternion::Quaternion;
|
||||
use vector::{Vector, EuclideanVector};
|
||||
use vector::{VectorSpace, InnerSpace};
|
||||
use vector::{Vector2, Vector3, Vector4};
|
||||
|
||||
/// A 2 x 2, column major matrix
|
||||
|
@ -40,7 +38,9 @@ use vector::{Vector2, Vector3, Vector4};
|
|||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub struct Matrix2<S> {
|
||||
/// The first column of the matrix.
|
||||
pub x: Vector2<S>,
|
||||
/// The second column of the matrix.
|
||||
pub y: Vector2<S>,
|
||||
}
|
||||
|
||||
|
@ -50,8 +50,11 @@ pub struct Matrix2<S> {
|
|||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub struct Matrix3<S> {
|
||||
/// The first column of the matrix.
|
||||
pub x: Vector3<S>,
|
||||
/// The second column of the matrix.
|
||||
pub y: Vector3<S>,
|
||||
/// The third column of the matrix.
|
||||
pub z: Vector3<S>,
|
||||
}
|
||||
|
||||
|
@ -61,9 +64,13 @@ pub struct Matrix3<S> {
|
|||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub struct Matrix4<S> {
|
||||
/// The first column of the matrix.
|
||||
pub x: Vector4<S>,
|
||||
/// The second column of the matrix.
|
||||
pub y: Vector4<S>,
|
||||
/// The third column of the matrix.
|
||||
pub z: Vector4<S>,
|
||||
/// The fourth column of the matrix.
|
||||
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.
|
||||
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
|
||||
Self: Index<usize, Output = <Self as Matrix>::Column>,
|
||||
Self: IndexMut<usize, Output = <Self as Matrix>::Column>,
|
||||
Self: ApproxEq<Epsilon = <Self as Matrix>::Element>,
|
||||
|
||||
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>,
|
||||
Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
{
|
||||
/// The type of the elements in the matrix.
|
||||
type Element: BaseFloat;
|
||||
|
||||
/// The row vector of the matrix.
|
||||
type Row: Array<Element = Self::Element>;
|
||||
/// The column vector of the matrix.
|
||||
type Column: Array<Element = Self::Element>;
|
||||
type Row: VectorSpace<Scalar = Self::Scalar> + Array<Element = Self::Scalar>;
|
||||
|
||||
/// The type of the transposed matrix
|
||||
type Transpose: Matrix<Element = Self::Element, Row = Self::Column, Column = Self::Row>;
|
||||
/// The column vector of the matrix.
|
||||
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.
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *const Self::Element {
|
||||
fn as_ptr(&self) -> *const Self::Scalar {
|
||||
&self[0][0]
|
||||
}
|
||||
|
||||
/// Get a mutable pointer to the first element of the array.
|
||||
#[inline]
|
||||
fn as_mut_ptr(&mut self) -> *mut Self::Element {
|
||||
fn as_mut_ptr(&mut self) -> *mut Self::Scalar {
|
||||
&mut self[0][0]
|
||||
}
|
||||
|
||||
|
@ -302,15 +354,14 @@ pub trait Matrix where
|
|||
/// Swap the values at index `a` and `b`
|
||||
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.
|
||||
fn transpose(&self) -> Self::Transpose;
|
||||
}
|
||||
|
||||
/// A column-major major matrix where the rows and column vectors are of the same dimensions.
|
||||
pub trait SquareMatrix where
|
||||
Self::Scalar: BaseFloat,
|
||||
|
||||
Self: Matrix<
|
||||
// FIXME: Can be cleaned up once equality constraints in where clauses are implemented
|
||||
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
|
||||
/// constraints being implemented for `where` clauses. Once those are added, this type will
|
||||
/// 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.
|
||||
fn from_value(value: Self::Element) -> Self;
|
||||
fn from_value(value: Self::Scalar) -> Self;
|
||||
/// 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
|
||||
/// matrix with another has no effect.
|
||||
|
@ -340,14 +391,14 @@ pub trait SquareMatrix where
|
|||
/// Transpose this matrix in-place.
|
||||
fn transpose_self(&mut self);
|
||||
/// 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.
|
||||
fn diagonal(&self) -> Self::Column;
|
||||
fn diagonal(&self) -> Self::ColumnRow;
|
||||
|
||||
/// Return the trace of this matrix. That is, the sum of the diagonal.
|
||||
#[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
|
||||
/// 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.
|
||||
#[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
|
||||
/// and every element in the diagonal is one.
|
||||
|
@ -380,7 +431,6 @@ pub trait SquareMatrix where
|
|||
}
|
||||
|
||||
impl<S: BaseFloat> Matrix for Matrix2<S> {
|
||||
type Element = S;
|
||||
type Column = Vector2<S>;
|
||||
type Row = Vector2<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]) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn zero() -> Matrix2<S> {
|
||||
Matrix2::new(S::zero(), S::zero(),
|
||||
S::zero(), S::zero())
|
||||
}
|
||||
|
||||
fn transpose(&self) -> Matrix2<S> {
|
||||
Matrix2::new(self[0][0], self[1][0],
|
||||
self[0][1], self[1][1])
|
||||
|
@ -483,7 +527,6 @@ impl<S: BaseFloat> SquareMatrix for Matrix2<S> {
|
|||
}
|
||||
|
||||
impl<S: BaseFloat> Matrix for Matrix3<S> {
|
||||
type Element = S;
|
||||
type Column = Vector3<S>;
|
||||
type Row = Vector3<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]) };
|
||||
}
|
||||
|
||||
#[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> {
|
||||
Matrix3::new(self[0][0], self[1][0], self[2][0],
|
||||
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> {
|
||||
type Element = S;
|
||||
type Column = Vector4<S>;
|
||||
type Row = Vector4<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]) };
|
||||
}
|
||||
|
||||
#[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> {
|
||||
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],
|
||||
|
|
41
src/point.rs
41
src/point.rs
|
@ -25,7 +25,6 @@ use rust_num::{One, Zero};
|
|||
|
||||
use approx::ApproxEq;
|
||||
use array::Array;
|
||||
use matrix::Matrix;
|
||||
use num::{BaseNum, BaseFloat};
|
||||
use vector::*;
|
||||
|
||||
|
@ -79,7 +78,7 @@ impl<S: BaseNum> Point3<S> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
|
@ -116,34 +115,34 @@ impl<S: BaseNum> Point3<S> {
|
|||
/// ## Converting between points and vectors
|
||||
///
|
||||
/// Points can be converted to and from displacement vectors using the
|
||||
/// `Point::{from_vec, to_vec}` methods. Note that under the hood these are
|
||||
/// implemented as inlined a type conversion, so should not have any performance
|
||||
/// implications.
|
||||
/// `EuclideanSpace::{from_vec, to_vec}` methods. Note that under the hood these
|
||||
/// are implemented as inlined a type conversion, so should not have any
|
||||
/// performance implications.
|
||||
///
|
||||
/// ## 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)
|
||||
/// - [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
|
||||
Self: Array<Element = <Self as Point>::Scalar>,
|
||||
Self: Array<Element = <Self as EuclideanSpace>::Scalar>,
|
||||
|
||||
Self: Add<<Self as Point>::Vector, Output = Self>,
|
||||
Self: Sub<Self, Output = <Self as Point>::Vector>,
|
||||
Self: Add<<Self as EuclideanSpace>::Diff, Output = Self>,
|
||||
Self: Sub<Self, Output = <Self as EuclideanSpace>::Diff>,
|
||||
|
||||
Self: Mul<<Self as Point>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as Point>::Scalar, Output = Self>,
|
||||
Self: Rem<<Self as Point>::Scalar, Output = Self>,
|
||||
Self: Mul<<Self as EuclideanSpace>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as EuclideanSpace>::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
|
||||
/// alias to `Self::Vector::Scalar`.
|
||||
/// Due to the equality constraints demanded by `Self::Diff`, this is effectively just an
|
||||
/// alias to `Self::Diff::Scalar`.
|
||||
type Scalar: BaseNum;
|
||||
|
||||
/// 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.
|
||||
fn origin() -> Self;
|
||||
|
@ -152,16 +151,16 @@ pub trait Point: Copy + Clone where
|
|||
///
|
||||
/// This can be considered equivalent to the addition of the displacement
|
||||
/// 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.
|
||||
///
|
||||
/// This can be seen as equivalent to the displacement vector from
|
||||
/// `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.
|
||||
fn dot(self, v: Self::Vector) -> Self::Scalar;
|
||||
fn dot(self, v: Self::Diff) -> Self::Scalar;
|
||||
}
|
||||
|
||||
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 Vector = $VectorN<S>;
|
||||
type Diff = $VectorN<S>;
|
||||
|
||||
#[inline]
|
||||
fn origin() -> $PointN<S> {
|
||||
|
|
|
@ -10,7 +10,7 @@ pub use array::ElementWise;
|
|||
pub use matrix::Matrix;
|
||||
pub use matrix::SquareMatrix;
|
||||
|
||||
pub use point::Point;
|
||||
pub use point::EuclideanSpace;
|
||||
|
||||
pub use rotation::Rotation;
|
||||
pub use rotation::Rotation2;
|
||||
|
@ -20,5 +20,5 @@ pub use transform::Transform;
|
|||
pub use transform::Transform2;
|
||||
pub use transform::Transform3;
|
||||
|
||||
pub use vector::EuclideanVector;
|
||||
pub use vector::Vector;
|
||||
pub use vector::InnerSpace;
|
||||
pub use vector::VectorSpace;
|
||||
|
|
|
@ -26,7 +26,7 @@ use matrix::{Matrix3, Matrix4};
|
|||
use num::BaseFloat;
|
||||
use point::Point3;
|
||||
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
|
||||
|
@ -36,7 +36,9 @@ use vector::{Vector3, Vector, EuclideanVector};
|
|||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
|
||||
pub struct Quaternion<S> {
|
||||
/// The scalar part of the quaternion.
|
||||
pub s: S,
|
||||
/// The vector part of the quaternion.
|
||||
pub v: Vector3<S>,
|
||||
}
|
||||
|
||||
|
@ -54,62 +56,40 @@ impl<S: BaseFloat> Quaternion<S> {
|
|||
Quaternion { s: s, v: v }
|
||||
}
|
||||
|
||||
/// The additive identity, ie: `q = 0 + 0i + 0j + 0i`
|
||||
#[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`
|
||||
/// The multiplicative identity.
|
||||
#[inline]
|
||||
pub fn one() -> Quaternion<S> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn conjugate(self) -> Quaternion<S> {
|
||||
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`.
|
||||
pub fn nlerp(self, other: Quaternion<S>, amount: S) -> Quaternion<S> {
|
||||
(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> {
|
||||
fn neg(quat) -> Quaternion<S> {
|
||||
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; }
|
||||
});
|
||||
|
||||
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> {
|
||||
fn mul(lhs, rhs) -> Vector3<S> {{
|
||||
let rhs = rhs.clone();
|
||||
|
|
|
@ -20,29 +20,29 @@ use approx::ApproxEq;
|
|||
use matrix::SquareMatrix;
|
||||
use matrix::{Matrix2, Matrix3};
|
||||
use num::BaseFloat;
|
||||
use point::{Point, Point2, Point3};
|
||||
use point::{EuclideanSpace, Point2, Point3};
|
||||
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
|
||||
/// 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
|
||||
Self: ApproxEq<Epsilon = <P as Point>::Scalar>,
|
||||
<P as Point>::Scalar: BaseFloat,
|
||||
Self: ApproxEq<Epsilon = <P as EuclideanSpace>::Scalar>,
|
||||
<P as EuclideanSpace>::Scalar: BaseFloat,
|
||||
{
|
||||
/// Create the identity transform (causes no transformation).
|
||||
fn one() -> Self;
|
||||
|
||||
/// 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'.
|
||||
/// 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.
|
||||
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
|
||||
/// representation as a vector.
|
||||
|
|
|
@ -27,24 +27,24 @@ use vector::*;
|
|||
/// A trait representing an [affine
|
||||
/// transformation](https://en.wikipedia.org/wiki/Affine_transformation) that
|
||||
/// 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
|
||||
/// does nothing.
|
||||
fn one() -> Self;
|
||||
|
||||
/// Create a transformation that rotates a vector to look at `center` from
|
||||
/// `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.
|
||||
fn transform_vector(&self, vec: P::Vector) -> P::Vector;
|
||||
fn transform_vector(&self, vec: P::Diff) -> P::Diff;
|
||||
|
||||
/// Transform a point using this transform.
|
||||
fn transform_point(&self, point: P) -> P;
|
||||
|
||||
/// Transform a vector as a point using this transform.
|
||||
#[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()
|
||||
}
|
||||
|
||||
|
@ -72,40 +72,40 @@ pub trait Transform<P: Point>: Sized {
|
|||
/// A generic transformation consisting of a rotation,
|
||||
/// displacement vector and scale amount.
|
||||
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct Decomposed<V: Vector, R> {
|
||||
pub struct Decomposed<V: VectorSpace, R> {
|
||||
pub scale: V::Scalar,
|
||||
pub rot: R,
|
||||
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
|
||||
<P as Point>::Scalar: BaseFloat,
|
||||
<P as EuclideanSpace>::Scalar: BaseFloat,
|
||||
// FIXME: Investigate why this is needed!
|
||||
<P as Point>::Vector: Vector,
|
||||
<P as EuclideanSpace>::Diff: VectorSpace,
|
||||
{
|
||||
#[inline]
|
||||
fn one() -> Decomposed<P::Vector, R> {
|
||||
fn one() -> Decomposed<P::Diff, R> {
|
||||
Decomposed {
|
||||
scale: <P as Point>::Scalar::one(),
|
||||
scale: <P as EuclideanSpace>::Scalar::one(),
|
||||
rot: R::one(),
|
||||
disp: P::Vector::zero(),
|
||||
disp: P::Diff::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[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 disp = rot.rotate_vector(P::origin() - eye);
|
||||
Decomposed {
|
||||
scale: <P as Point>::Scalar::one(),
|
||||
scale: <P as EuclideanSpace>::Scalar::one(),
|
||||
rot: rot,
|
||||
disp: disp,
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
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 {
|
||||
scale: self.scale * other.scale,
|
||||
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>> {
|
||||
if self.scale.approx_eq(&<P as Point>::Scalar::zero()) {
|
||||
fn invert(&self) -> Option<Decomposed<P::Diff, R>> {
|
||||
if self.scale.approx_eq(&<P as EuclideanSpace>::Scalar::zero()) {
|
||||
None
|
||||
} 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 d = r.rotate_vector(self.disp.clone()) * -s;
|
||||
Some(Decomposed {
|
||||
|
|
106
src/vector.rs
106
src/vector.rs
|
@ -13,80 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// 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::mem;
|
||||
use std::ops::*;
|
||||
|
@ -104,6 +30,8 @@ use num::{BaseNum, BaseFloat, PartialOrd};
|
|||
/// together and [multiplied](https://en.wikipedia.org/wiki/Scalar_multiplication)
|
||||
/// by scalars.
|
||||
///
|
||||
/// Examples include vectors, matrices, and quaternions.
|
||||
///
|
||||
/// # Required operators
|
||||
///
|
||||
/// ## Vector addition
|
||||
|
@ -143,16 +71,14 @@ use num::{BaseNum, BaseFloat, PartialOrd};
|
|||
/// let upscaled_translation = translation * scale_factor;
|
||||
/// let downscaled_translation = translation / scale_factor;
|
||||
/// ```
|
||||
pub trait Vector: Copy + Clone where
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: Array<Element = <Self as Vector>::Scalar>,
|
||||
|
||||
pub trait VectorSpace: Copy + Clone where
|
||||
Self: Add<Self, Output = Self>,
|
||||
Self: Sub<Self, Output = Self>,
|
||||
|
||||
Self: Mul<<Self as Vector>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as Vector>::Scalar, Output = Self>,
|
||||
Self: Rem<<Self as Vector>::Scalar, Output = Self>,
|
||||
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
|
||||
Self: Mul<<Self as VectorSpace>::Scalar, Output = Self>,
|
||||
Self: Div<<Self as VectorSpace>::Scalar, Output = Self>,
|
||||
Self: Rem<<Self as VectorSpace>::Scalar, Output = Self>,
|
||||
{
|
||||
/// The associated scalar.
|
||||
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;
|
||||
|
||||
#[inline]
|
||||
|
@ -530,10 +456,12 @@ impl<S: BaseNum> Vector4<S> {
|
|||
///
|
||||
/// The dot product allows for the definition of other useful operations, like
|
||||
/// 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
|
||||
<Self as Vector>::Scalar: BaseFloat,
|
||||
Self: ApproxEq<Epsilon = <Self as Vector>::Scalar>,
|
||||
<Self as VectorSpace>::Scalar: BaseFloat,
|
||||
Self: ApproxEq<Epsilon = <Self as VectorSpace>::Scalar>,
|
||||
{
|
||||
/// Vector dot (or inner) product.
|
||||
fn dot(self, other: Self) -> Self::Scalar;
|
||||
|
@ -593,13 +521,13 @@ pub trait EuclideanVector: Vector + Sized where
|
|||
|
||||
/// Dot product of two vectors.
|
||||
#[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::dot(a, b)
|
||||
}
|
||||
|
||||
impl<S: BaseFloat> EuclideanVector for Vector2<S> {
|
||||
impl<S: BaseFloat> InnerSpace for Vector2<S> {
|
||||
#[inline]
|
||||
fn dot(self, other: Vector2<S>) -> S {
|
||||
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]
|
||||
fn dot(self, other: Vector3<S>) -> S {
|
||||
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]
|
||||
fn dot(self, other: Vector4<S>) -> S {
|
||||
Vector4::mul_element_wise(self, other).sum()
|
||||
|
|
Loading…
Reference in a new issue