Add doc-tests to Transform.

This commit is contained in:
sebcrozet 2018-11-08 07:51:43 +01:00 committed by Sébastien Crozet
parent b6d741c593
commit 69490c2cea
3 changed files with 224 additions and 9 deletions

View File

@ -1,3 +1,4 @@
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use std::any::Any;
use std::fmt::Debug;
use std::marker::PhantomData;
@ -14,7 +15,7 @@ use base::{DefaultAllocator, MatrixN};
/// Trait implemented by phantom types identifying the projective transformation type.
///
/// NOTE: this trait is not intended to be implementable outside of the `nalgebra` crate.
/// 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`.
@ -174,7 +175,8 @@ impl<N: Real, D: DimNameAdd<U1> + Copy, C: TCategory> Copy for Transform<N, D, C
where
DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>,
Owned<N, DimNameSum<D, U1>, DimNameSum<D, U1>>: Copy,
{}
{
}
impl<N: Real, D: DimNameAdd<U1>, C: TCategory> Clone for Transform<N, D, C>
where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
@ -236,13 +238,35 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
}
}
/// The underlying matrix.
/// 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<N, DimNameSum<D, U1>> {
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<N, DimNameSum<D, U1>> {
&self.matrix
@ -252,6 +276,24 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
///
/// 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<N, DimNameSum<D, U1>> {
&mut self.matrix
@ -270,19 +312,54 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// Clones this transform into one that owns its data.
#[inline]
#[deprecated(note = "This method is a no-op and will be removed in a future release.")]
#[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<N, D, C> {
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<N, DimNameSum<D, U1>> {
self.matrix().clone_owned()
}
/// Attempts to invert this transformation. You may use `.inverse` instead of this
/// transformation has a subcategory of `TProjective`.
/// 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<Transform<N, D, C>> {
if let Some(m) = self.matrix.try_inverse() {
@ -293,7 +370,22 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
}
/// Inverts this transformation. Use `.try_inverse` if this transform has the `TGeneral`
/// category (it may not be invertible).
/// 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<N, D, C>
where C: SubTCategoryOf<TProjective> {
@ -303,6 +395,29 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// 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()
@ -310,6 +425,22 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
/// 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<TProjective> {
@ -328,6 +459,63 @@ where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
}
}
impl<N: Real, D: DimNameAdd<U1>, C: TCategory> AbsDiffEq for Transform<N, D, C>
where
N::Epsilon: Copy,
DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>,
{
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<N: Real, D: DimNameAdd<U1>, C: TCategory> RelativeEq for Transform<N, D, C>
where
N::Epsilon: Copy,
DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>,
{
#[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<N: Real, D: DimNameAdd<U1>, C: TCategory> UlpsEq for Transform<N, D, C>
where
N::Epsilon: Copy,
DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>,
{
#[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::*;

View File

@ -2,16 +2,16 @@ use base::dimension::{U2, U3};
use geometry::{TAffine, TGeneral, TProjective, Transform};
/// A 2D general transformation that may not be inversible. Stored as an homogeneous 3x3 matrix.
/// A 2D general transformation that may not be invertible. Stored as an homogeneous 3x3 matrix.
pub type Transform2<N> = Transform<N, U2, TGeneral>;
/// An inversible 2D general transformation. Stored as an homogeneous 3x3 matrix.
/// An invertible 2D general transformation. Stored as an homogeneous 3x3 matrix.
pub type Projective2<N> = Transform<N, U2, TProjective>;
/// A 2D affine transformation. Stored as an homogeneous 3x3 matrix.
pub type Affine2<N> = Transform<N, U2, TAffine>;
/// A 3D general transformation that may not be inversible. Stored as an homogeneous 4x4 matrix.
pub type Transform3<N> = Transform<N, U3, TGeneral>;
/// An inversible 3D general transformation. Stored as an homogeneous 4x4 matrix.
/// An invertible 3D general transformation. Stored as an homogeneous 4x4 matrix.
pub type Projective3<N> = Transform<N, U3, TProjective>;
/// A 3D affine transformation. Stored as an homogeneous 4x4 matrix.
pub type Affine3<N> = Transform<N, U3, TAffine>;

View File

@ -12,6 +12,33 @@ impl<N: Real, D: DimNameAdd<U1>, C: TCategory> Transform<N, D, C>
where DefaultAllocator: Allocator<N, DimNameSum<D, U1>, DimNameSum<D, U1>>
{
/// Creates a new identity transform.
///
/// # Example
///
/// ```
/// # use nalgebra::{Transform2, Projective2, Affine2, Transform3, Projective3, Affine3, Point2, Point3};
///
/// let pt = Point2::new(1.0, 2.0);
/// let t = Projective2::identity();
/// assert_eq!(t * pt, pt);
///
/// let aff = Affine2::identity();
/// assert_eq!(aff * pt, pt);
///
/// let aff = Transform2::identity();
/// assert_eq!(aff * pt, pt);
///
/// // Also works in 3D.
/// let pt = Point3::new(1.0, 2.0, 3.0);
/// let t = Projective3::identity();
/// assert_eq!(t * pt, pt);
///
/// let aff = Affine3::identity();
/// assert_eq!(aff * pt, pt);
///
/// let aff = Transform3::identity();
/// assert_eq!(aff * pt, pt);
/// ```
#[inline]
pub fn identity() -> Self {
Self::from_matrix_unchecked(MatrixN::<_, DimNameSum<D, U1>>::identity())