Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/isometry.rs
6595 views
1
//! Isometry types for expressing rigid motions in two and three dimensions.
2
3
use crate::{Affine2, Affine3, Affine3A, Dir2, Dir3, Mat3, Mat3A, Quat, Rot2, Vec2, Vec3, Vec3A};
4
use core::ops::Mul;
5
6
#[cfg(feature = "approx")]
7
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
8
9
#[cfg(feature = "bevy_reflect")]
10
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
11
#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
12
use 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, Clone)
92
)]
93
#[cfg_attr(
94
all(feature = "serialize", feature = "bevy_reflect"),
95
reflect(Serialize, Deserialize)
96
)]
97
pub 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
104
impl 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 const 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 const 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 const 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 const 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
185
impl 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
195
impl From<Vec2> for Isometry2d {
196
#[inline]
197
fn from(translation: Vec2) -> Self {
198
Isometry2d::from_translation(translation)
199
}
200
}
201
202
impl From<Rot2> for Isometry2d {
203
#[inline]
204
fn from(rotation: Rot2) -> Self {
205
Isometry2d::from_rotation(rotation)
206
}
207
}
208
209
impl 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
221
impl 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
230
impl 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")]
240
impl 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")]
254
impl 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")]
274
impl 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, Clone)
370
)]
371
#[cfg_attr(
372
all(feature = "serialize", feature = "bevy_reflect"),
373
reflect(Serialize, Deserialize)
374
)]
375
pub 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
382
impl 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 const 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 const 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
463
impl 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
473
impl 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
483
impl From<Vec3> for Isometry3d {
484
#[inline]
485
fn from(translation: Vec3) -> Self {
486
Isometry3d::from_translation(translation)
487
}
488
}
489
490
impl From<Vec3A> for Isometry3d {
491
#[inline]
492
fn from(translation: Vec3A) -> Self {
493
Isometry3d::from_translation(translation)
494
}
495
}
496
497
impl From<Quat> for Isometry3d {
498
#[inline]
499
fn from(rotation: Quat) -> Self {
500
Isometry3d::from_rotation(rotation)
501
}
502
}
503
504
impl 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
516
impl 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
525
impl 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
534
impl 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")]
544
impl 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")]
558
impl 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")]
578
impl 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)]
592
#[cfg(feature = "approx")]
593
mod tests {
594
use super::*;
595
use crate::{vec2, vec3, vec3a};
596
use approx::assert_abs_diff_eq;
597
use core::f32::consts::{FRAC_PI_2, FRAC_PI_3};
598
599
#[test]
600
fn mul_2d() {
601
let iso1 = Isometry2d::new(vec2(1.0, 0.0), Rot2::FRAC_PI_2);
602
let iso2 = Isometry2d::new(vec2(0.0, 1.0), Rot2::FRAC_PI_2);
603
let expected = Isometry2d::new(vec2(0.0, 0.0), Rot2::PI);
604
assert_abs_diff_eq!(iso1 * iso2, expected);
605
}
606
607
#[test]
608
fn inverse_mul_2d() {
609
let iso1 = Isometry2d::new(vec2(1.0, 0.0), Rot2::FRAC_PI_2);
610
let iso2 = Isometry2d::new(vec2(0.0, 0.0), Rot2::PI);
611
let expected = Isometry2d::new(vec2(0.0, 1.0), Rot2::FRAC_PI_2);
612
assert_abs_diff_eq!(iso1.inverse_mul(iso2), expected);
613
}
614
615
#[test]
616
fn mul_3d() {
617
let iso1 = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_x(FRAC_PI_2));
618
let iso2 = Isometry3d::new(vec3(0.0, 1.0, 0.0), Quat::IDENTITY);
619
let expected = Isometry3d::new(vec3(1.0, 0.0, 1.0), Quat::from_rotation_x(FRAC_PI_2));
620
assert_abs_diff_eq!(iso1 * iso2, expected);
621
}
622
623
#[test]
624
fn inverse_mul_3d() {
625
let iso1 = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_x(FRAC_PI_2));
626
let iso2 = Isometry3d::new(vec3(1.0, 0.0, 1.0), Quat::from_rotation_x(FRAC_PI_2));
627
let expected = Isometry3d::new(vec3(0.0, 1.0, 0.0), Quat::IDENTITY);
628
assert_abs_diff_eq!(iso1.inverse_mul(iso2), expected);
629
}
630
631
#[test]
632
fn identity_2d() {
633
let iso = Isometry2d::new(vec2(-1.0, -0.5), Rot2::degrees(75.0));
634
assert_abs_diff_eq!(Isometry2d::IDENTITY * iso, iso);
635
assert_abs_diff_eq!(iso * Isometry2d::IDENTITY, iso);
636
}
637
638
#[test]
639
fn identity_3d() {
640
let iso = Isometry3d::new(vec3(-1.0, 2.5, 3.3), Quat::from_rotation_z(FRAC_PI_3));
641
assert_abs_diff_eq!(Isometry3d::IDENTITY * iso, iso);
642
assert_abs_diff_eq!(iso * Isometry3d::IDENTITY, iso);
643
}
644
645
#[test]
646
fn inverse_2d() {
647
let iso = Isometry2d::new(vec2(-1.0, -0.5), Rot2::degrees(75.0));
648
let inv = iso.inverse();
649
assert_abs_diff_eq!(iso * inv, Isometry2d::IDENTITY);
650
assert_abs_diff_eq!(inv * iso, Isometry2d::IDENTITY);
651
}
652
653
#[test]
654
fn inverse_3d() {
655
let iso = Isometry3d::new(vec3(-1.0, 2.5, 3.3), Quat::from_rotation_z(FRAC_PI_3));
656
let inv = iso.inverse();
657
assert_abs_diff_eq!(iso * inv, Isometry3d::IDENTITY);
658
assert_abs_diff_eq!(inv * iso, Isometry3d::IDENTITY);
659
}
660
661
#[test]
662
fn transform_2d() {
663
let iso = Isometry2d::new(vec2(0.5, -0.5), Rot2::FRAC_PI_2);
664
let point = vec2(1.0, 1.0);
665
assert_abs_diff_eq!(vec2(-0.5, 0.5), iso * point);
666
}
667
668
#[test]
669
fn inverse_transform_2d() {
670
let iso = Isometry2d::new(vec2(0.5, -0.5), Rot2::FRAC_PI_2);
671
let point = vec2(-0.5, 0.5);
672
assert_abs_diff_eq!(vec2(1.0, 1.0), iso.inverse_transform_point(point));
673
}
674
675
#[test]
676
fn transform_3d() {
677
let iso = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_y(FRAC_PI_2));
678
let point = vec3(1.0, 1.0, 1.0);
679
assert_abs_diff_eq!(vec3(2.0, 1.0, -1.0), iso * point);
680
}
681
682
#[test]
683
fn inverse_transform_3d() {
684
let iso = Isometry3d::new(vec3(1.0, 0.0, 0.0), Quat::from_rotation_y(FRAC_PI_2));
685
let point = vec3(2.0, 1.0, -1.0);
686
assert_abs_diff_eq!(vec3a(1.0, 1.0, 1.0), iso.inverse_transform_point(point));
687
}
688
}
689
690