1use std::ops::Mul;
50
51use nalgebra::Rotation3;
52
53use super::exts::Vec3Ext;
54use super::{
55 ArticulatedBodyInertia, Mat3, SpatialForceVector, SpatialMotionVector, UnitQuat, UnitVec3, Vec3,
56};
57use crate::exts::MatrixExt;
58use crate::{Real, SMatrix, VEC3_ZERO, unit_quat};
59
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62#[derive(Debug, Clone, Copy, PartialEq)]
63pub struct PlukerRotation(Rotation3<Real>);
64
65impl PlukerRotation {
66 #[inline]
73 pub fn from_axis_angle(axis: UnitVec3, angle: Real) -> Self {
74 Self(Rotation3::from_axis_angle(&axis, angle).inverse())
77 }
78
79 #[inline]
84 pub fn from_quat(quat: UnitQuat) -> Self {
85 Self(Rotation3::from(quat))
86 }
87
88 #[inline]
90 pub fn into_quat(self) -> UnitQuat {
91 self.0.into()
92 }
93
94 #[inline]
96 pub fn into_matrix(self) -> Mat3 {
97 self.0.into_inner()
98 }
99
100 #[inline]
102 pub fn identity() -> Self {
103 Self(Rotation3::identity())
104 }
105
106 #[inline]
111 #[must_use]
112 pub fn transpose(&self) -> Self {
113 Self(self.0.transpose())
114 }
115
116 #[inline]
120 pub fn matrix(&self) -> Mat3 {
121 self.0.into_inner()
122 }
123
124 #[inline]
128 pub fn inverse_transform_vector(&self, v: Vec3) -> Vec3 {
129 self.0.inverse_transform_vector(&v)
130 }
131
132 #[inline]
136 pub fn inverse_transform_unitvec(&self, v: UnitVec3) -> UnitVec3 {
137 self.0.inverse_transform_unit_vector(&v)
138 }
139
140 #[inline]
142 pub fn axis_angle(&self) -> Option<(UnitVec3, Real)> {
143 self.0.axis_angle().map(|(axis, angle)| (axis, -angle))
144 }
145
146 #[inline]
153 pub fn from_matrix_unchecked(matrix: Mat3) -> Self {
154 Self(Rotation3::from_matrix_unchecked(matrix))
155 }
156
157 #[inline]
161 pub fn as_slice(&self) -> &[Real] {
162 self.0.matrix().as_slice()
163 }
164}
165
166impl Mul<Vec3> for PlukerRotation {
167 type Output = Vec3;
168
169 #[inline]
170 fn mul(self, rhs: Vec3) -> Vec3 {
171 self.0 * rhs
172 }
173}
174
175impl Mul<Self> for PlukerRotation {
176 type Output = Self;
177
178 #[inline]
179 fn mul(self, rhs: Self) -> Self {
180 Self(self.0 * rhs.0)
181 }
182}
183
184impl Mul<Mat3> for PlukerRotation {
185 type Output = Mat3;
186
187 #[inline]
188 fn mul(self, rhs: Mat3) -> Mat3 {
189 self.0 * rhs
190 }
191}
192
193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195#[derive(Debug, Clone, Copy, PartialEq)]
196pub struct PluckerTransform {
197 pub rotation: PlukerRotation,
202
203 pub translation: Vec3,
208}
209
210impl Default for PluckerTransform {
211 #[inline]
212 fn default() -> Self {
213 Self {
214 rotation: PlukerRotation::identity(),
215 translation: Vec3::zeros(),
216 }
217 }
218}
219
220impl PluckerTransform {
221 #[inline]
225 pub fn identity() -> Self {
226 Self {
227 rotation: PlukerRotation::identity(),
228 translation: VEC3_ZERO,
229 }
230 }
231
232 #[inline]
235 pub fn from_pose(pose: Pose) -> Self {
236 Self {
237 rotation: PlukerRotation::from_quat(pose.rotation.inverse()),
241 translation: pose.translation,
242 }
243 }
244
245 #[inline]
247 pub fn any_nan(&self) -> bool {
248 self.rotation.matrix().any_nan() || self.translation.any_nan()
249 }
250
251 #[inline]
258 #[must_use]
259 pub fn mul_transform(&self, rhs: &PluckerTransform) -> PluckerTransform {
260 PluckerTransform {
262 rotation: self.rotation * rhs.rotation,
263 translation: rhs.translation + rhs.rotation.transpose() * self.translation,
264 }
265 }
266
267 #[inline]
273 #[must_use]
274 pub fn inverse(&self) -> PluckerTransform {
275 PluckerTransform {
277 rotation: self.rotation.transpose(),
278 translation: -(self.rotation * self.translation),
279 }
280 }
281
282 #[inline]
293 pub fn transform_motion_vec(&self, v: SpatialMotionVector) -> SpatialMotionVector {
294 let w = v.top;
296 let v = v.bottom;
297
298 let w1 = self.rotation * w;
299 let v1 = self.rotation * (v - self.translation.cross(&w));
300
301 SpatialMotionVector::from_pair(w1, v1)
302 }
303
304 #[inline]
315 pub fn inverse_transform_motion_vec(&self, v: SpatialMotionVector) -> SpatialMotionVector {
316 let w = v.top;
318 let v = v.bottom;
319
320 let e_transpose = self.rotation.transpose();
321
322 let w = e_transpose * w;
323 let v = e_transpose * v + self.translation.cross(&w);
324
325 SpatialMotionVector::from_pair(w, v)
326 }
327
328 #[inline]
339 pub fn transform_force_vec(&self, f: SpatialForceVector) -> SpatialForceVector {
340 let n = f.top;
343 let f = f.bottom;
344
345 let e = self.rotation;
346 let r = self.translation;
347
348 let n = e * (n - r.cross(&f));
349 let f = e * f;
350
351 SpatialForceVector::from_pair(n, f)
352 }
353
354 #[inline]
365 pub fn inverse_transform_force_vec(&self, f: SpatialForceVector) -> SpatialForceVector {
366 let n = f.top;
369 let f = f.bottom;
370
371 let e_transpose = self.rotation.transpose();
372
373 let f = e_transpose * f;
374 let n = e_transpose * n + self.translation.cross(&f);
375
376 SpatialForceVector::from_pair(n, f)
377 }
378
379 #[inline]
381 pub fn transform_point(&self, point: Vec3) -> Vec3 {
382 let v = point - self.translation;
383 self.rotation * v
384 }
385
386 #[inline]
392 pub fn transform_vec3(&self, vec3: Vec3) -> Vec3 {
393 self.rotation * vec3
394 }
395
396 #[inline]
398 pub fn inverse_transform_point(&self, point: Vec3) -> Vec3 {
399 let v = self.rotation.inverse_transform_vector(point);
400 v + self.translation
401 }
402
403 #[inline]
408 pub fn inverse_transform_vec3(&self, vec3: Vec3) -> Vec3 {
409 self.rotation.inverse_transform_vector(vec3)
410 }
411
412 #[inline]
417 pub fn inverse_transform_unitvec3(&self, vec3: UnitVec3) -> UnitVec3 {
418 self.rotation.inverse_transform_unitvec(vec3)
419 }
420
421 #[inline]
437 pub fn transform_articulated_inertia(
438 &self,
439 inertia: &ArticulatedBodyInertia,
440 ) -> ArticulatedBodyInertia {
441 let rotation = self.rotation.matrix();
442 let rotation_t = rotation.transpose();
443 let m = inertia.mass.mat3();
444 let r = self.translation.into_cross_matrix();
445 let i = inertia.top_left.mat3();
446 let h = inertia.h;
447
448 let new_m = rotation * m * rotation_t;
450 let new_h = rotation * (h - r * m) * rotation_t;
452 let new_inertia = rotation * (i - r * h.transpose() + (h - r * m) * r) * rotation_t;
454 ArticulatedBodyInertia {
455 top_left: new_inertia.into(),
456 h: new_h,
457 mass: new_m.into(),
458 }
459 }
460
461 #[inline]
473 pub fn matrix(&self) -> SMatrix<6, 6> {
474 let mut result = SMatrix::<6, 6>::zeros();
475
476 let rotation = self.rotation.matrix();
477
478 let bottom_left = -(rotation * self.translation.into_cross_matrix());
479
480 result.fixed_view_mut::<3, 3>(0, 0).copy_from(&rotation);
482 result.fixed_view_mut::<3, 3>(3, 3).copy_from(&rotation);
484
485 result.fixed_view_mut::<3, 3>(3, 0).copy_from(&bottom_left);
487
488 result
489 }
490
491 #[inline]
504 pub fn dual_matrix(&self) -> SMatrix<6, 6> {
505 let mut result = SMatrix::<6, 6>::zeros();
506
507 let rotation = self.rotation.matrix();
508 result.fixed_view_mut::<3, 3>(0, 0).copy_from(&rotation);
510 result.fixed_view_mut::<3, 3>(3, 3).copy_from(&rotation);
512
513 let top_right = -rotation * self.translation.into_cross_matrix();
514
515 result.fixed_view_mut::<3, 3>(0, 3).copy_from(&top_right);
516
517 result
518 }
519
520 #[inline]
532 pub fn is_near_identity(&self, epsilon: Real) -> bool {
533 self.rotation.0.angle() < epsilon && self.translation.iter().all(|x| x.abs() < epsilon)
534 }
535}
536
537impl Mul<Self> for PluckerTransform {
538 type Output = Self;
539
540 #[inline]
541 fn mul(self, rhs: Self) -> Self {
542 self.mul_transform(&rhs)
543 }
544}
545
546impl Mul<SpatialMotionVector> for PluckerTransform {
547 type Output = SpatialMotionVector;
548
549 #[inline]
550 fn mul(self, rhs: SpatialMotionVector) -> SpatialMotionVector {
551 self.transform_motion_vec(rhs)
552 }
553}
554
555impl Mul<SpatialForceVector> for PluckerTransform {
556 type Output = SpatialForceVector;
557
558 #[inline]
559 fn mul(self, rhs: SpatialForceVector) -> SpatialForceVector {
560 self.transform_force_vec(rhs)
561 }
562}
563
564impl Mul<&ArticulatedBodyInertia> for PluckerTransform {
565 type Output = ArticulatedBodyInertia;
566
567 #[inline]
568 fn mul(self, rhs: &ArticulatedBodyInertia) -> ArticulatedBodyInertia {
569 self.transform_articulated_inertia(rhs)
570 }
571}
572
573impl PluckerTransform {
574 #[inline]
582 pub fn pose(&self) -> Pose {
583 let quat: UnitQuat = self.rotation.0.into();
584 Pose {
585 rotation: quat.inverse(),
586 translation: self.translation,
587 }
588 }
589}
590
591#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597#[derive(Debug, Default, Clone, Copy)]
598pub struct Pose {
599 pub rotation: UnitQuat,
601 pub translation: Vec3,
603}
604
605impl Pose {
606 #[inline]
617 pub fn transform_point(&self, local: Vec3) -> Vec3 {
618 self.rotation * local + self.translation
619 }
620
621 #[inline]
633 pub fn transform_vec(&self, local: Vec3) -> Vec3 {
634 self.rotation * local
635 }
636
637 #[inline]
645 #[must_use]
646 pub fn inverse(&self) -> Pose {
647 let r = self.rotation.inverse();
648 let t = -self.translation;
649 Pose {
650 rotation: r,
651 translation: r * t,
652 }
653 }
654
655 #[inline]
666 pub fn is_identity(&self, epsilon: Real) -> bool {
667 self.rotation.angle() < epsilon && self.translation.iter().all(|x| x.abs() < epsilon)
668 }
669
670 #[inline]
672 pub fn into_array(self) -> [Real; 7] {
673 let p = self.translation;
674 let r = self.rotation;
675 [p.x, p.y, p.z, r.i, r.j, r.k, r.w]
676 }
677
678 #[inline]
680 pub fn from_array(arr: [Real; 7]) -> Self {
681 Pose {
682 translation: Vec3::new(arr[0], arr[1], arr[2]),
683 rotation: unit_quat(arr[3], arr[4], arr[5], arr[6]),
684 }
685 }
686
687 #[inline]
693 pub fn from_slice(arr: &[Real]) -> Self {
694 assert_eq!(arr.len(), 7);
695 Pose {
696 translation: Vec3::new(arr[0], arr[1], arr[2]),
697 rotation: unit_quat(arr[3], arr[4], arr[5], arr[6]),
698 }
699 }
700}
701
702impl From<Pose> for PluckerTransform {
703 #[inline]
704 fn from(pose: Pose) -> Self {
705 PluckerTransform::from_pose(pose)
706 }
707}
708
709impl Mul<Pose> for Pose {
710 type Output = Pose;
711
712 #[inline]
713 fn mul(self, rhs: Pose) -> Pose {
714 let rotation = self.rotation * rhs.rotation;
715 let translation = self.rotation * rhs.translation + self.translation;
716 Pose {
717 rotation,
718 translation,
719 }
720 }
721}
722
723#[cfg(feature = "approx")]
724mod approx_eq {
725
726 use crate::Real;
727
728 impl approx::AbsDiffEq for super::PlukerRotation {
729 type Epsilon = Real;
730
731 #[inline]
732 fn default_epsilon() -> Self::Epsilon {
733 Real::EPSILON
734 }
735
736 #[inline]
737 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
738 self.0.abs_diff_eq(&other.0, epsilon)
739 }
740 }
741
742 impl approx::RelativeEq for super::PlukerRotation {
743 #[inline]
744 fn default_max_relative() -> Self::Epsilon {
745 Real::default_max_relative()
746 }
747
748 #[inline]
749 fn relative_eq(
750 &self,
751 other: &Self,
752 epsilon: Self::Epsilon,
753 max_relative: Self::Epsilon,
754 ) -> bool {
755 self.0.relative_eq(&other.0, epsilon, max_relative)
756 }
757 }
758
759 impl approx::AbsDiffEq for super::PluckerTransform {
760 type Epsilon = Real;
761
762 #[inline]
763 fn default_epsilon() -> Self::Epsilon {
764 Real::EPSILON
765 }
766
767 #[inline]
768 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
769 self.rotation.abs_diff_eq(&other.rotation, epsilon)
770 & self.translation.abs_diff_eq(&other.translation, epsilon)
771 }
772 }
773
774 impl approx::RelativeEq for super::PluckerTransform {
775 #[inline]
776 fn default_max_relative() -> Self::Epsilon {
777 Real::default_max_relative()
778 }
779
780 #[inline]
781 fn relative_eq(
782 &self,
783 other: &Self,
784 epsilon: Self::Epsilon,
785 max_relative: Self::Epsilon,
786 ) -> bool {
787 self.rotation
788 .relative_eq(&other.rotation, epsilon, max_relative)
789 & self
790 .translation
791 .relative_eq(&other.translation, epsilon, max_relative)
792 }
793 }
794}
795
796#[cfg(test)]
797mod tests {
798 use approx::assert_relative_eq;
799
800 use super::PluckerTransform;
801 use crate::exts::Vec3Ext;
802 use crate::transform::PlukerRotation;
803 use crate::{
804 ArticulatedBodyInertia, Mat3, Real, RigidBodyInertia, SpatialMotionVector, SymmetricMat3,
805 UnitQuat, UnitVec3, Vec3, unit_vec3, vec3,
806 };
807
808 #[test]
809 fn test_transform_motion_vec() {
810 let transform = PluckerTransform {
811 rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
812 translation: vec3(1., 2., 3.),
813 };
814
815 let v0 = SpatialMotionVector::from_array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]);
816 let v1 = transform * v0;
817
818 assert_relative_eq!(v1.top, transform.rotation * v0.top);
819 assert_relative_eq!(
820 v1.bottom,
821 transform.rotation * (v0.bottom + v0.top.cross(&transform.translation))
822 );
823 }
824
825 #[test]
826 fn test_transform_mul() {
827 let a_to_b = PluckerTransform {
828 rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
829 translation: Vec3::zeros(),
830 };
831
832 let b_to_c = PluckerTransform {
833 rotation: PlukerRotation::identity(),
834 translation: vec3(1.0, 0.0, 0.0),
835 };
836
837 let a_to_c = b_to_c * a_to_b;
838
839 assert_relative_eq!(a_to_c.translation, vec3(0.0, 1.0, 0.0));
840 }
841
842 #[test]
843 fn test_transform_inverse() {
844 let transform = PluckerTransform {
845 rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
846 translation: vec3(1.0, 0.0, 0.0),
847 };
848
849 let transform_inv = transform.inverse();
850
851 let result = transform_inv * transform;
852
853 assert_relative_eq!(result.rotation.matrix(), Mat3::identity());
854 assert_relative_eq!(result.translation, Vec3::zeros());
855 }
856
857 #[test]
858 fn test_transform_to_matrix() {
859 let transform = PluckerTransform {
860 rotation: PlukerRotation::from_axis_angle(
861 UnitVec3::new_normalize(vec3(1.0, 2.0, 3.0)),
862 Real::to_radians(45.0),
863 ),
864 translation: vec3(1.0, 2.0, 3.0),
865 };
866
867 let matrix = transform.matrix();
868
869 let e = transform.rotation.matrix();
870 let rx = transform.translation.into_cross_matrix();
871
872 {
873 let top_left = matrix.fixed_view::<3, 3>(0, 0);
874 let bottom_right = matrix.fixed_view::<3, 3>(3, 3);
875 assert_relative_eq!(bottom_right, top_left);
876
877 let top_left: Vec<_> = top_left.iter().collect();
878 let e_data = e.iter().collect::<Vec<_>>();
879 assert_eq!(top_left, e_data);
880 }
881
882 {
883 let top_right = matrix.fixed_view::<3, 3>(0, 3);
884 top_right.iter().for_each(|&x| assert_relative_eq!(x, 0.0));
885 }
886
887 {
888 let bottom_left = matrix.fixed_view::<3, 3>(3, 0);
889
890 let expected = -e * rx;
891
892 bottom_left.iter().zip(expected.iter()).for_each(|(a, b)| {
893 assert_relative_eq!(a, b);
894 });
895 }
896
897 let motion_vec = SpatialMotionVector::from_pair(vec3(1.0, 2.0, 3.0), vec3(4.0, 5.0, 6.0));
898
899 assert_relative_eq!(
900 (transform * motion_vec).into_vec6(),
901 matrix * motion_vec.into_vec6(),
902 epsilon = 1e-6
903 );
904 }
905
906 #[test]
907 fn test_transform_to_dual_matrix() {
908 let transform = PluckerTransform {
909 rotation: PlukerRotation::from_axis_angle(
910 UnitVec3::new_normalize(vec3(1.0, 2.0, 3.0)),
911 Real::to_radians(90.0),
912 ),
913 translation: vec3(1.0, 2.0, 3.0),
914 };
915
916 let matrix = transform.matrix();
917 let dual_matrix = transform.dual_matrix();
918
919 assert_relative_eq!(
920 matrix.transpose().try_inverse().unwrap(),
921 dual_matrix,
922 epsilon = 1e-6
923 );
924 }
925
926 #[test]
927 fn test_transform_articulated_inertia() {
928 let transform = PluckerTransform {
929 rotation: PlukerRotation::from_axis_angle(Vec3::z_axis(), Real::to_radians(90.0)),
930 translation: vec3(1.0, 0.0, 0.0),
931 };
932
933 let inertia: ArticulatedBodyInertia =
934 RigidBodyInertia::new(1.0, vec3(1.0, 2.0, 3.0), SymmetricMat3::ONE).into();
935
936 let new_inertia = transform.transform_articulated_inertia(&inertia);
937
938 let dual_x = transform.dual_matrix();
939 let inverse_x = transform.matrix().try_inverse().unwrap();
940
941 let expected_inertia = dual_x * inertia.matrix() * inverse_x;
942
943 assert_relative_eq!(new_inertia.matrix(), expected_inertia, epsilon = 1e-6);
944
945 assert_relative_eq!(
946 new_inertia.mass.mat3(),
947 transform.rotation.matrix()
948 * inertia.mass.mat3()
949 * transform.rotation.matrix().transpose(),
950 epsilon = 1e-6
951 );
952 }
953
954 #[test]
955 fn test_pose_inverse() {
956 let pose = super::Pose {
957 rotation: UnitQuat::from_axis_angle(&unit_vec3(1., 2., 3.), Real::to_radians(36.0)),
958 translation: vec3(1.0, 2.0, 3.0),
959 };
960
961 let inv = pose.inverse();
962
963 let result = inv * pose;
964
965 assert_relative_eq!(result.rotation, UnitQuat::identity());
966 assert_relative_eq!(result.translation, Vec3::zeros());
967 }
968}