use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::any::Any; use std::fmt::Debug; use std::marker::PhantomData; #[cfg(feature = "serde-serialize")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use alga::general::Real; use base::allocator::Allocator; use base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use base::storage::Owned; use base::{DefaultAllocator, MatrixN}; /// Trait implemented by phantom types identifying the projective transformation type. /// /// NOTE: this trait is not intended to be implemented outside of the `nalgebra` crate. pub trait TCategory: Any + Debug + Copy + PartialEq + Send { /// Indicates whether a `Transform` with the category `Self` has a bottom-row different from /// `0 0 .. 1`. #[inline] fn has_normalizer() -> bool { true } /// Checks that the given matrix is a valid homogeneous representation of an element of the /// category `Self`. fn check_homogeneous_invariants(mat: &MatrixN) -> bool where N::Epsilon: Copy, DefaultAllocator: Allocator; } /// Traits that gives the `Transform` category that is compatible with the result of the /// multiplication of transformations with categories `Self` and `Other`. pub trait TCategoryMul: TCategory { /// The transform category that results from the multiplication of a `Transform` to a /// `Transform`. This is usually equal to `Self` or `Other`, whichever is the most /// general category. type Representative: TCategory; } /// Indicates that `Self` is a more general `Transform` category than `Other`. pub trait SuperTCategoryOf: TCategory {} /// Indicates that `Self` is a more specific `Transform` category than `Other`. /// /// Automatically implemented based on `SuperTCategoryOf`. pub trait SubTCategoryOf: TCategory {} impl SubTCategoryOf for T1 where T1: TCategory, T2: SuperTCategoryOf, { } /// Tag representing the most general (not necessarily inversible) `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum TGeneral {} /// Tag representing the most general inversible `Transform` type. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum TProjective {} /// Tag representing an affine `Transform`. Its bottom-row is equal to `(0, 0 ... 0, 1)`. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum TAffine {} impl TCategory for TGeneral { #[inline] fn check_homogeneous_invariants(_: &MatrixN) -> bool where N::Epsilon: Copy, DefaultAllocator: Allocator, { true } } impl TCategory for TProjective { #[inline] fn check_homogeneous_invariants(mat: &MatrixN) -> bool where N::Epsilon: Copy, DefaultAllocator: Allocator, { mat.is_invertible() } } impl TCategory for TAffine { #[inline] fn has_normalizer() -> bool { false } #[inline] fn check_homogeneous_invariants(mat: &MatrixN) -> bool where N::Epsilon: Copy, DefaultAllocator: Allocator, { let last = D::dim() - 1; mat.is_invertible() && mat[(last, last)] == N::one() && (0..last).all(|i| mat[(last, i)].is_zero()) } } macro_rules! category_mul_impl( ($($a: ident * $b: ident => $c: ty);* $(;)*) => {$( impl TCategoryMul<$a> for $b { type Representative = $c; } )*} ); // We require stability uppon multiplication. impl TCategoryMul for T { type Representative = T; } category_mul_impl!( // TGeneral * TGeneral => TGeneral; TGeneral * TProjective => TGeneral; TGeneral * TAffine => TGeneral; TProjective * TGeneral => TGeneral; // TProjective * TProjective => TProjective; TProjective * TAffine => TProjective; TAffine * TGeneral => TGeneral; TAffine * TProjective => TProjective; // TAffine * TAffine => TAffine; ); macro_rules! super_tcategory_impl( ($($a: ident >= $b: ident);* $(;)*) => {$( impl SuperTCategoryOf<$b> for $a { } )*} ); impl SuperTCategoryOf for T {} super_tcategory_impl!( TGeneral >= TProjective; TGeneral >= TAffine; TProjective >= TAffine; ); /// A transformation matrix in homogeneous coordinates. /// /// It is stored as a matrix with dimensions `(D + 1, D + 1)`, e.g., it stores a 4x4 matrix for a /// 3D transformation. #[repr(C)] #[derive(Debug)] pub struct Transform, C: TCategory> where DefaultAllocator: Allocator, DimNameSum> { matrix: MatrixN>, _phantom: PhantomData, } // FIXME // impl + hash::Hash, C: TCategory> hash::Hash for Transform // where DefaultAllocator: Allocator, DimNameSum>, // Owned, DimNameSum>: hash::Hash { // fn hash(&self, state: &mut H) { // self.matrix.hash(state); // } // } impl + Copy, C: TCategory> Copy for Transform where DefaultAllocator: Allocator, DimNameSum>, Owned, DimNameSum>: Copy, { } impl, C: TCategory> Clone for Transform where DefaultAllocator: Allocator, DimNameSum> { #[inline] fn clone(&self) -> Self { Transform::from_matrix_unchecked(self.matrix.clone()) } } #[cfg(feature = "serde-serialize")] impl, C: TCategory> Serialize for Transform where DefaultAllocator: Allocator, DimNameSum>, Owned, DimNameSum>: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer { self.matrix.serialize(serializer) } } #[cfg(feature = "serde-serialize")] impl<'a, N: Real, D: DimNameAdd, C: TCategory> Deserialize<'a> for Transform where DefaultAllocator: Allocator, DimNameSum>, Owned, DimNameSum>: Deserialize<'a>, { fn deserialize(deserializer: Des) -> Result where Des: Deserializer<'a> { let matrix = MatrixN::>::deserialize(deserializer)?; Ok(Transform::from_matrix_unchecked(matrix)) } } impl, C: TCategory> Eq for Transform where DefaultAllocator: Allocator, DimNameSum> {} impl, C: TCategory> PartialEq for Transform where DefaultAllocator: Allocator, DimNameSum> { #[inline] fn eq(&self, right: &Self) -> bool { self.matrix == right.matrix } } impl, C: TCategory> Transform where DefaultAllocator: Allocator, DimNameSum> { /// Creates a new transformation from the given homogeneous matrix. The transformation category /// of `Self` is not checked to be verified by the given matrix. #[inline] pub fn from_matrix_unchecked(matrix: MatrixN>) -> Self { Transform { matrix: matrix, _phantom: PhantomData, } } /// Retrieves the underlying matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert_eq!(t.unwrap(), m); /// ``` #[inline] pub fn unwrap(self) -> MatrixN> { self.matrix } /// A reference to the underlying matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert_eq!(*t.matrix(), m); /// ``` #[inline] pub fn matrix(&self) -> &MatrixN> { &self.matrix } /// A mutable reference to the underlying matrix. /// /// It is `_unchecked` because direct modifications of this matrix may break invariants /// identified by this transformation category. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let mut t = Transform2::from_matrix_unchecked(m); /// t.matrix_mut_unchecked().m12 = 42.0; /// t.matrix_mut_unchecked().m23 = 90.0; /// /// /// let expected = Matrix3::new(1.0, 42.0, 0.0, /// 3.0, 4.0, 90.0, /// 0.0, 0.0, 1.0); /// assert_eq!(*t.matrix(), expected); /// ``` #[inline] pub fn matrix_mut_unchecked(&mut self) -> &mut MatrixN> { &mut self.matrix } /// Sets the category of this transform. /// /// This can be done only if the new category is more general than the current one, e.g., a /// transform with category `TProjective` cannot be converted to a transform with category /// `TAffine` because not all projective transformations are affine (the other way-round is /// valid though). #[inline] pub fn set_category>(self) -> Transform { Transform::from_matrix_unchecked(self.matrix) } /// Clones this transform into one that owns its data. #[inline] #[deprecated( note = "This method is redundant with automatic `Copy` and the `.clone()` method and will be removed in a future release." )] pub fn clone_owned(&self) -> Transform { Transform::from_matrix_unchecked(self.matrix.clone_owned()) } /// Converts this transform into its equivalent homogeneous transformation matrix. /// /// # Examples /// ``` /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(1.0, 2.0, 0.0, /// 3.0, 4.0, 0.0, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert_eq!(t.unwrap(), m); /// ``` #[inline] pub fn to_homogeneous(&self) -> MatrixN> { self.matrix().clone_owned() } /// Attempts to invert this transformation. You may use `.inverse` instead of this /// transformation has a subcategory of `TProjective` (i.e. if it is a `Projective{2,3}` or `Affine{2,3}`). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// let inv_t = t.try_inverse().unwrap(); /// assert_relative_eq!(t * inv_t, Transform2::identity()); /// assert_relative_eq!(inv_t * t, Transform2::identity()); /// /// // Non-invertible case. /// let m = Matrix3::new(0.0, 2.0, 1.0, /// 3.0, 0.0, 5.0, /// 0.0, 0.0, 0.0); /// let t = Transform2::from_matrix_unchecked(m); /// assert!(t.try_inverse().is_none()); /// ``` #[inline] pub fn try_inverse(self) -> Option> { if let Some(m) = self.matrix.try_inverse() { Some(Transform::from_matrix_unchecked(m)) } else { None } } /// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral` /// category (i.e., a `Transform{2,3}` may not be invertible). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let proj = Projective2::from_matrix_unchecked(m); /// let inv_t = proj.inverse(); /// assert_relative_eq!(proj * inv_t, Projective2::identity()); /// assert_relative_eq!(inv_t * proj, Projective2::identity()); /// ``` #[inline] pub fn inverse(self) -> Transform where C: SubTCategoryOf { // FIXME: specialize for TAffine? Transform::from_matrix_unchecked(self.matrix.try_inverse().unwrap()) } /// Attempts to invert this transformation in-place. You may use `.inverse_mut` instead of this /// transformation has a subcategory of `TProjective`. /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Transform2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let t = Transform2::from_matrix_unchecked(m); /// let mut inv_t = t; /// assert!(inv_t.try_inverse_mut()); /// assert_relative_eq!(t * inv_t, Transform2::identity()); /// assert_relative_eq!(inv_t * t, Transform2::identity()); /// /// // Non-invertible case. /// let m = Matrix3::new(0.0, 2.0, 1.0, /// 3.0, 0.0, 5.0, /// 0.0, 0.0, 0.0); /// let mut t = Transform2::from_matrix_unchecked(m); /// assert!(!t.try_inverse_mut()); /// ``` #[inline] pub fn try_inverse_mut(&mut self) -> bool { self.matrix.try_inverse_mut() } /// Inverts this transformation in-place. Use `.try_inverse_mut` if this transform has the /// `TGeneral` category (it may not be invertible). /// /// # Examples /// ``` /// # #[macro_use] extern crate approx; /// # extern crate nalgebra; /// # use nalgebra::{Matrix3, Projective2}; /// /// let m = Matrix3::new(2.0, 2.0, -0.3, /// 3.0, 4.0, 0.1, /// 0.0, 0.0, 1.0); /// let proj = Projective2::from_matrix_unchecked(m); /// let mut inv_t = proj; /// inv_t.inverse_mut(); /// assert_relative_eq!(proj * inv_t, Projective2::identity()); /// assert_relative_eq!(inv_t * proj, Projective2::identity()); /// ``` #[inline] pub fn inverse_mut(&mut self) where C: SubTCategoryOf { let _ = self.matrix.try_inverse_mut(); } } impl> Transform where DefaultAllocator: Allocator, DimNameSum> { /// A mutable reference to underlying matrix. Use `.matrix_mut_unchecked` instead if this /// transformation category is not `TGeneral`. #[inline] pub fn matrix_mut(&mut self) -> &mut MatrixN> { self.matrix_mut_unchecked() } } impl, C: TCategory> AbsDiffEq for Transform where N::Epsilon: Copy, DefaultAllocator: Allocator, DimNameSum>, { type Epsilon = N::Epsilon; #[inline] fn default_epsilon() -> Self::Epsilon { N::default_epsilon() } #[inline] fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { self.matrix.abs_diff_eq(&other.matrix, epsilon) } } impl, C: TCategory> RelativeEq for Transform where N::Epsilon: Copy, DefaultAllocator: Allocator, DimNameSum>, { #[inline] fn default_max_relative() -> Self::Epsilon { N::default_max_relative() } #[inline] fn relative_eq( &self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon, ) -> bool { self.matrix .relative_eq(&other.matrix, epsilon, max_relative) } } impl, C: TCategory> UlpsEq for Transform where N::Epsilon: Copy, DefaultAllocator: Allocator, DimNameSum>, { #[inline] fn default_max_ulps() -> u32 { N::default_max_ulps() } #[inline] fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { self.matrix.ulps_eq(&other.matrix, epsilon, max_ulps) } } #[cfg(test)] mod tests { use super::*; use base::Matrix4; #[test] fn checks_homogeneous_invariants_of_square_identity_matrix() { assert!(TAffine::check_homogeneous_invariants( &Matrix4::::identity() )); } }