1use approxeq::ApproxEq;
2use num_traits::Float;
3use trig::Trig;
4use {TypedRotation3D, TypedTransform3D, TypedVector3D, UnknownUnit};
5
6#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[repr(C)]
17pub struct TypedRigidTransform3D<T, Src, Dst> {
18 pub rotation: TypedRotation3D<T, Src, Dst>,
19 pub translation: TypedVector3D<T, Dst>,
20}
21
22pub type RigidTransform3D<T> = TypedRigidTransform3D<T, UnknownUnit, UnknownUnit>;
23
24impl<T: Float + ApproxEq<T>, Src, Dst> TypedRigidTransform3D<T, Src, Dst> {
29 #[inline]
31 pub fn new(rotation: TypedRotation3D<T, Src, Dst>, translation: TypedVector3D<T, Dst>) -> Self {
32 Self {
33 rotation,
34 translation,
35 }
36 }
37
38 #[inline]
40 pub fn identity() -> Self {
41 Self {
42 rotation: TypedRotation3D::identity(),
43 translation: TypedVector3D::zero(),
44 }
45 }
46
47 #[inline]
49 pub fn new_from_reversed(
50 translation: TypedVector3D<T, Src>,
51 rotation: TypedRotation3D<T, Src, Dst>,
52 ) -> Self {
53 let translation = rotation.rotate_vector3d(&translation);
63 Self {
64 rotation,
65 translation,
66 }
67 }
68
69 #[inline]
70 pub fn from_rotation(rotation: TypedRotation3D<T, Src, Dst>) -> Self {
71 Self {
72 rotation,
73 translation: TypedVector3D::zero(),
74 }
75 }
76
77 #[inline]
78 pub fn from_translation(translation: TypedVector3D<T, Dst>) -> Self {
79 Self {
80 translation,
81 rotation: TypedRotation3D::identity(),
82 }
83 }
84
85 #[inline]
89 pub fn decompose_reversed(&self) -> (TypedVector3D<T, Src>, TypedRotation3D<T, Src, Dst>) {
90 let translation = self.rotation.inverse().rotate_vector3d(&self.translation);
98 (translation, self.rotation)
99 }
100
101 #[inline]
106 pub fn post_mul<Dst2>(
107 &self,
108 other: &TypedRigidTransform3D<T, Dst, Dst2>,
109 ) -> TypedRigidTransform3D<T, Src, Dst2> {
110 let t_prime = other
123 .rotation
124 .rotate_vector3d(&self.translation);
125 let r_prime = self.rotation.post_rotate(&other.rotation);
126 let t_prime2 = t_prime + other.translation;
127 TypedRigidTransform3D {
128 rotation: r_prime,
129 translation: t_prime2,
130 }
131 }
132
133 #[inline]
138 pub fn pre_mul<Src2>(
139 &self,
140 other: &TypedRigidTransform3D<T, Src2, Src>,
141 ) -> TypedRigidTransform3D<T, Src2, Dst> {
142 other.post_mul(&self)
143 }
144
145 #[inline]
147 pub fn inverse(&self) -> TypedRigidTransform3D<T, Dst, Src> {
148 TypedRigidTransform3D::new_from_reversed(
161 -self.translation,
162 self.rotation.inverse(),
163 )
164 }
165
166 pub fn to_transform(&self) -> TypedTransform3D<T, Src, Dst>
167 where
168 T: Trig,
169 {
170 self.translation
171 .to_transform()
172 .pre_mul(&self.rotation.to_transform())
173 }
174}
175
176impl<T: Float + ApproxEq<T>, Src, Dst> From<TypedRotation3D<T, Src, Dst>>
177 for TypedRigidTransform3D<T, Src, Dst>
178{
179 fn from(rot: TypedRotation3D<T, Src, Dst>) -> Self {
180 Self::from_rotation(rot)
181 }
182}
183
184impl<T: Float + ApproxEq<T>, Src, Dst> From<TypedVector3D<T, Dst>>
185 for TypedRigidTransform3D<T, Src, Dst>
186{
187 fn from(t: TypedVector3D<T, Dst>) -> Self {
188 Self::from_translation(t)
189 }
190}
191
192#[cfg(test)]
193mod test {
194 use super::RigidTransform3D;
195 use {Rotation3D, TypedTransform3D, Vector3D};
196
197 #[test]
198 fn test_rigid_construction() {
199 let translation = Vector3D::new(12.1, 17.8, -5.5);
200 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
201
202 let rigid = RigidTransform3D::new(rotation, translation);
203 assert!(rigid
204 .to_transform()
205 .approx_eq(&translation.to_transform().pre_mul(&rotation.to_transform())));
206
207 let rigid = RigidTransform3D::new_from_reversed(translation, rotation);
208 assert!(rigid.to_transform().approx_eq(
209 &translation
210 .to_transform()
211 .post_mul(&rotation.to_transform())
212 ));
213 }
214
215 #[test]
216 fn test_rigid_decomposition() {
217 let translation = Vector3D::new(12.1, 17.8, -5.5);
218 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
219
220 let rigid = RigidTransform3D::new(rotation, translation);
221 let (t2, r2) = rigid.decompose_reversed();
222 assert!(rigid
223 .to_transform()
224 .approx_eq(&t2.to_transform().post_mul(&r2.to_transform())));
225 }
226
227 #[test]
228 fn test_rigid_inverse() {
229 let translation = Vector3D::new(12.1, 17.8, -5.5);
230 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
231
232 let rigid = RigidTransform3D::new(rotation, translation);
233 let inverse = rigid.inverse();
234 assert!(rigid
235 .post_mul(&inverse)
236 .to_transform()
237 .approx_eq(&TypedTransform3D::identity()));
238 assert!(inverse
239 .to_transform()
240 .approx_eq(&rigid.to_transform().inverse().unwrap()));
241 }
242
243 #[test]
244 fn test_rigid_multiply() {
245 let translation = Vector3D::new(12.1, 17.8, -5.5);
246 let rotation = Rotation3D::unit_quaternion(0.5, -7.8, 2.2, 4.3);
247 let translation2 = Vector3D::new(9.3, -3.9, 1.1);
248 let rotation2 = Rotation3D::unit_quaternion(0.1, 0.2, 0.3, -0.4);
249 let rigid = RigidTransform3D::new(rotation, translation);
250 let rigid2 = RigidTransform3D::new(rotation2, translation2);
251
252 assert!(rigid
253 .post_mul(&rigid2)
254 .to_transform()
255 .approx_eq(&rigid.to_transform().post_mul(&rigid2.to_transform())));
256 assert!(rigid
257 .pre_mul(&rigid2)
258 .to_transform()
259 .approx_eq(&rigid.to_transform().pre_mul(&rigid2.to_transform())));
260 }
261}