From 886e2fb240036b21fbeb6ffe62faf721325bb72e Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 9 Apr 2016 13:47:04 +1000 Subject: [PATCH] Unify trait hierarchies for vectors, quaternions and matrices --- src/matrix.rs | 128 +++++++++++++++++++++++++++------------------- src/point.rs | 1 - src/quaternion.rs | 65 ++++++++++------------- src/vector.rs | 8 +-- 4 files changed, 105 insertions(+), 97 deletions(-) diff --git a/src/matrix.rs b/src/matrix.rs index 032dfc6..99c693f 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -248,41 +248,86 @@ impl Matrix4 { } } +impl VectorSpace for Matrix2 { + type Scalar = S; + + #[inline] + fn zero() -> Matrix2 { + Matrix2::new(S::zero(), S::zero(), + S::zero(), S::zero()) + } +} + +impl VectorSpace for Matrix3 { + type Scalar = S; + + #[inline] + fn zero() -> Matrix3 { + Matrix3::new(S::zero(), S::zero(), S::zero(), + S::zero(), S::zero(), S::zero(), + S::zero(), S::zero(), S::zero()) + } +} + +impl VectorSpace for Matrix4 { + type Scalar = S; + + #[inline] + fn zero() -> Matrix4 { + 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` +/// - `Sub` +/// - `Neg` +/// +/// Scalar multiplication: +/// +/// - `Mul` +/// - `Div` +/// - `Rem` +/// +/// 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::Column>, Self: IndexMut::Column>, - Self: ApproxEq::Element>, - - Self: Add, - Self: Sub, - Self: Neg, - - Self: Mul<::Element, Output = Self>, - Self: Div<::Element, Output = Self>, - Self: Rem<::Element, Output = Self>, + Self: ApproxEq::Scalar>, { - /// The type of the elements in the matrix. - type Element: BaseFloat; - /// The row vector of the matrix. - type Row: Array; - /// The column vector of the matrix. - type Column: Array; + type Row: VectorSpace + Array; - /// The type of the transposed matrix - type Transpose: Matrix; + /// The column vector of the matrix. + type Column: VectorSpace + Array; + + /// The result of transposing the matrix + type Transpose: Matrix; /// 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 = ::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; + type ColumnRow: VectorSpace + Array; /// 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 Matrix for Matrix2 { - type Element = S; type Column = Vector2; type Row = Vector2; type Transpose = Matrix2; @@ -409,12 +452,6 @@ impl Matrix for Matrix2 { unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } - #[inline] - fn zero() -> Matrix2 { - Matrix2::new(S::zero(), S::zero(), - S::zero(), S::zero()) - } - fn transpose(&self) -> Matrix2 { Matrix2::new(self[0][0], self[1][0], self[0][1], self[1][1]) @@ -483,7 +520,6 @@ impl SquareMatrix for Matrix2 { } impl Matrix for Matrix3 { - type Element = S; type Column = Vector3; type Row = Vector3; type Transpose = Matrix3; @@ -514,13 +550,6 @@ impl Matrix for Matrix3 { unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } - #[inline] - fn zero() -> Matrix3 { - Matrix3::new(S::zero(), S::zero(), S::zero(), - S::zero(), S::zero(), S::zero(), - S::zero(), S::zero(), S::zero()) - } - fn transpose(&self) -> Matrix3 { 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 SquareMatrix for Matrix3 { } impl Matrix for Matrix4 { - type Element = S; type Column = Vector4; type Row = Vector4; type Transpose = Matrix4; @@ -636,14 +664,6 @@ impl Matrix for Matrix4 { unsafe { ptr::swap(&mut self[ac][ar], &mut self[bc][br]) }; } - #[inline] - fn zero() -> Matrix4 { - 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 { 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], diff --git a/src/point.rs b/src/point.rs index 40eb355..4a5b6b4 100644 --- a/src/point.rs +++ b/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::*; diff --git a/src/quaternion.rs b/src/quaternion.rs index 9a04f92..ea8d8e3 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -54,62 +54,40 @@ impl Quaternion { Quaternion { s: s, v: v } } - /// The additive identity, ie: `q = 0 + 0i + 0j + 0i` - #[inline] - pub fn zero() -> Quaternion { - 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 { Quaternion::from_sv(S::one(), Vector3::zero()) } - /// The dot product of the quaternion and `q`. - #[inline] - pub fn dot(self, other: Quaternion) -> S { - self.s * other.s + self.v.dot(other.v) - } - /// The conjugate of the quaternion. #[inline] pub fn conjugate(self) -> Quaternion { 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 { - self * (S::one() / self.magnitude()) - } - /// Do a normalized linear interpolation with `other`, by `amount`. pub fn nlerp(self, other: Quaternion, amount: S) -> Quaternion { (self * (S::one() - amount) + other * amount).normalize() } } +impl VectorSpace for Quaternion { + type Scalar = S; + + #[inline] + fn zero() -> Quaternion { + Quaternion::from_sv(S::zero(), Vector3::zero()) + } +} + +impl InnerSpace for Quaternion { + #[inline] + fn dot(self, other: Quaternion) -> S { + self.s * other.s + self.v.dot(other.v) + } +} + impl_operator!( Neg for Quaternion { fn neg(quat) -> Quaternion { Quaternion::from_sv(-quat.s, -quat.v) @@ -134,6 +112,15 @@ impl_assignment_operator!( DivAssign for Quaternion { fn div_assign(&mut self, scalar) { self.s /= scalar; self.v /= scalar; } }); +impl_operator!( Rem for Quaternion { + fn rem(lhs, rhs) -> Quaternion { + Quaternion::from_sv(lhs.s % rhs, lhs.v % rhs) + } +}); +impl_assignment_operator!( RemAssign for Quaternion { + fn rem_assign(&mut self, scalar) { self.s %= scalar; self.v %= scalar; } +}); + impl_operator!( Mul > for Quaternion { fn mul(lhs, rhs) -> Vector3 {{ let rhs = rhs.clone(); diff --git a/src/vector.rs b/src/vector.rs index ffbeff6..57bb110 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -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::Scalar>, - Self: Add, Self: Sub, + // FIXME: Ugly type signatures - blocked by rust-lang/rust#24092 Self: Mul<::Scalar, Output = Self>, Self: Div<::Scalar, Output = Self>, Self: Rem<::Scalar, Output = Self>, @@ -530,6 +530,8 @@ impl Vector4 { /// /// 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 ::Scalar: BaseFloat,