use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use std::fmt; use std::hash; #[cfg(feature = "abomonation-serialize")] use std::io::{Result as IOResult, Write}; use std::marker::PhantomData; #[cfg(feature = "serde-serialize")] use serde::{Serialize, Deserialize}; #[cfg(feature = "abomonation-serialize")] use abomonation::Abomonation; use alga::general::{Real, SubsetOf}; use alga::linear::Rotation; use base::allocator::Allocator; use base::dimension::{DimName, DimNameAdd, DimNameSum, U1}; use base::storage::Owned; use base::{DefaultAllocator, MatrixN}; use geometry::{Point, Translation}; /// A direct isometry, i.e., a rotation followed by a translation, aka. a rigid-body motion, aka. an element of a Special Euclidean (SE) group. #[repr(C)] #[derive(Debug)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde-serialize", serde( bound( serialize = "R: Serialize, DefaultAllocator: Allocator, Owned: Serialize" ) ) )] #[cfg_attr( feature = "serde-serialize", serde( bound( deserialize = "R: Deserialize<'de>, DefaultAllocator: Allocator, Owned: Deserialize<'de>" ) ) )] pub struct Isometry where DefaultAllocator: Allocator, { /// The pure rotational part of this isometry. pub rotation: R, /// The pure translational part of this isometry. pub translation: Translation, // One dummy private field just to prevent explicit construction. #[cfg_attr(feature = "serde-serialize", serde(skip_serializing, skip_deserializing))] _noconstruct: PhantomData, } #[cfg(feature = "abomonation-serialize")] impl Abomonation for Isometry where N: Real, D: DimName, R: Abomonation, Translation: Abomonation, DefaultAllocator: Allocator, { unsafe fn entomb(&self, writer: &mut W) -> IOResult<()> { self.rotation.entomb(writer)?; self.translation.entomb(writer) } fn extent(&self) -> usize { self.rotation.extent() + self.translation.extent() } unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> { self.rotation .exhume(bytes) .and_then(|bytes| self.translation.exhume(bytes)) } } impl hash::Hash for Isometry where DefaultAllocator: Allocator, Owned: hash::Hash, { fn hash(&self, state: &mut H) { self.translation.hash(state); self.rotation.hash(state); } } impl> + Copy> Copy for Isometry where DefaultAllocator: Allocator, Owned: Copy, { } impl> + Clone> Clone for Isometry where DefaultAllocator: Allocator, { #[inline] fn clone(&self) -> Self { Isometry::from_parts(self.translation.clone(), self.rotation.clone()) } } impl>> Isometry where DefaultAllocator: Allocator, { /// Creates a new isometry from its rotational and translational parts. #[inline] pub fn from_parts(translation: Translation, rotation: R) -> Isometry { Isometry { rotation: rotation, translation: translation, _noconstruct: PhantomData, } } /// Inverts `self`. #[inline] pub fn inverse(&self) -> Isometry { let mut res = self.clone(); res.inverse_mut(); res } /// Inverts `self`. #[inline] pub fn inverse_mut(&mut self) { self.rotation.inverse_mut(); self.translation.inverse_mut(); self.translation.vector = self.rotation.transform_vector(&self.translation.vector); } /// Appends to `self` the given translation in-place. #[inline] pub fn append_translation_mut(&mut self, t: &Translation) { self.translation.vector += &t.vector } /// Appends to `self` the given rotation in-place. #[inline] pub fn append_rotation_mut(&mut self, r: &R) { self.rotation = self.rotation.append_rotation(&r); self.translation.vector = r.transform_vector(&self.translation.vector); } /// Appends in-place to `self` a rotation centered at the point `p`, i.e., the rotation that /// lets `p` invariant. #[inline] pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point) { self.translation.vector -= &p.coords; self.append_rotation_mut(r); self.translation.vector += &p.coords; } /// Appends in-place to `self` a rotation centered at the point with coordinates /// `self.translation`. #[inline] pub fn append_rotation_wrt_center_mut(&mut self, r: &R) { let center = Point::from_coordinates(self.translation.vector.clone()); self.append_rotation_wrt_point_mut(r, ¢er) } } // NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation // and makes it hard to use it, e.g., for Transform × Isometry implementation. // This is OK since all constructors of the isometry enforce the Rotation bound already (and // explicit struct construction is prevented by the dummy ZST field). impl Isometry where DefaultAllocator: Allocator, { /// Converts this isometry into its equivalent homogeneous transformation matrix. #[inline] pub fn to_homogeneous(&self) -> MatrixN> where D: DimNameAdd, R: SubsetOf>>, DefaultAllocator: Allocator, DimNameSum>, { let mut res: MatrixN = ::convert_ref(&self.rotation); res.fixed_slice_mut::(0, D::dim()) .copy_from(&self.translation.vector); res } } impl Eq for Isometry where R: Rotation> + Eq, DefaultAllocator: Allocator, { } impl PartialEq for Isometry where R: Rotation> + PartialEq, DefaultAllocator: Allocator, { #[inline] fn eq(&self, right: &Isometry) -> bool { self.translation == right.translation && self.rotation == right.rotation } } impl AbsDiffEq for Isometry where R: Rotation> + AbsDiffEq, DefaultAllocator: Allocator, N::Epsilon: Copy, { 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.translation.abs_diff_eq(&other.translation, epsilon) && self.rotation.abs_diff_eq(&other.rotation, epsilon) } } impl RelativeEq for Isometry where R: Rotation> + RelativeEq, DefaultAllocator: Allocator, N::Epsilon: Copy, { #[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.translation .relative_eq(&other.translation, epsilon, max_relative) && self .rotation .relative_eq(&other.rotation, epsilon, max_relative) } } impl UlpsEq for Isometry where R: Rotation> + UlpsEq, DefaultAllocator: Allocator, N::Epsilon: Copy, { #[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.translation .ulps_eq(&other.translation, epsilon, max_ulps) && self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps) } } /* * * Display * */ impl fmt::Display for Isometry where R: fmt::Display, DefaultAllocator: Allocator + Allocator, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let precision = f.precision().unwrap_or(3); try!(writeln!(f, "Isometry {{")); try!(write!(f, "{:.*}", precision, self.translation)); try!(write!(f, "{:.*}", precision, self.rotation)); writeln!(f, "}}") } }