bevy_math/
isometry.rs

1//! Isometry types for expressing rigid motions in two and three dimensions.
2
3use crate::{Affine2, Affine3, Affine3A, Dir2, Dir3, Mat3, Mat3A, Quat, Rot2, Vec2, Vec3, Vec3A};
4use core::ops::Mul;
5
6#[cfg(feature = "approx")]
7use approx::{AbsDiffEq, RelativeEq, UlpsEq};
8
9#[cfg(feature = "bevy_reflect")]
10use bevy_reflect::{std_traits::ReflectDefault, Reflect};
11#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
12use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
13
14/// An isometry in two dimensions, representing a rotation followed by a translation.
15/// This can often be useful for expressing relative positions and transformations from one position to another.
16///
17/// In particular, this type represents a distance-preserving transformation known as a *rigid motion* or a *direct motion*,
18/// and belongs to the special [Euclidean group] SE(2). This includes translation and rotation, but excludes reflection.
19///
20/// For the three-dimensional version, see [`Isometry3d`].
21///
22/// [Euclidean group]: https://en.wikipedia.org/wiki/Euclidean_group
23///
24/// # Example
25///
26/// Isometries can be created from a given translation and rotation:
27///
28/// ```
29/// # use bevy_math::{Isometry2d, Rot2, Vec2};
30/// #
31/// let iso = Isometry2d::new(Vec2::new(2.0, 1.0), Rot2::degrees(90.0));
32/// ```
33///
34/// Or from separate parts:
35///
36/// ```
37/// # use bevy_math::{Isometry2d, Rot2, Vec2};
38/// #
39/// let iso1 = Isometry2d::from_translation(Vec2::new(2.0, 1.0));
40/// let iso2 = Isometry2d::from_rotation(Rot2::degrees(90.0));
41/// ```
42///
43/// The isometries can be used to transform points:
44///
45/// ```
46/// # use approx::assert_abs_diff_eq;
47/// # use bevy_math::{Isometry2d, Rot2, Vec2};
48/// #
49/// let iso = Isometry2d::new(Vec2::new(2.0, 1.0), Rot2::degrees(90.0));
50/// let point = Vec2::new(4.0, 4.0);
51///
52/// // These are equivalent
53/// let result = iso.transform_point(point);
54/// let result = iso * point;
55///
56/// assert_eq!(result, Vec2::new(-2.0, 5.0));
57/// ```
58///
59/// Isometries can also be composed together:
60///
61/// ```
62/// # use bevy_math::{Isometry2d, Rot2, Vec2};
63/// #
64/// # let iso = Isometry2d::new(Vec2::new(2.0, 1.0), Rot2::degrees(90.0));
65/// # let iso1 = Isometry2d::from_translation(Vec2::new(2.0, 1.0));
66/// # let iso2 = Isometry2d::from_rotation(Rot2::degrees(90.0));
67/// #
68/// assert_eq!(iso1 * iso2, iso);
69/// ```
70///
71/// One common operation is to compute an isometry representing the relative positions of two objects
72/// for things like intersection tests. This can be done with an inverse transformation:
73///
74/// ```
75/// # use bevy_math::{Isometry2d, Rot2, Vec2};
76/// #
77/// let circle_iso = Isometry2d::from_translation(Vec2::new(2.0, 1.0));
78/// let rectangle_iso = Isometry2d::from_rotation(Rot2::degrees(90.0));
79///
80/// // Compute the relative position and orientation between the two shapes
81/// let relative_iso = circle_iso.inverse() * rectangle_iso;
82///
83/// // Or alternatively, to skip an extra rotation operation:
84/// let relative_iso = circle_iso.inverse_mul(rectangle_iso);
85/// ```
86#[derive(Copy, Clone, Default, Debug, PartialEq)]
87#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
88#[cfg_attr(
89    feature = "bevy_reflect",
90    derive(Reflect),
91    reflect(Debug, PartialEq, Default)
92)]
93#[cfg_attr(
94    all(feature = "serialize", feature = "bevy_reflect"),
95    reflect(Serialize, Deserialize)
96)]
97pub struct Isometry2d {
98    /// The rotational part of a two-dimensional isometry.
99    pub rotation: Rot2,
100    /// The translational part of a two-dimensional isometry.
101    pub translation: Vec2,
102}
103
104impl Isometry2d {
105    /// The identity isometry which represents the rigid motion of not doing anything.
106    pub const IDENTITY: Self = Isometry2d {
107        rotation: Rot2::IDENTITY,
108        translation: Vec2::ZERO,
109    };
110
111    /// Create a two-dimensional isometry from a rotation and a translation.
112    #[inline]
113    pub fn new(translation: Vec2, rotation: Rot2) -> Self {
114        Isometry2d {
115            rotation,
116            translation,
117        }
118    }
119
120    /// Create a two-dimensional isometry from a rotation.
121    #[inline]
122    pub fn from_rotation(rotation: Rot2) -> Self {
123        Isometry2d {
124            rotation,
125            translation: Vec2::ZERO,
126        }
127    }
128
129    /// Create a two-dimensional isometry from a translation.
130    #[inline]
131    pub fn from_translation(translation: Vec2) -> Self {
132        Isometry2d {
133            rotation: Rot2::IDENTITY,
134            translation,
135        }
136    }
137
138    /// Create a two-dimensional isometry from a translation with the given `x` and `y` components.
139    #[inline]
140    pub fn from_xy(x: f32, y: f32) -> Self {
141        Isometry2d {
142            rotation: Rot2::IDENTITY,
143            translation: Vec2::new(x, y),
144        }
145    }
146
147    /// The inverse isometry that undoes this one.
148    #[inline]
149    pub fn inverse(&self) -> Self {
150        let inv_rot = self.rotation.inverse();
151        Isometry2d {
152            rotation: inv_rot,
153            translation: inv_rot * -self.translation,
154        }
155    }
156
157    /// Compute `iso1.inverse() * iso2` in a more efficient way for one-shot cases.
158    ///
159    /// If the same isometry is used multiple times, it is more efficient to instead compute
160    /// the inverse once and use that for each transformation.
161    #[inline]
162    pub fn inverse_mul(&self, rhs: Self) -> Self {
163        let inv_rot = self.rotation.inverse();
164        let delta_translation = rhs.translation - self.translation;
165        Self::new(inv_rot * delta_translation, inv_rot * rhs.rotation)
166    }
167
168    /// Transform a point by rotating and translating it using this isometry.
169    #[inline]
170    pub fn transform_point(&self, point: Vec2) -> Vec2 {
171        self.rotation * point + self.translation
172    }
173
174    /// Transform a point by rotating and translating it using the inverse of this isometry.
175    ///
176    /// This is more efficient than `iso.inverse().transform_point(point)` for one-shot cases.
177    /// If the same isometry is used multiple times, it is more efficient to instead compute
178    /// the inverse once and use that for each transformation.
179    #[inline]
180    pub fn inverse_transform_point(&self, point: Vec2) -> Vec2 {
181        self.rotation.inverse() * (point - self.translation)
182    }
183}
184
185impl From<Isometry2d> for Affine2 {
186    #[inline]
187    fn from(iso: Isometry2d) -> Self {
188        Affine2 {
189            matrix2: iso.rotation.into(),
190            translation: iso.translation,
191        }
192    }
193}
194
195impl From<Vec2> for Isometry2d {
196    #[inline]
197    fn from(translation: Vec2) -> Self {
198        Isometry2d::from_translation(translation)
199    }
200}
201
202impl From<Rot2> for Isometry2d {
203    #[inline]
204    fn from(rotation: Rot2) -> Self {
205        Isometry2d::from_rotation(rotation)
206    }
207}
208
209impl Mul for Isometry2d {
210    type Output = Self;
211
212    #[inline]
213    fn mul(self, rhs: Self) -> Self::Output {
214        Isometry2d {
215            rotation: self.rotation * rhs.rotation,
216            translation: self.rotation * rhs.translation + self.translation,
217        }
218    }
219}
220
221impl Mul<Vec2> for Isometry2d {
222    type Output = Vec2;
223
224    #[inline]
225    fn mul(self, rhs: Vec2) -> Self::Output {
226        self.transform_point(rhs)
227    }
228}
229
230impl Mul<Dir2> for Isometry2d {
231    type Output = Dir2;
232
233    #[inline]
234    fn mul(self, rhs: Dir2) -> Self::Output {
235        self.rotation * rhs
236    }
237}
238
239#[cfg(feature = "approx")]
240impl AbsDiffEq for Isometry2d {
241    type Epsilon = <f32 as AbsDiffEq>::Epsilon;
242
243    fn default_epsilon() -> Self::Epsilon {
244        f32::default_epsilon()
245    }
246
247    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
248        self.rotation.abs_diff_eq(&other.rotation, epsilon)
249            && self.translation.abs_diff_eq(other.translation, epsilon)
250    }
251}
252
253#[cfg(feature = "approx")]
254impl RelativeEq for Isometry2d {
255    fn default_max_relative() -> Self::Epsilon {
256        Self::default_epsilon()
257    }
258
259    fn relative_eq(
260        &self,
261        other: &Self,
262        epsilon: Self::Epsilon,
263        max_relative: Self::Epsilon,
264    ) -> bool {
265        self.rotation
266            .relative_eq(&other.rotation, epsilon, max_relative)
267            && self
268                .translation
269                .relative_eq(&other.translation, epsilon, max_relative)
270    }
271}
272
273#[cfg(feature = "approx")]
274impl UlpsEq for Isometry2d {
275    fn default_max_ulps() -> u32 {
276        4
277    }
278
279    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
280        self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps)
281            && self
282                .translation
283                .ulps_eq(&other.translation, epsilon, max_ulps)
284    }
285}
286
287/// An isometry in three dimensions, representing a rotation followed by a translation.
288/// This can often be useful for expressing relative positions and transformations from one position to another.
289///
290/// In particular, this type represents a distance-preserving transformation known as a *rigid motion* or a *direct motion*,
291/// and belongs to the special [Euclidean group] SE(3). This includes translation and rotation, but excludes reflection.
292///
293/// For the two-dimensional version, see [`Isometry2d`].
294///
295/// [Euclidean group]: https://en.wikipedia.org/wiki/Euclidean_group
296///
297/// # Example
298///
299/// Isometries can be created from a given translation and rotation:
300///
301/// ```
302/// # use bevy_math::{Isometry3d, Quat, Vec3};
303/// # use std::f32::consts::FRAC_PI_2;
304/// #
305/// let iso = Isometry3d::new(Vec3::new(2.0, 1.0, 3.0), Quat::from_rotation_z(FRAC_PI_2));
306/// ```
307///
308/// Or from separate parts:
309///
310/// ```
311/// # use bevy_math::{Isometry3d, Quat, Vec3};
312/// # use std::f32::consts::FRAC_PI_2;
313/// #
314/// let iso1 = Isometry3d::from_translation(Vec3::new(2.0, 1.0, 3.0));
315/// let iso2 = Isometry3d::from_rotation(Quat::from_rotation_z(FRAC_PI_2));
316/// ```
317///
318/// The isometries can be used to transform points:
319///
320/// ```
321/// # use approx::assert_relative_eq;
322/// # use bevy_math::{Isometry3d, Quat, Vec3};
323/// # use std::f32::consts::FRAC_PI_2;
324/// #
325/// let iso = Isometry3d::new(Vec3::new(2.0, 1.0, 3.0), Quat::from_rotation_z(FRAC_PI_2));
326/// let point = Vec3::new(4.0, 4.0, 4.0);
327///
328/// // These are equivalent
329/// let result = iso.transform_point(point);
330/// let result = iso * point;
331///
332/// assert_relative_eq!(result, Vec3::new(-2.0, 5.0, 7.0));
333/// ```
334///
335/// Isometries can also be composed together:
336///
337/// ```
338/// # use bevy_math::{Isometry3d, Quat, Vec3};
339/// # use std::f32::consts::FRAC_PI_2;
340/// #
341/// # let iso = Isometry3d::new(Vec3::new(2.0, 1.0, 3.0), Quat::from_rotation_z(FRAC_PI_2));
342/// # let iso1 = Isometry3d::from_translation(Vec3::new(2.0, 1.0, 3.0));
343/// # let iso2 = Isometry3d::from_rotation(Quat::from_rotation_z(FRAC_PI_2));
344/// #
345/// assert_eq!(iso1 * iso2, iso);
346/// ```
347///
348/// One common operation is to compute an isometry representing the relative positions of two objects
349/// for things like intersection tests. This can be done with an inverse transformation:
350///
351/// ```
352/// # use bevy_math::{Isometry3d, Quat, Vec3};
353/// # use std::f32::consts::FRAC_PI_2;
354/// #
355/// let sphere_iso = Isometry3d::from_translation(Vec3::new(2.0, 1.0, 3.0));
356/// let cuboid_iso = Isometry3d::from_rotation(Quat::from_rotation_z(FRAC_PI_2));
357///
358/// // Compute the relative position and orientation between the two shapes
359/// let relative_iso = sphere_iso.inverse() * cuboid_iso;
360///
361/// // Or alternatively, to skip an extra rotation operation:
362/// let relative_iso = sphere_iso.inverse_mul(cuboid_iso);
363/// ```
364#[derive(Copy, Clone, Default, Debug, PartialEq)]
365#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
366#[cfg_attr(
367    feature = "bevy_reflect",
368    derive(Reflect),
369    reflect(Debug, PartialEq, Default)
370)]
371#[cfg_attr(
372    all(feature = "serialize", feature = "bevy_reflect"),
373    reflect(Serialize, Deserialize)
374)]
375pub struct Isometry3d {
376    /// The rotational part of a three-dimensional isometry.
377    pub rotation: Quat,
378    /// The translational part of a three-dimensional isometry.
379    pub translation: Vec3A,
380}
381
382impl Isometry3d {
383    /// The identity isometry which represents the rigid motion of not doing anything.
384    pub const IDENTITY: Self = Isometry3d {
385        rotation: Quat::IDENTITY,
386        translation: Vec3A::ZERO,
387    };
388
389    /// Create a three-dimensional isometry from a rotation and a translation.
390    #[inline]
391    pub fn new(translation: impl Into<Vec3A>, rotation: Quat) -> Self {
392        Isometry3d {
393            rotation,
394            translation: translation.into(),
395        }
396    }
397
398    /// Create a three-dimensional isometry from a rotation.
399    #[inline]
400    pub fn from_rotation(rotation: Quat) -> Self {
401        Isometry3d {
402            rotation,
403            translation: Vec3A::ZERO,
404        }
405    }
406
407    /// Create a three-dimensional isometry from a translation.
408    #[inline]
409    pub fn from_translation(translation: impl Into<Vec3A>) -> Self {
410        Isometry3d {
411            rotation: Quat::IDENTITY,
412            translation: translation.into(),
413        }
414    }
415
416    /// Create a three-dimensional isometry from a translation with the given `x`, `y`, and `z` components.
417    #[inline]
418    pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
419        Isometry3d {
420            rotation: Quat::IDENTITY,
421            translation: Vec3A::new(x, y, z),
422        }
423    }
424
425    /// The inverse isometry that undoes this one.
426    #[inline]
427    pub fn inverse(&self) -> Self {
428        let inv_rot = self.rotation.inverse();
429        Isometry3d {
430            rotation: inv_rot,
431            translation: inv_rot * -self.translation,
432        }
433    }
434
435    /// Compute `iso1.inverse() * iso2` in a more efficient way for one-shot cases.
436    ///
437    /// If the same isometry is used multiple times, it is more efficient to instead compute
438    /// the inverse once and use that for each transformation.
439    #[inline]
440    pub fn inverse_mul(&self, rhs: Self) -> Self {
441        let inv_rot = self.rotation.inverse();
442        let delta_translation = rhs.translation - self.translation;
443        Self::new(inv_rot * delta_translation, inv_rot * rhs.rotation)
444    }
445
446    /// Transform a point by rotating and translating it using this isometry.
447    #[inline]
448    pub fn transform_point(&self, point: impl Into<Vec3A>) -> Vec3A {
449        self.rotation * point.into() + self.translation
450    }
451
452    /// Transform a point by rotating and translating it using the inverse of this isometry.
453    ///
454    /// This is more efficient than `iso.inverse().transform_point(point)` for one-shot cases.
455    /// If the same isometry is used multiple times, it is more efficient to instead compute
456    /// the inverse once and use that for each transformation.
457    #[inline]
458    pub fn inverse_transform_point(&self, point: impl Into<Vec3A>) -> Vec3A {
459        self.rotation.inverse() * (point.into() - self.translation)
460    }
461}
462
463impl From<Isometry3d> for Affine3 {
464    #[inline]
465    fn from(iso: Isometry3d) -> Self {
466        Affine3 {
467            matrix3: Mat3::from_quat(iso.rotation),
468            translation: iso.translation.into(),
469        }
470    }
471}
472
473impl From<Isometry3d> for Affine3A {
474    #[inline]
475    fn from(iso: Isometry3d) -> Self {
476        Affine3A {
477            matrix3: Mat3A::from_quat(iso.rotation),
478            translation: iso.translation,
479        }
480    }
481}
482
483impl From<Vec3> for Isometry3d {
484    #[inline]
485    fn from(translation: Vec3) -> Self {
486        Isometry3d::from_translation(translation)
487    }
488}
489
490impl From<Vec3A> for Isometry3d {
491    #[inline]
492    fn from(translation: Vec3A) -> Self {
493        Isometry3d::from_translation(translation)
494    }
495}
496
497impl From<Quat> for Isometry3d {
498    #[inline]
499    fn from(rotation: Quat) -> Self {
500        Isometry3d::from_rotation(rotation)
501    }
502}
503
504impl Mul for Isometry3d {
505    type Output = Self;
506
507    #[inline]
508    fn mul(self, rhs: Self) -> Self::Output {
509        Isometry3d {
510            rotation: self.rotation * rhs.rotation,
511            translation: self.rotation * rhs.translation + self.translation,
512        }
513    }
514}
515
516impl Mul<Vec3A> for Isometry3d {
517    type Output = Vec3A;
518
519    #[inline]
520    fn mul(self, rhs: Vec3A) -> Self::Output {
521        self.transform_point(rhs)
522    }
523}
524
525impl Mul<Vec3> for Isometry3d {
526    type Output = Vec3;
527
528    #[inline]
529    fn mul(self, rhs: Vec3) -> Self::Output {
530        self.transform_point(rhs).into()
531    }
532}
533
534impl Mul<Dir3> for Isometry3d {
535    type Output = Dir3;
536
537    #[inline]
538    fn mul(self, rhs: Dir3) -> Self::Output {
539        self.rotation * rhs
540    }
541}
542
543#[cfg(feature = "approx")]
544impl AbsDiffEq for Isometry3d {
545    type Epsilon = <f32 as AbsDiffEq>::Epsilon;
546
547    fn default_epsilon() -> Self::Epsilon {
548        f32::default_epsilon()
549    }
550
551    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
552        self.rotation.abs_diff_eq(other.rotation, epsilon)
553            && self.translation.abs_diff_eq(other.translation, epsilon)
554    }
555}
556
557#[cfg(feature = "approx")]
558impl RelativeEq for Isometry3d {
559    fn default_max_relative() -> Self::Epsilon {
560        Self::default_epsilon()
561    }
562
563    fn relative_eq(
564        &self,
565        other: &Self,
566        epsilon: Self::Epsilon,
567        max_relative: Self::Epsilon,
568    ) -> bool {
569        self.rotation
570            .relative_eq(&other.rotation, epsilon, max_relative)
571            && self
572                .translation
573                .relative_eq(&other.translation, epsilon, max_relative)
574    }
575}
576
577#[cfg(feature = "approx")]
578impl UlpsEq for Isometry3d {
579    fn default_max_ulps() -> u32 {
580        4
581    }
582
583    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
584        self.rotation.ulps_eq(&other.rotation, epsilon, max_ulps)
585            && self
586                .translation
587                .ulps_eq(&other.translation, epsilon, max_ulps)
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use super::*;
594    use crate::{vec2, vec3, vec3a};
595    // use approx::assert_abs_diff_eq;
596    use core::f32::consts::{FRAC_PI_2, FRAC_PI_3};
597
598    #[test]
599    fn mul_2d() {
600        let iso1 = Isometry2d::new(vec2(1.0, 0.0), Rot2::FRAC_PI_2);
601        let iso2 = Isometry2d::new(vec2(0.0, 1.0), Rot2::FRAC_PI_2);
602        let expected = Isometry2d::new(vec2(0.0, 0.0), Rot2::PI);
603        // assert_abs_diff_eq!(iso1 * iso2, expected);
604    }
605
606    #[test]
607    fn inverse_mul_2d() {
608        let iso1 = Isometry2d::new(vec2(1.0, 0.0), Rot2::FRAC_PI_2);
609        let iso2 = Isometry2d::new(vec2(0.0, 0.0), Rot2::PI);
610        let expected = Isometry2d::new(vec2(0.0, 1.0), Rot2::FRAC_PI_2);
611        // assert_abs_diff_eq!(iso1.inverse_mul(iso2), expected);
612    }
613
614    #[test]
615    fn mul_3d() {
616        let iso1 = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_x(FRAC_PI_2));
617        let iso2 = Isometry3d::new(vec3(0.0, 1.0, 0.0), Quat::IDENTITY);
618        let expected = Isometry3d::new(vec3(1.0, 0.0, 1.0), Quat::from_rotation_x(FRAC_PI_2));
619        // assert_abs_diff_eq!(iso1 * iso2, expected);
620    }
621
622    #[test]
623    fn inverse_mul_3d() {
624        let iso1 = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_x(FRAC_PI_2));
625        let iso2 = Isometry3d::new(vec3(1.0, 0.0, 1.0), Quat::from_rotation_x(FRAC_PI_2));
626        let expected = Isometry3d::new(vec3(0.0, 1.0, 0.0), Quat::IDENTITY);
627        // assert_abs_diff_eq!(iso1.inverse_mul(iso2), expected);
628    }
629
630    #[test]
631    fn identity_2d() {
632        let iso = Isometry2d::new(vec2(-1.0, -0.5), Rot2::degrees(75.0));
633        // assert_abs_diff_eq!(Isometry2d::IDENTITY * iso, iso);
634        // assert_abs_diff_eq!(iso * Isometry2d::IDENTITY, iso);
635    }
636
637    #[test]
638    fn identity_3d() {
639        let iso = Isometry3d::new(vec3(-1.0, 2.5, 3.3), Quat::from_rotation_z(FRAC_PI_3));
640        // assert_abs_diff_eq!(Isometry3d::IDENTITY * iso, iso);
641        // assert_abs_diff_eq!(iso * Isometry3d::IDENTITY, iso);
642    }
643
644    #[test]
645    fn inverse_2d() {
646        let iso = Isometry2d::new(vec2(-1.0, -0.5), Rot2::degrees(75.0));
647        let inv = iso.inverse();
648        // assert_abs_diff_eq!(iso * inv, Isometry2d::IDENTITY);
649        // assert_abs_diff_eq!(inv * iso, Isometry2d::IDENTITY);
650    }
651
652    #[test]
653    fn inverse_3d() {
654        let iso = Isometry3d::new(vec3(-1.0, 2.5, 3.3), Quat::from_rotation_z(FRAC_PI_3));
655        let inv = iso.inverse();
656        // assert_abs_diff_eq!(iso * inv, Isometry3d::IDENTITY);
657        // assert_abs_diff_eq!(inv * iso, Isometry3d::IDENTITY);
658    }
659
660    #[test]
661    fn transform_2d() {
662        let iso = Isometry2d::new(vec2(0.5, -0.5), Rot2::FRAC_PI_2);
663        let point = vec2(1.0, 1.0);
664        // assert_abs_diff_eq!(vec2(-0.5, 0.5), iso * point);
665    }
666
667    #[test]
668    fn inverse_transform_2d() {
669        let iso = Isometry2d::new(vec2(0.5, -0.5), Rot2::FRAC_PI_2);
670        let point = vec2(-0.5, 0.5);
671        // assert_abs_diff_eq!(vec2(1.0, 1.0), iso.inverse_transform_point(point));
672    }
673
674    #[test]
675    fn transform_3d() {
676        let iso = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_y(FRAC_PI_2));
677        let point = vec3(1.0, 1.0, 1.0);
678        // assert_abs_diff_eq!(vec3(2.0, 1.0, -1.0), iso * point);
679    }
680
681    #[test]
682    fn inverse_transform_3d() {
683        let iso = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_y(FRAC_PI_2));
684        let point = vec3(2.0, 1.0, -1.0);
685        // assert_abs_diff_eq!(vec3a(1.0, 1.0, 1.0), iso.inverse_transform_point(point));
686    }
687}