1use 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#[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 pub rotation: Rot2,
100 pub translation: Vec2,
102}
103
104impl Isometry2d {
105 pub const IDENTITY: Self = Isometry2d {
107 rotation: Rot2::IDENTITY,
108 translation: Vec2::ZERO,
109 };
110
111 #[inline]
113 pub fn new(translation: Vec2, rotation: Rot2) -> Self {
114 Isometry2d {
115 rotation,
116 translation,
117 }
118 }
119
120 #[inline]
122 pub fn from_rotation(rotation: Rot2) -> Self {
123 Isometry2d {
124 rotation,
125 translation: Vec2::ZERO,
126 }
127 }
128
129 #[inline]
131 pub fn from_translation(translation: Vec2) -> Self {
132 Isometry2d {
133 rotation: Rot2::IDENTITY,
134 translation,
135 }
136 }
137
138 #[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 #[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 #[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 #[inline]
170 pub fn transform_point(&self, point: Vec2) -> Vec2 {
171 self.rotation * point + self.translation
172 }
173
174 #[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#[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 pub rotation: Quat,
378 pub translation: Vec3A,
380}
381
382impl Isometry3d {
383 pub const IDENTITY: Self = Isometry3d {
385 rotation: Quat::IDENTITY,
386 translation: Vec3A::ZERO,
387 };
388
389 #[inline]
391 pub fn new(translation: impl Into<Vec3A>, rotation: Quat) -> Self {
392 Isometry3d {
393 rotation,
394 translation: translation.into(),
395 }
396 }
397
398 #[inline]
400 pub fn from_rotation(rotation: Quat) -> Self {
401 Isometry3d {
402 rotation,
403 translation: Vec3A::ZERO,
404 }
405 }
406
407 #[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 #[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 #[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 #[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 #[inline]
448 pub fn transform_point(&self, point: impl Into<Vec3A>) -> Vec3A {
449 self.rotation * point.into() + self.translation
450 }
451
452 #[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 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 }
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 }
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 }
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 }
629
630 #[test]
631 fn identity_2d() {
632 let iso = Isometry2d::new(vec2(-1.0, -0.5), Rot2::degrees(75.0));
633 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
687}