nalgebra/geometry/isometry.rs
1use approx::{AbsDiffEq, RelativeEq, UlpsEq};
2use std::fmt;
3use std::hash;
4#[cfg(feature = "abomonation-serialize")]
5use std::io::{Result as IOResult, Write};
6
7#[cfg(feature = "serde-serialize-no-std")]
8use serde::{Deserialize, Serialize};
9
10#[cfg(feature = "abomonation-serialize")]
11use abomonation::Abomonation;
12
13use simba::scalar::{RealField, SubsetOf};
14use simba::simd::SimdRealField;
15
16use crate::base::allocator::Allocator;
17use crate::base::dimension::{DimNameAdd, DimNameSum, U1};
18use crate::base::storage::Owned;
19use crate::base::{Const, DefaultAllocator, OMatrix, SVector, Scalar, Unit};
20use crate::geometry::{AbstractRotation, Point, Translation};
21
22/// A direct isometry, i.e., a rotation followed by a translation (aka. a rigid-body motion).
23///
24/// This is also known as an element of a Special Euclidean (SE) group.
25/// The `Isometry` type can either represent a 2D or 3D isometry.
26/// A 2D isometry is composed of:
27/// - A translation part of type [`Translation2`](crate::Translation2)
28/// - A rotation part which can either be a [`UnitComplex`](crate::UnitComplex) or a [`Rotation2`](crate::Rotation2).
29/// A 3D isometry is composed of:
30/// - A translation part of type [`Translation3`](crate::Translation3)
31/// - A rotation part which can either be a [`UnitQuaternion`](crate::UnitQuaternion) or a [`Rotation3`](crate::Rotation3).
32///
33/// Note that instead of using the [`Isometry`](crate::Isometry) type in your code directly, you should use one
34/// of its aliases: [`Isometry2`](crate::Isometry2), [`Isometry3`](crate::Isometry3),
35/// [`IsometryMatrix2`](crate::IsometryMatrix2), [`IsometryMatrix3`](crate::IsometryMatrix3). Though
36/// keep in mind that all the documentation of all the methods of these aliases will also appears on
37/// this page.
38///
39/// # Construction
40/// * [From a 2D vector and/or an angle <span style="float:right;">`new`, `translation`, `rotation`…</span>](#construction-from-a-2d-vector-andor-a-rotation-angle)
41/// * [From a 3D vector and/or an axis-angle <span style="float:right;">`new`, `translation`, `rotation`…</span>](#construction-from-a-3d-vector-andor-an-axis-angle)
42/// * [From a 3D eye position and target point <span style="float:right;">`look_at`, `look_at_lh`, `face_towards`…</span>](#construction-from-a-3d-eye-position-and-target-point)
43/// * [From the translation and rotation parts <span style="float:right;">`from_parts`…</span>](#from-the-translation-and-rotation-parts)
44///
45/// # Transformation and composition
46/// Note that transforming vectors and points can be done by multiplication, e.g., `isometry * point`.
47/// Composing an isometry with another transformation can also be done by multiplication or division.
48///
49/// * [Transformation of a vector or a point <span style="float:right;">`transform_vector`, `inverse_transform_point`…</span>](#transformation-of-a-vector-or-a-point)
50/// * [Inversion and in-place composition <span style="float:right;">`inverse`, `append_rotation_wrt_point_mut`…</span>](#inversion-and-in-place-composition)
51/// * [Interpolation <span style="float:right;">`lerp_slerp`…</span>](#interpolation)
52///
53/// # Conversion to a matrix
54/// * [Conversion to a matrix <span style="float:right;">`to_matrix`…</span>](#conversion-to-a-matrix)
55///
56#[repr(C)]
57#[derive(Debug, Copy, Clone)]
58#[cfg_attr(
59 all(not(target_os = "cuda"), feature = "cuda"),
60 derive(cust::DeviceCopy)
61)]
62#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
63#[cfg_attr(
64 feature = "serde-serialize-no-std",
65 serde(bound(serialize = "R: Serialize,
66 DefaultAllocator: Allocator<T, Const<D>>,
67 Owned<T, Const<D>>: Serialize,
68 T: Scalar"))
69)]
70#[cfg_attr(
71 feature = "serde-serialize-no-std",
72 serde(bound(deserialize = "R: Deserialize<'de>,
73 DefaultAllocator: Allocator<T, Const<D>>,
74 Owned<T, Const<D>>: Deserialize<'de>,
75 T: Scalar"))
76)]
77pub struct Isometry<T, R, const D: usize> {
78 /// The pure rotational part of this isometry.
79 pub rotation: R,
80 /// The pure translational part of this isometry.
81 pub translation: Translation<T, D>,
82}
83
84#[cfg(feature = "abomonation-serialize")]
85impl<T, R, const D: usize> Abomonation for Isometry<T, R, D>
86where
87 T: SimdRealField,
88 R: Abomonation,
89 Translation<T, D>: Abomonation,
90{
91 unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {
92 self.rotation.entomb(writer)?;
93 self.translation.entomb(writer)
94 }
95
96 fn extent(&self) -> usize {
97 self.rotation.extent() + self.translation.extent()
98 }
99
100 unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> {
101 self.rotation
102 .exhume(bytes)
103 .and_then(|bytes| self.translation.exhume(bytes))
104 }
105}
106
107#[cfg(feature = "rkyv-serialize-no-std")]
108mod rkyv_impl {
109 use super::Isometry;
110 use crate::{base::Scalar, geometry::Translation};
111 use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize};
112
113 impl<T: Scalar + Archive, R: Archive, const D: usize> Archive for Isometry<T, R, D>
114 where
115 T::Archived: Scalar,
116 {
117 type Archived = Isometry<T::Archived, R::Archived, D>;
118 type Resolver = (R::Resolver, <Translation<T, D> as Archive>::Resolver);
119
120 fn resolve(
121 &self,
122 pos: usize,
123 resolver: Self::Resolver,
124 out: &mut core::mem::MaybeUninit<Self::Archived>,
125 ) {
126 self.rotation.resolve(
127 pos + offset_of!(Self::Archived, rotation),
128 resolver.0,
129 project_struct!(out: Self::Archived => rotation),
130 );
131 self.translation.resolve(
132 pos + offset_of!(Self::Archived, translation),
133 resolver.1,
134 project_struct!(out: Self::Archived => translation),
135 );
136 }
137 }
138
139 impl<T: Scalar + Serialize<S>, R: Serialize<S>, S: Fallible + ?Sized, const D: usize>
140 Serialize<S> for Isometry<T, R, D>
141 where
142 T::Archived: Scalar,
143 {
144 fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
145 Ok((
146 self.rotation.serialize(serializer)?,
147 self.translation.serialize(serializer)?,
148 ))
149 }
150 }
151
152 impl<T: Scalar + Archive, R: Archive, _D: Fallible + ?Sized, const D: usize>
153 Deserialize<Isometry<T, R, D>, _D> for Isometry<T::Archived, R::Archived, D>
154 where
155 T::Archived: Scalar + Deserialize<T, _D>,
156 R::Archived: Scalar + Deserialize<R, _D>,
157 {
158 fn deserialize(&self, deserializer: &mut _D) -> Result<Isometry<T, R, D>, _D::Error> {
159 Ok(Isometry {
160 rotation: self.rotation.deserialize(deserializer)?,
161 translation: self.translation.deserialize(deserializer)?,
162 })
163 }
164 }
165}
166
167impl<T: Scalar + hash::Hash, R: hash::Hash, const D: usize> hash::Hash for Isometry<T, R, D>
168where
169 Owned<T, Const<D>>: hash::Hash,
170{
171 fn hash<H: hash::Hasher>(&self, state: &mut H) {
172 self.translation.hash(state);
173 self.rotation.hash(state);
174 }
175}
176
177/// # From the translation and rotation parts
178impl<T: Scalar, R: AbstractRotation<T, D>, const D: usize> Isometry<T, R, D> {
179 /// Creates a new isometry from its rotational and translational parts.
180 ///
181 /// # Example
182 ///
183 /// ```
184 /// # #[macro_use] extern crate approx;
185 /// # use std::f32;
186 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3};
187 /// let tra = Translation3::new(0.0, 0.0, 3.0);
188 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::PI);
189 /// let iso = Isometry3::from_parts(tra, rot);
190 ///
191 /// assert_relative_eq!(iso * Point3::new(1.0, 2.0, 3.0), Point3::new(-1.0, 2.0, 0.0), epsilon = 1.0e-6);
192 /// ```
193 #[inline]
194 pub fn from_parts(translation: Translation<T, D>, rotation: R) -> Self {
195 Self {
196 rotation,
197 translation,
198 }
199 }
200}
201
202/// # Inversion and in-place composition
203impl<T: SimdRealField, R: AbstractRotation<T, D>, const D: usize> Isometry<T, R, D>
204where
205 T::Element: SimdRealField,
206{
207 /// Inverts `self`.
208 ///
209 /// # Example
210 ///
211 /// ```
212 /// # use std::f32;
213 /// # use nalgebra::{Isometry2, Point2, Vector2};
214 /// let iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
215 /// let inv = iso.inverse();
216 /// let pt = Point2::new(1.0, 2.0);
217 ///
218 /// assert_eq!(inv * (iso * pt), pt);
219 /// ```
220 #[inline]
221 #[must_use = "Did you mean to use inverse_mut()?"]
222 pub fn inverse(&self) -> Self {
223 let mut res = self.clone();
224 res.inverse_mut();
225 res
226 }
227
228 /// Inverts `self` in-place.
229 ///
230 /// # Example
231 ///
232 /// ```
233 /// # use std::f32;
234 /// # use nalgebra::{Isometry2, Point2, Vector2};
235 /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
236 /// let pt = Point2::new(1.0, 2.0);
237 /// let transformed_pt = iso * pt;
238 /// iso.inverse_mut();
239 ///
240 /// assert_eq!(iso * transformed_pt, pt);
241 /// ```
242 #[inline]
243 pub fn inverse_mut(&mut self) {
244 self.rotation.inverse_mut();
245 self.translation.inverse_mut();
246 self.translation.vector = self.rotation.transform_vector(&self.translation.vector);
247 }
248
249 /// Computes `self.inverse() * rhs` in a more efficient way.
250 ///
251 /// # Example
252 ///
253 /// ```
254 /// # use std::f32;
255 /// # use nalgebra::{Isometry2, Point2, Vector2};
256 /// let mut iso1 = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
257 /// let mut iso2 = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_4);
258 ///
259 /// assert_eq!(iso1.inverse() * iso2, iso1.inv_mul(&iso2));
260 /// ```
261 #[inline]
262 #[must_use]
263 pub fn inv_mul(&self, rhs: &Isometry<T, R, D>) -> Self {
264 let inv_rot1 = self.rotation.inverse();
265 let tr_12 = &rhs.translation.vector - &self.translation.vector;
266 Isometry::from_parts(
267 inv_rot1.transform_vector(&tr_12).into(),
268 inv_rot1 * rhs.rotation.clone(),
269 )
270 }
271
272 /// Appends to `self` the given translation in-place.
273 ///
274 /// # Example
275 ///
276 /// ```
277 /// # use std::f32;
278 /// # use nalgebra::{Isometry2, Translation2, Vector2};
279 /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
280 /// let tra = Translation2::new(3.0, 4.0);
281 /// // Same as `iso = tra * iso`.
282 /// iso.append_translation_mut(&tra);
283 ///
284 /// assert_eq!(iso.translation, Translation2::new(4.0, 6.0));
285 /// ```
286 #[inline]
287 pub fn append_translation_mut(&mut self, t: &Translation<T, D>) {
288 self.translation.vector += &t.vector
289 }
290
291 /// Appends to `self` the given rotation in-place.
292 ///
293 /// # Example
294 ///
295 /// ```
296 /// # #[macro_use] extern crate approx;
297 /// # use std::f32;
298 /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2};
299 /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::PI / 6.0);
300 /// let rot = UnitComplex::new(f32::consts::PI / 2.0);
301 /// // Same as `iso = rot * iso`.
302 /// iso.append_rotation_mut(&rot);
303 ///
304 /// assert_relative_eq!(iso, Isometry2::new(Vector2::new(-2.0, 1.0), f32::consts::PI * 2.0 / 3.0), epsilon = 1.0e-6);
305 /// ```
306 #[inline]
307 pub fn append_rotation_mut(&mut self, r: &R) {
308 self.rotation = r.clone() * self.rotation.clone();
309 self.translation.vector = r.transform_vector(&self.translation.vector);
310 }
311
312 /// Appends in-place to `self` a rotation centered at the point `p`, i.e., the rotation that
313 /// lets `p` invariant.
314 ///
315 /// # Example
316 ///
317 /// ```
318 /// # #[macro_use] extern crate approx;
319 /// # use std::f32;
320 /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2};
321 /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
322 /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2);
323 /// let pt = Point2::new(1.0, 0.0);
324 /// iso.append_rotation_wrt_point_mut(&rot, &pt);
325 ///
326 /// assert_relative_eq!(iso * pt, Point2::new(-2.0, 0.0), epsilon = 1.0e-6);
327 /// ```
328 #[inline]
329 pub fn append_rotation_wrt_point_mut(&mut self, r: &R, p: &Point<T, D>) {
330 self.translation.vector -= &p.coords;
331 self.append_rotation_mut(r);
332 self.translation.vector += &p.coords;
333 }
334
335 /// Appends in-place to `self` a rotation centered at the point with coordinates
336 /// `self.translation`.
337 ///
338 /// # Example
339 ///
340 /// ```
341 /// # use std::f32;
342 /// # use nalgebra::{Isometry2, Translation2, UnitComplex, Vector2, Point2};
343 /// let mut iso = Isometry2::new(Vector2::new(1.0, 2.0), f32::consts::FRAC_PI_2);
344 /// let rot = UnitComplex::new(f32::consts::FRAC_PI_2);
345 /// iso.append_rotation_wrt_center_mut(&rot);
346 ///
347 /// // The translation part should not have changed.
348 /// assert_eq!(iso.translation.vector, Vector2::new(1.0, 2.0));
349 /// assert_eq!(iso.rotation, UnitComplex::new(f32::consts::PI));
350 /// ```
351 #[inline]
352 pub fn append_rotation_wrt_center_mut(&mut self, r: &R) {
353 self.rotation = r.clone() * self.rotation.clone();
354 }
355}
356
357/// # Transformation of a vector or a point
358impl<T: SimdRealField, R: AbstractRotation<T, D>, const D: usize> Isometry<T, R, D>
359where
360 T::Element: SimdRealField,
361{
362 /// Transform the given point by this isometry.
363 ///
364 /// This is the same as the multiplication `self * pt`.
365 ///
366 /// # Example
367 ///
368 /// ```
369 /// # #[macro_use] extern crate approx;
370 /// # use std::f32;
371 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3};
372 /// let tra = Translation3::new(0.0, 0.0, 3.0);
373 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2);
374 /// let iso = Isometry3::from_parts(tra, rot);
375 ///
376 /// let transformed_point = iso.transform_point(&Point3::new(1.0, 2.0, 3.0));
377 /// assert_relative_eq!(transformed_point, Point3::new(3.0, 2.0, 2.0), epsilon = 1.0e-6);
378 /// ```
379 #[inline]
380 #[must_use]
381 pub fn transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
382 self * pt
383 }
384
385 /// Transform the given vector by this isometry, ignoring the translation
386 /// component of the isometry.
387 ///
388 /// This is the same as the multiplication `self * v`.
389 ///
390 /// # Example
391 ///
392 /// ```
393 /// # #[macro_use] extern crate approx;
394 /// # use std::f32;
395 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
396 /// let tra = Translation3::new(0.0, 0.0, 3.0);
397 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2);
398 /// let iso = Isometry3::from_parts(tra, rot);
399 ///
400 /// let transformed_point = iso.transform_vector(&Vector3::new(1.0, 2.0, 3.0));
401 /// assert_relative_eq!(transformed_point, Vector3::new(3.0, 2.0, -1.0), epsilon = 1.0e-6);
402 /// ```
403 #[inline]
404 #[must_use]
405 pub fn transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
406 self * v
407 }
408
409 /// Transform the given point by the inverse of this isometry. This may be
410 /// less expensive than computing the entire isometry inverse and then
411 /// transforming the point.
412 ///
413 /// # Example
414 ///
415 /// ```
416 /// # #[macro_use] extern crate approx;
417 /// # use std::f32;
418 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3, Point3};
419 /// let tra = Translation3::new(0.0, 0.0, 3.0);
420 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2);
421 /// let iso = Isometry3::from_parts(tra, rot);
422 ///
423 /// let transformed_point = iso.inverse_transform_point(&Point3::new(1.0, 2.0, 3.0));
424 /// assert_relative_eq!(transformed_point, Point3::new(0.0, 2.0, 1.0), epsilon = 1.0e-6);
425 /// ```
426 #[inline]
427 #[must_use]
428 pub fn inverse_transform_point(&self, pt: &Point<T, D>) -> Point<T, D> {
429 self.rotation
430 .inverse_transform_point(&(pt - &self.translation.vector))
431 }
432
433 /// Transform the given vector by the inverse of this isometry, ignoring the
434 /// translation component of the isometry. This may be
435 /// less expensive than computing the entire isometry inverse and then
436 /// transforming the point.
437 ///
438 /// # Example
439 ///
440 /// ```
441 /// # #[macro_use] extern crate approx;
442 /// # use std::f32;
443 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
444 /// let tra = Translation3::new(0.0, 0.0, 3.0);
445 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::y() * f32::consts::FRAC_PI_2);
446 /// let iso = Isometry3::from_parts(tra, rot);
447 ///
448 /// let transformed_point = iso.inverse_transform_vector(&Vector3::new(1.0, 2.0, 3.0));
449 /// assert_relative_eq!(transformed_point, Vector3::new(-3.0, 2.0, 1.0), epsilon = 1.0e-6);
450 /// ```
451 #[inline]
452 #[must_use]
453 pub fn inverse_transform_vector(&self, v: &SVector<T, D>) -> SVector<T, D> {
454 self.rotation.inverse_transform_vector(v)
455 }
456
457 /// Transform the given unit vector by the inverse of this isometry, ignoring the
458 /// translation component of the isometry. This may be
459 /// less expensive than computing the entire isometry inverse and then
460 /// transforming the point.
461 ///
462 /// # Example
463 ///
464 /// ```
465 /// # #[macro_use] extern crate approx;
466 /// # use std::f32;
467 /// # use nalgebra::{Isometry3, Translation3, UnitQuaternion, Vector3};
468 /// let tra = Translation3::new(0.0, 0.0, 3.0);
469 /// let rot = UnitQuaternion::from_scaled_axis(Vector3::z() * f32::consts::FRAC_PI_2);
470 /// let iso = Isometry3::from_parts(tra, rot);
471 ///
472 /// let transformed_point = iso.inverse_transform_unit_vector(&Vector3::x_axis());
473 /// assert_relative_eq!(transformed_point, -Vector3::y_axis(), epsilon = 1.0e-6);
474 /// ```
475 #[inline]
476 #[must_use]
477 pub fn inverse_transform_unit_vector(&self, v: &Unit<SVector<T, D>>) -> Unit<SVector<T, D>> {
478 self.rotation.inverse_transform_unit_vector(v)
479 }
480}
481
482// NOTE: we don't require `R: Rotation<...>` here because this is not useful for the implementation
483// and makes it hard to use it, e.g., for Transform × Isometry implementation.
484// This is OK since all constructors of the isometry enforce the Rotation bound already (and
485// explicit struct construction is prevented by the dummy ZST field).
486/// # Conversion to a matrix
487impl<T: SimdRealField, R, const D: usize> Isometry<T, R, D> {
488 /// Converts this isometry into its equivalent homogeneous transformation matrix.
489 ///
490 /// This is the same as `self.to_matrix()`.
491 ///
492 /// # Example
493 ///
494 /// ```
495 /// # #[macro_use] extern crate approx;
496 /// # use std::f32;
497 /// # use nalgebra::{Isometry2, Vector2, Matrix3};
498 /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6);
499 /// let expected = Matrix3::new(0.8660254, -0.5, 10.0,
500 /// 0.5, 0.8660254, 20.0,
501 /// 0.0, 0.0, 1.0);
502 ///
503 /// assert_relative_eq!(iso.to_homogeneous(), expected, epsilon = 1.0e-6);
504 /// ```
505 #[inline]
506 #[must_use]
507 pub fn to_homogeneous(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
508 where
509 Const<D>: DimNameAdd<U1>,
510 R: SubsetOf<OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>>,
511 DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
512 {
513 let mut res: OMatrix<T, _, _> = crate::convert_ref(&self.rotation);
514 res.fixed_slice_mut::<D, 1>(0, D)
515 .copy_from(&self.translation.vector);
516
517 res
518 }
519
520 /// Converts this isometry into its equivalent homogeneous transformation matrix.
521 ///
522 /// This is the same as `self.to_homogeneous()`.
523 ///
524 /// # Example
525 ///
526 /// ```
527 /// # #[macro_use] extern crate approx;
528 /// # use std::f32;
529 /// # use nalgebra::{Isometry2, Vector2, Matrix3};
530 /// let iso = Isometry2::new(Vector2::new(10.0, 20.0), f32::consts::FRAC_PI_6);
531 /// let expected = Matrix3::new(0.8660254, -0.5, 10.0,
532 /// 0.5, 0.8660254, 20.0,
533 /// 0.0, 0.0, 1.0);
534 ///
535 /// assert_relative_eq!(iso.to_matrix(), expected, epsilon = 1.0e-6);
536 /// ```
537 #[inline]
538 #[must_use]
539 pub fn to_matrix(&self) -> OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>
540 where
541 Const<D>: DimNameAdd<U1>,
542 R: SubsetOf<OMatrix<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>>,
543 DefaultAllocator: Allocator<T, DimNameSum<Const<D>, U1>, DimNameSum<Const<D>, U1>>,
544 {
545 self.to_homogeneous()
546 }
547}
548
549impl<T: SimdRealField, R, const D: usize> Eq for Isometry<T, R, D> where
550 R: AbstractRotation<T, D> + Eq
551{
552}
553
554impl<T: SimdRealField, R, const D: usize> PartialEq for Isometry<T, R, D>
555where
556 R: AbstractRotation<T, D> + PartialEq,
557{
558 #[inline]
559 fn eq(&self, right: &Self) -> bool {
560 self.translation == right.translation && self.rotation == right.rotation
561 }
562}
563
564impl<T: RealField, R, const D: usize> AbsDiffEq for Isometry<T, R, D>
565where
566 R: AbstractRotation<T, D> + AbsDiffEq<Epsilon = T::Epsilon>,
567 T::Epsilon: Clone,
568{
569 type Epsilon = T::Epsilon;
570
571 #[inline]
572 fn default_epsilon() -> Self::Epsilon {
573 T::default_epsilon()
574 }
575
576 #[inline]
577 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
578 self.translation
579 .abs_diff_eq(&other.translation, epsilon.clone())
580 && self.rotation.abs_diff_eq(&other.rotation, epsilon)
581 }
582}
583
584impl<T: RealField, R, const D: usize> RelativeEq for Isometry<T, R, D>
585where
586 R: AbstractRotation<T, D> + RelativeEq<Epsilon = T::Epsilon>,
587 T::Epsilon: Clone,
588{
589 #[inline]
590 fn default_max_relative() -> Self::Epsilon {
591 T::default_max_relative()
592 }
593
594 #[inline]
595 fn relative_eq(
596 &self,
597 other: &Self,
598 epsilon: Self::Epsilon,
599 max_relative: Self::Epsilon,
600 ) -> bool {
601 self.translation
602 .relative_eq(&other.translation, epsilon.clone(), max_relative.clone())
603 && self
604 .rotation
605 .relative_eq(&other.rotation, epsilon, max_relative)
606 }
607}
608
609impl<T: RealField, R, const D: usize> UlpsEq for Isometry<T, R, D>
610where
611 R: AbstractRotation<T, D> + UlpsEq<Epsilon = T::Epsilon>,
612 T::Epsilon: Clone,
613{
614 #[inline]
615 fn default_max_ulps() -> u32 {
616 T::default_max_ulps()
617 }
618
619 #[inline]
620 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
621 self.translation
622 .ulps_eq(&other.translation, epsilon.clone(), max_ulps)
623 && self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps)
624 }
625}
626
627/*
628 *
629 * Display
630 *
631 */
632impl<T: RealField + fmt::Display, R, const D: usize> fmt::Display for Isometry<T, R, D>
633where
634 R: fmt::Display,
635{
636 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637 let precision = f.precision().unwrap_or(3);
638
639 writeln!(f, "Isometry {{")?;
640 write!(f, "{:.*}", precision, self.translation)?;
641 write!(f, "{:.*}", precision, self.rotation)?;
642 writeln!(f, "}}")
643 }
644}