Unify trait hierarchies for vectors, quaternions and matrices

This commit is contained in:
Brendan Zabarauskas 2016-04-09 13:47:04 +10:00
parent b4063ea72a
commit 886e2fb240
4 changed files with 105 additions and 97 deletions

View file

@ -248,41 +248,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 +347,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 +370,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 +384,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 +407,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 +424,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 +452,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 +520,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 +550,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 +632,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 +664,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],

View file

@ -25,7 +25,6 @@ use rust_num::{One, Zero};
use approx::ApproxEq;
use array::Array;
use matrix::Matrix;
use num::{BaseNum, BaseFloat};
use vector::*;

View file

@ -54,62 +54,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 +112,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();

View file

@ -104,6 +104,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
@ -144,12 +146,10 @@ use num::{BaseNum, BaseFloat, PartialOrd};
/// let downscaled_translation = translation / scale_factor;
/// ```
pub trait VectorSpace: Copy + Clone where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
Self: Array<Element = <Self as VectorSpace>::Scalar>,
Self: Add<Self, Output = Self>,
Self: Sub<Self, 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>,
@ -530,6 +530,8 @@ 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.
///
/// Examples include vectors and quaternions.
pub trait InnerSpace: VectorSpace + Sized where
// FIXME: Ugly type signatures - blocked by rust-lang/rust#24092
<Self as VectorSpace>::Scalar: BaseFloat,