1use crate::approxeq::ApproxEq;
6use crate::trig::Trig;
7use crate::{Rotation3D, Transform3D, UnknownUnit, Vector3D};
8use num_traits::Float;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[repr(C)]
23pub struct RigidTransform3D<T, Src, Dst> {
24 pub rotation: Rotation3D<T, Src, Dst>,
25 pub translation: Vector3D<T, Dst>,
26}
27
28impl<T, Src, Dst> RigidTransform3D<T, Src, Dst> {
29 #[inline]
31 pub const fn new(rotation: Rotation3D<T, Src, Dst>, translation: Vector3D<T, Dst>) -> Self {
32 Self {
33 rotation,
34 translation,
35 }
36 }
37}
38
39impl<T: Copy, Src, Dst> RigidTransform3D<T, Src, Dst> {
40 pub fn cast_unit<Src2, Dst2>(&self) -> RigidTransform3D<T, Src2, Dst2> {
41 RigidTransform3D {
42 rotation: self.rotation.cast_unit(),
43 translation: self.translation.cast_unit(),
44 }
45 }
46}
47
48impl<T: Float + ApproxEq<T>, Src, Dst> RigidTransform3D<T, Src, Dst> {
49 #[inline]
51 pub fn identity() -> Self {
52 Self {
53 rotation: Rotation3D::identity(),
54 translation: Vector3D::zero(),
55 }
56 }
57
58 #[inline]
60 pub fn new_from_reversed(
61 translation: Vector3D<T, Src>,
62 rotation: Rotation3D<T, Src, Dst>,
63 ) -> Self {
64 let translation = rotation.transform_vector3d(translation);
74 Self {
75 rotation,
76 translation,
77 }
78 }
79
80 #[inline]
81 pub fn from_rotation(rotation: Rotation3D<T, Src, Dst>) -> Self {
82 Self {
83 rotation,
84 translation: Vector3D::zero(),
85 }
86 }
87
88 #[inline]
89 pub fn from_translation(translation: Vector3D<T, Dst>) -> Self {
90 Self {
91 translation,
92 rotation: Rotation3D::identity(),
93 }
94 }
95
96 #[inline]
100 pub fn decompose_reversed(&self) -> (Vector3D<T, Src>, Rotation3D<T, Src, Dst>) {
101 let translation = self.rotation.inverse().transform_vector3d(self.translation);
109 (translation, self.rotation)
110 }
111
112 #[inline]
117 pub fn then<Dst2>(
118 &self,
119 other: &RigidTransform3D<T, Dst, Dst2>,
120 ) -> RigidTransform3D<T, Src, Dst2> {
121 let t_prime = other.rotation.transform_vector3d(self.translation);
134 let r_prime = self.rotation.then(&other.rotation);
135 let t_prime2 = t_prime + other.translation;
136 RigidTransform3D {
137 rotation: r_prime,
138 translation: t_prime2,
139 }
140 }
141
142 #[inline]
144 pub fn inverse(&self) -> RigidTransform3D<T, Dst, Src> {
145 RigidTransform3D::new_from_reversed(-self.translation, self.rotation.inverse())
158 }
159
160 pub fn to_transform(&self) -> Transform3D<T, Src, Dst>
161 where
162 T: Trig,
163 {
164 self.rotation.to_transform().then(&self.translation.to_transform())
165 }
166
167 #[inline]
169 pub fn to_untyped(&self) -> RigidTransform3D<T, UnknownUnit, UnknownUnit> {
170 RigidTransform3D {
171 rotation: self.rotation.to_untyped(),
172 translation: self.translation.to_untyped(),
173 }
174 }
175
176 #[inline]
178 pub fn from_untyped(transform: &RigidTransform3D<T, UnknownUnit, UnknownUnit>) -> Self {
179 RigidTransform3D {
180 rotation: Rotation3D::from_untyped(&transform.rotation),
181 translation: Vector3D::from_untyped(transform.translation),
182 }
183 }
184}
185
186impl<T: Float + ApproxEq<T>, Src, Dst> From<Rotation3D<T, Src, Dst>>
187 for RigidTransform3D<T, Src, Dst>
188{
189 fn from(rot: Rotation3D<T, Src, Dst>) -> Self {
190 Self::from_rotation(rot)
191 }
192}
193
194impl<T: Float + ApproxEq<T>, Src, Dst> From<Vector3D<T, Dst>> for RigidTransform3D<T, Src, Dst> {
195 fn from(t: Vector3D<T, Dst>) -> Self {
196 Self::from_translation(t)
197 }
198}
199
200#[cfg(test)]
201mod test {
202 use super::RigidTransform3D;
203 use crate::default::{Rotation3D, Transform3D, Vector3D};
204
205 #[test]
206 fn test_rigid_construction() {
207 let translation = Vector3D::new(12.1, 17.8, -5.5);
208 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
209
210 let rigid = RigidTransform3D::new(rotation, translation);
211 assert!(rigid.to_transform().approx_eq(
212 &rotation.to_transform().then(&translation.to_transform())
213 ));
214
215 let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
216 assert!(rigid.to_transform().approx_eq(
217 &translation.to_transform().then(&rotation.to_transform())
218 ));
219 }
220
221 #[test]
222 fn test_rigid_decomposition() {
223 let translation = Vector3D::new(12.1, 17.8, -5.5);
224 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
225
226 let rigid = RigidTransform3D::new(rotation, translation);
227 let (t2, r2) = rigid.decompose_reversed();
228 assert!(rigid
229 .to_transform()
230 .approx_eq(&t2.to_transform().then(&r2.to_transform())));
231 }
232
233 #[test]
234 fn test_rigid_inverse() {
235 let translation = Vector3D::new(12.1, 17.8, -5.5);
236 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
237
238 let rigid = RigidTransform3D::new(rotation, translation);
239 let inverse = rigid.inverse();
240 assert!(rigid
241 .then(&inverse)
242 .to_transform()
243 .approx_eq(&Transform3D::identity()));
244 assert!(inverse
245 .to_transform()
246 .approx_eq(&rigid.to_transform().inverse().unwrap()));
247 }
248
249 #[test]
250 fn test_rigid_multiply() {
251 let translation = Vector3D::new(12.1, 17.8, -5.5);
252 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
253 let translation2 = Vector3D::new(9.3, -3.9, 1.1);
254 let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
255 let rigid = RigidTransform3D::new(rotation, translation);
256 let rigid2 = RigidTransform3D::new(rotation2, translation2);
257
258 assert!(rigid
259 .then(&rigid2)
260 .to_transform()
261 .approx_eq(&rigid.to_transform().then(&rigid2.to_transform())));
262 assert!(rigid2
263 .then(&rigid)
264 .to_transform()
265 .approx_eq(&rigid2.to_transform().then(&rigid.to_transform())));
266 }
267}