1#[cfg(feature = "f64")]
4use crate::rot2::DRot2;
5use crate::rot2::Rot2;
6use core::ops::{Mul, MulAssign};
7
8macro_rules! impl_pose2 {
10 ($Pose2:ident, $Rot2:ident, $Real:ty, $Vec2:ty, $Vec3:ty, $Mat2:ty, $Mat3:ty $(, #[$attr:meta])*) => {
11 #[doc = concat!("A 2D pose (rotation + translation), representing a rigid body transformation (", stringify!($Real), " precision).")]
12 #[derive(Copy, Clone, Debug, PartialEq)]
13 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14 #[cfg_attr(feature = "rkyv", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]
15 #[repr(C)]
16 $(#[$attr])*
17 pub struct $Pose2 {
18 pub rotation: $Rot2,
20 pub translation: $Vec2,
22 }
23
24 impl $Pose2 {
25 pub const IDENTITY: Self = Self {
27 rotation: $Rot2::IDENTITY,
28 translation: <$Vec2>::ZERO,
29 };
30
31 #[inline]
33 pub fn identity() -> Self {
34 Self::IDENTITY
35 }
36
37 #[inline]
39 pub fn from_translation(translation: $Vec2) -> Self {
40 Self {
41 rotation: $Rot2::IDENTITY,
42 translation,
43 }
44 }
45
46 #[inline]
48 pub fn translation(x: $Real, y: $Real) -> Self {
49 Self::from_translation(<$Vec2>::new(x, y))
50 }
51
52 #[inline]
54 pub fn from_rotation(rotation: $Rot2) -> Self {
55 Self {
56 rotation,
57 translation: <$Vec2>::ZERO,
58 }
59 }
60
61 #[inline]
63 pub fn rotation(angle: $Real) -> Self {
64 Self::from_rotation(<$Rot2>::new(angle))
65 }
66
67 #[inline]
69 pub fn from_parts(translation: $Vec2, rotation: $Rot2) -> Self {
70 Self {
71 rotation,
72 translation,
73 }
74 }
75
76 #[inline]
78 pub fn new(translation: $Vec2, angle: $Real) -> Self {
79 Self {
80 rotation: $Rot2::new(angle),
81 translation,
82 }
83 }
84
85 #[inline]
87 pub fn prepend_translation(self, translation: $Vec2) -> Self {
88 Self {
89 rotation: self.rotation,
90 translation: self.translation + self.rotation * translation,
91 }
92 }
93
94 #[inline]
96 pub fn append_translation(self, translation: $Vec2) -> Self {
97 Self {
98 rotation: self.rotation,
99 translation: self.translation + translation,
100 }
101 }
102
103 #[inline]
105 pub fn inverse(&self) -> Self {
106 let inv_rot = self.rotation.inverse();
107 Self {
108 rotation: inv_rot,
109 translation: inv_rot.transform_vector(-self.translation),
110 }
111 }
112
113 #[inline]
115 pub fn inv_mul(&self, rhs: &Self) -> Self {
116 self.inverse() * *rhs
117 }
118
119 #[inline]
123 pub fn transform_point(&self, p: $Vec2) -> $Vec2 {
124 self.rotation.transform_vector(p) + self.translation
125 }
126
127 #[inline]
131 pub fn transform_vector(&self, v: $Vec2) -> $Vec2 {
132 self.rotation.transform_vector(v)
133 }
134
135 #[inline]
137 pub fn inverse_transform_point(&self, p: $Vec2) -> $Vec2 {
138 self.rotation.inverse_transform_vector(p - self.translation)
139 }
140
141 #[inline]
143 pub fn inverse_transform_vector(&self, v: $Vec2) -> $Vec2 {
144 self.rotation.inverse_transform_vector(v)
145 }
146
147 #[inline]
151 pub fn lerp(&self, other: &Self, t: $Real) -> Self {
152 Self {
153 rotation: self.rotation.slerp(&other.rotation, t),
154 translation: self.translation.lerp(other.translation, t),
155 }
156 }
157
158 #[inline]
160 pub fn is_finite(&self) -> bool {
161 self.rotation.is_finite() && self.translation.is_finite()
162 }
163
164 #[inline]
166 pub fn is_nan(&self) -> bool {
167 self.rotation.is_nan() || self.translation.is_nan()
168 }
169
170 #[inline]
172 pub fn to_mat3(&self) -> $Mat3 {
173 let (sin, cos) = (self.rotation.sin(), self.rotation.cos());
174 <$Mat3>::from_cols(
175 <$Vec3>::new(cos, sin, 0.0),
176 <$Vec3>::new(-sin, cos, 0.0),
177 <$Vec3>::new(self.translation.x, self.translation.y, 1.0),
178 )
179 }
180
181 #[inline]
186 pub fn from_mat3(mat: $Mat3) -> Self {
187 let rotation = $Rot2::from_mat(<$Mat2>::from_cols(
188 mat.x_axis.truncate(),
189 mat.y_axis.truncate(),
190 ));
191 let translation = mat.z_axis.truncate();
192 Self { rotation, translation }
193 }
194 }
195
196 impl Mul<$Pose2> for $Pose2 {
198 type Output = Self;
199
200 #[inline]
201 fn mul(self, rhs: Self) -> Self::Output {
202 Self {
203 rotation: self.rotation * rhs.rotation,
204 translation: self.translation + self.rotation.transform_vector(rhs.translation),
205 }
206 }
207 }
208
209 impl MulAssign<$Pose2> for $Pose2 {
210 #[inline]
211 fn mul_assign(&mut self, rhs: Self) {
212 *self = *self * rhs;
213 }
214 }
215
216 impl Mul<$Vec2> for $Pose2 {
218 type Output = $Vec2;
219
220 #[inline]
221 fn mul(self, rhs: $Vec2) -> Self::Output {
222 self.transform_point(rhs)
223 }
224 }
225
226 impl Mul<$Vec2> for &$Pose2 {
227 type Output = $Vec2;
228
229 #[inline]
230 fn mul(self, rhs: $Vec2) -> Self::Output {
231 self.transform_point(rhs)
232 }
233 }
234
235 impl Mul<&$Vec2> for $Pose2 {
236 type Output = $Vec2;
237
238 #[inline]
239 fn mul(self, rhs: &$Vec2) -> Self::Output {
240 self.transform_point(*rhs)
241 }
242 }
243
244 impl Default for $Pose2 {
245 fn default() -> Self {
246 Self::IDENTITY
247 }
248 }
249
250 impl Mul<&$Pose2> for &$Pose2 {
252 type Output = $Pose2;
253
254 #[inline]
255 fn mul(self, rhs: &$Pose2) -> Self::Output {
256 *self * *rhs
257 }
258 }
259
260 impl Mul<&$Pose2> for $Pose2 {
262 type Output = $Pose2;
263
264 #[inline]
265 fn mul(self, rhs: &$Pose2) -> Self::Output {
266 self * *rhs
267 }
268 }
269
270 impl Mul<$Pose2> for &$Pose2 {
272 type Output = $Pose2;
273
274 #[inline]
275 fn mul(self, rhs: $Pose2) -> Self::Output {
276 *self * rhs
277 }
278 }
279
280 impl Mul<$Pose2> for $Rot2 {
281 type Output = $Pose2;
282
283 #[inline]
284 fn mul(self, rhs: $Pose2) -> Self::Output {
285 $Pose2 {
286 translation: self * rhs.translation,
287 rotation: self * rhs.rotation,
288 }
289 }
290 }
291
292 impl Mul<$Rot2> for $Pose2 {
293 type Output = $Pose2;
294
295 #[inline]
296 fn mul(self, rhs: $Rot2) -> Self::Output {
297 $Pose2 {
298 translation: self.translation,
299 rotation: self.rotation * rhs,
300 }
301 }
302 }
303
304 impl MulAssign<$Rot2> for $Pose2 {
305 #[inline]
306 fn mul_assign(&mut self, rhs: $Rot2) {
307 *self = *self * rhs;
308 }
309 }
310
311 impl From<$Rot2> for $Pose2 {
312 #[inline]
313 fn from(rotation: $Rot2) -> Self {
314 Self::from_rotation(rotation)
315 }
316 }
317
318 impl From<($Vec2, $Real)> for $Pose2 {
319 #[inline]
320 fn from((translation, rotation): ($Vec2, $Real)) -> Self {
321 Self {
322 translation,
323 rotation: $Rot2::new(rotation),
324 }
325 }
326 }
327
328 impl From<$Pose2> for ($Vec2, $Real) {
329 #[inline]
330 fn from(pose: $Pose2) -> ($Vec2, $Real) {
331 (pose.translation, pose.rotation.angle())
332 }
333 }
334
335 #[cfg(feature = "approx")]
336 impl approx::AbsDiffEq for $Pose2 {
337 type Epsilon = $Real;
338
339 fn default_epsilon() -> Self::Epsilon {
340 <$Real>::EPSILON
341 }
342
343 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
344 self.rotation.abs_diff_eq(&other.rotation, epsilon)
345 && self.translation.abs_diff_eq(other.translation, epsilon)
346 }
347 }
348
349 #[cfg(feature = "approx")]
350 impl approx::RelativeEq for $Pose2 {
351 fn default_max_relative() -> Self::Epsilon {
352 <$Real>::EPSILON
353 }
354
355 fn relative_eq(
356 &self,
357 other: &Self,
358 epsilon: Self::Epsilon,
359 max_relative: Self::Epsilon,
360 ) -> bool {
361 self.rotation.relative_eq(&other.rotation, epsilon, max_relative)
362 && self.translation.abs_diff_eq(other.translation, epsilon.max(max_relative))
363 }
364 }
365 };
366}
367
368impl_pose2!(
369 Pose2,
370 Rot2,
371 f32,
372 glam::Vec2,
373 glam::Vec3,
374 glam::Mat2,
375 glam::Mat3,
376 #[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
377);
378#[cfg(feature = "f64")]
379impl_pose2!(
380 DPose2,
381 DRot2,
382 f64,
383 glam::DVec2,
384 glam::DVec3,
385 glam::DMat2,
386 glam::DMat3
387);
388
389#[cfg(feature = "f64")]
391impl From<Pose2> for DPose2 {
392 #[inline]
393 fn from(p: Pose2) -> Self {
394 Self {
395 rotation: p.rotation.into(),
396 translation: p.translation.into(),
397 }
398 }
399}
400
401#[cfg(feature = "f64")]
402impl From<DPose2> for Pose2 {
403 #[inline]
404 fn from(p: DPose2) -> Self {
405 Self {
406 rotation: p.rotation.into(),
407 translation: p.translation.as_vec2(),
408 }
409 }
410}
411
412#[cfg(feature = "nalgebra")]
414mod nalgebra_conv {
415 use super::*;
416
417 impl From<Pose2> for nalgebra::Isometry2<f32> {
418 fn from(p: Pose2) -> Self {
419 nalgebra::Isometry2 {
420 translation: p.translation.into(),
421 rotation: p.rotation.into(),
422 }
423 }
424 }
425
426 impl From<nalgebra::Isometry2<f32>> for Pose2 {
427 fn from(iso: nalgebra::Isometry2<f32>) -> Self {
428 Self {
429 rotation: Rot2::from_cos_sin_unchecked(iso.rotation.re, iso.rotation.im),
430 translation: glam::Vec2::new(iso.translation.x, iso.translation.y),
431 }
432 }
433 }
434
435 #[cfg(feature = "f64")]
436 impl From<DPose2> for nalgebra::Isometry2<f64> {
437 fn from(p: DPose2) -> Self {
438 nalgebra::Isometry2 {
439 translation: p.translation.into(),
440 rotation: p.rotation.into(),
441 }
442 }
443 }
444
445 #[cfg(feature = "f64")]
446 impl From<nalgebra::Isometry2<f64>> for DPose2 {
447 fn from(iso: nalgebra::Isometry2<f64>) -> Self {
448 Self {
449 rotation: DRot2::from_cos_sin_unchecked(iso.rotation.re, iso.rotation.im),
450 translation: glam::DVec2::new(iso.translation.x, iso.translation.y),
451 }
452 }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459 use approx::assert_relative_eq;
460 use core::f32::consts::PI;
461
462 #[test]
463 fn test_pose2_identity() {
464 let p = Pose2::IDENTITY;
465 assert_eq!(p.rotation, Rot2::IDENTITY);
466 assert_eq!(p.translation, glam::Vec2::ZERO);
467 assert!(p.is_finite());
468 }
469
470 #[test]
471 fn test_pose2_from_translation() {
472 let t = glam::Vec2::new(1.0, 2.0);
473 let p = Pose2::from_translation(t);
474 assert_eq!(p.translation, t);
475 assert_eq!(p.rotation, Rot2::IDENTITY);
476 }
477
478 #[test]
479 fn test_pose2_from_rotation_translation() {
480 let t = glam::Vec2::new(1.0, 2.0);
481 let r = Rot2::new(PI / 4.0);
482 let p = Pose2::from_parts(t, r);
483 assert_eq!(p.translation, t);
484 assert_eq!(p.rotation, r);
485 }
486
487 #[test]
488 fn test_pose2_transform_point() {
489 let p = Pose2::new(glam::Vec2::new(1.0, 0.0), PI / 2.0);
490 let point = glam::Vec2::new(1.0, 0.0);
491 let result = p.transform_point(point);
492 assert_relative_eq!(result.x, 1.0, epsilon = 1e-6);
493 assert_relative_eq!(result.y, 1.0, epsilon = 1e-6);
494 let result2 = p * point;
496 assert_relative_eq!(result2.x, 1.0, epsilon = 1e-6);
497 assert_relative_eq!(result2.y, 1.0, epsilon = 1e-6);
498 }
499
500 #[test]
501 fn test_pose2_transform_vector() {
502 let p = Pose2::new(glam::Vec2::new(100.0, 200.0), PI / 2.0);
503 let vector = glam::Vec2::new(1.0, 0.0);
504 let result = p.transform_vector(vector);
506 assert_relative_eq!(result.x, 0.0, epsilon = 1e-6);
507 assert_relative_eq!(result.y, 1.0, epsilon = 1e-6);
508 }
509
510 #[test]
511 fn test_pose2_inverse() {
512 let p = Pose2::new(glam::Vec2::new(1.0, 2.0), 0.5);
513 let inv = p.inverse();
514 let identity = p * inv;
515 assert_relative_eq!(identity.rotation.re, 1.0, epsilon = 1e-6);
516 assert_relative_eq!(identity.rotation.im, 0.0, epsilon = 1e-6);
517 assert_relative_eq!(identity.translation.x, 0.0, epsilon = 1e-6);
518 assert_relative_eq!(identity.translation.y, 0.0, epsilon = 1e-6);
519 }
520
521 #[test]
522 fn test_pose2_to_from_mat3() {
523 let p = Pose2::new(glam::Vec2::new(1.0, 2.0), PI / 4.0);
524 let m = p.to_mat3();
525 let p2 = Pose2::from_mat3(m);
526 assert_relative_eq!(p.rotation.re, p2.rotation.re, epsilon = 1e-6);
527 assert_relative_eq!(p.rotation.im, p2.rotation.im, epsilon = 1e-6);
528 assert_relative_eq!(p.translation.x, p2.translation.x, epsilon = 1e-6);
529 assert_relative_eq!(p.translation.y, p2.translation.y, epsilon = 1e-6);
530 }
531
532 #[test]
533 fn test_pose2_lerp_across_branch_cut() {
534 let p1 = Pose2::new(glam::Vec2::new(0.0, 0.0), 3.13);
535 let p2 = Pose2::new(glam::Vec2::new(2.0, 4.0), 3.15);
536 let mid = p1.lerp(&p2, 0.5);
537 assert_relative_eq!(mid.rotation.angle(), 3.14, epsilon = 1e-6);
538 assert_relative_eq!(mid.translation.x, 1.0, epsilon = 1e-6);
539 assert_relative_eq!(mid.translation.y, 2.0, epsilon = 1e-6);
540 }
541
542 #[cfg(feature = "f64")]
543 #[test]
544 fn test_dpose2_new() {
545 let p = DPose2::new(glam::DVec2::new(1.0, 2.0), core::f64::consts::PI / 4.0);
546 assert_relative_eq!(p.translation.x, 1.0, epsilon = 1e-10);
547 assert_relative_eq!(p.translation.y, 2.0, epsilon = 1e-10);
548 }
549
550 #[test]
551 fn test_convert_back_to_equal_pose2() {
552 let (t1, r1) = (glam::Vec2::new(1.0, 2.0), PI / 4.0);
553 let p: Pose2 = (t1, r1).into();
554 let (t2, r2) = p.into();
555
556 assert_relative_eq!(t1, t2, epsilon = 1e-6);
557 assert_relative_eq!(r1, r2, epsilon = 1e-6);
558 }
559}