1use fast_gicp_sys::ffi;
4use nalgebra::{Isometry3, Matrix3, Matrix4, Vector3};
5
6#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Transform3f {
9 pub matrix: [[f32; 4]; 4],
11}
12
13impl Transform3f {
14 #[cfg_attr(feature = "docs-only", doc = "```no_run")]
18 #[cfg_attr(not(feature = "docs-only"), doc = "```")]
19 pub fn identity() -> Self {
26 let ffi_transform = ffi::transform_identity();
27 Self::from_transform4f(&ffi_transform)
28 }
29
30 #[cfg_attr(feature = "docs-only", doc = "```no_run")]
34 #[cfg_attr(not(feature = "docs-only"), doc = "```")]
35 pub fn from_flat(data: &[f32; 16]) -> Self {
49 let mut matrix = [[0.0; 4]; 4];
50 for i in 0..4 {
51 for j in 0..4 {
52 matrix[i][j] = data[i * 4 + j];
53 }
54 }
55 Self { matrix }
56 }
57
58 pub fn to_flat(&self) -> [f32; 16] {
60 let mut data = [0.0; 16];
61 for i in 0..4 {
62 for j in 0..4 {
63 data[i * 4 + j] = self.matrix[i][j];
64 }
65 }
66 data
67 }
68
69 pub fn from_translation(x: f32, y: f32, z: f32) -> Self {
71 let transform4f = ffi::transform_from_translation(x, y, z);
72 Self::from_transform4f(&transform4f)
73 }
74
75 pub fn from_translation_array(translation: [f32; 3]) -> Self {
77 Self::from_translation(translation[0], translation[1], translation[2])
78 }
79
80 pub fn translation(&self) -> Vector3<f32> {
82 Vector3::new(self.matrix[0][3], self.matrix[1][3], self.matrix[2][3])
83 }
84
85 pub fn set_translation(&mut self, x: f32, y: f32, z: f32) {
87 self.matrix[0][3] = x;
88 self.matrix[1][3] = y;
89 self.matrix[2][3] = z;
90 }
91
92 pub fn rotation(&self) -> Matrix3<f32> {
94 Matrix3::new(
95 self.matrix[0][0],
96 self.matrix[0][1],
97 self.matrix[0][2],
98 self.matrix[1][0],
99 self.matrix[1][1],
100 self.matrix[1][2],
101 self.matrix[2][0],
102 self.matrix[2][1],
103 self.matrix[2][2],
104 )
105 }
106
107 pub fn set_rotation(&mut self, rotation: [[f32; 3]; 3]) {
109 for (i, row) in rotation.iter().enumerate() {
110 for (j, &val) in row.iter().enumerate() {
111 self.matrix[i][j] = val;
112 }
113 }
114 }
115
116 pub fn from_rotation_translation(rotation: [[f32; 3]; 3], translation: [f32; 3]) -> Self {
118 let mut transform = Self::identity();
119 transform.set_rotation(rotation);
120 transform.set_translation(translation[0], translation[1], translation[2]);
121 transform
122 }
123
124 pub fn from_parts(translation: Vector3<f32>, rotation: Matrix3<f32>) -> Self {
126 let mut transform = Self::identity();
127
128 for i in 0..3 {
130 for j in 0..3 {
131 transform.matrix[i][j] = rotation[(i, j)];
132 }
133 }
134
135 transform.matrix[0][3] = translation.x;
137 transform.matrix[1][3] = translation.y;
138 transform.matrix[2][3] = translation.z;
139
140 transform
141 }
142
143 pub fn from_rotation_matrix(rotation: &Matrix3<f32>) -> Self {
145 Self::from_parts(Vector3::zeros(), *rotation)
146 }
147
148 pub fn multiply(&self, other: &Transform3f) -> Self {
150 let a = self.as_transform4f();
151 let b = other.as_transform4f();
152 let result = ffi::transform_multiply(&a, &b);
153 Self::from_transform4f(&result)
154 }
155
156 pub fn compose(&self, other: &Transform3f) -> Transform3f {
158 self.multiply(other)
159 }
160
161 pub fn inverse(&self) -> Self {
163 let transform4f = self.as_transform4f();
164 let result = ffi::transform_inverse(&transform4f);
165 Self::from_transform4f(&result)
166 }
167
168 pub fn transform_point(&self, point: &Vector3<f32>) -> Vector3<f32> {
170 let x = point.x;
171 let y = point.y;
172 let z = point.z;
173 let tx = self.matrix[0][0] * x
174 + self.matrix[0][1] * y
175 + self.matrix[0][2] * z
176 + self.matrix[0][3];
177 let ty = self.matrix[1][0] * x
178 + self.matrix[1][1] * y
179 + self.matrix[1][2] * z
180 + self.matrix[1][3];
181 let tz = self.matrix[2][0] * x
182 + self.matrix[2][1] * y
183 + self.matrix[2][2] * z
184 + self.matrix[2][3];
185 Vector3::new(tx, ty, tz)
186 }
187
188 pub fn transform_point_array(&self, point: [f32; 3]) -> [f32; 3] {
190 let v = self.transform_point(&Vector3::new(point[0], point[1], point[2]));
191 [v.x, v.y, v.z]
192 }
193
194 pub fn from_isometry(isometry: &Isometry3<f32>) -> Self {
196 let matrix = isometry.to_homogeneous();
197 let mut flat = [0.0f32; 16];
198
199 for i in 0..4 {
201 for j in 0..4 {
202 flat[i * 4 + j] = matrix[(i, j)];
203 }
204 }
205
206 Self::from_flat(&flat)
207 }
208
209 pub fn to_isometry(&self) -> Isometry3<f32> {
211 let mut matrix = Matrix4::<f32>::zeros();
212
213 for i in 0..4 {
215 for j in 0..4 {
216 matrix[(i, j)] = self.matrix[i][j];
217 }
218 }
219
220 let rotation = matrix.fixed_view::<3, 3>(0, 0);
222 let translation = matrix.fixed_view::<3, 1>(0, 3);
223
224 Isometry3::from_parts(
225 nalgebra::Translation3::from(nalgebra::Vector3::new(
226 translation[(0, 0)],
227 translation[(1, 0)],
228 translation[(2, 0)],
229 )),
230 nalgebra::UnitQuaternion::from_matrix(&rotation.into()),
231 )
232 }
233
234 pub fn matrix_data(&self) -> &[[f32; 4]; 4] {
236 &self.matrix
237 }
238
239 pub fn to_matrix(&self) -> Matrix4<f32> {
241 Matrix4::from_fn(|i, j| self.matrix[i][j])
242 }
243
244 pub fn from_matrix(matrix: &Matrix4<f32>) -> Self {
246 let mut transform = Self::identity();
247 for i in 0..4 {
248 for j in 0..4 {
249 transform.matrix[i][j] = matrix[(i, j)];
250 }
251 }
252 transform
253 }
254
255 pub(crate) fn as_transform4f(&self) -> ffi::Transform4f {
257 ffi::Transform4f {
258 data: self.to_flat(),
259 }
260 }
261
262 pub(crate) fn from_transform4f(transform: &ffi::Transform4f) -> Self {
264 Self::from_flat(&transform.data)
265 }
266}
267
268impl Default for Transform3f {
269 fn default() -> Self {
270 Self::identity()
271 }
272}
273
274impl std::ops::Mul for Transform3f {
275 type Output = Transform3f;
276
277 fn mul(self, rhs: Transform3f) -> Self::Output {
278 self.multiply(&rhs)
279 }
280}
281
282impl std::ops::MulAssign for Transform3f {
283 fn mul_assign(&mut self, rhs: Transform3f) {
284 *self = self.multiply(&rhs);
285 }
286}
287
288impl From<nalgebra::Matrix4<f32>> for Transform3f {
290 fn from(m: nalgebra::Matrix4<f32>) -> Self {
291 let mut flat = [0.0f32; 16];
292 for i in 0..4 {
293 for j in 0..4 {
294 flat[i * 4 + j] = m[(i, j)];
295 }
296 }
297 Self::from_flat(&flat)
298 }
299}
300
301impl From<Transform3f> for nalgebra::Matrix4<f32> {
302 fn from(t: Transform3f) -> Self {
303 let mut matrix = nalgebra::Matrix4::zeros();
304 for i in 0..4 {
305 for j in 0..4 {
306 matrix[(i, j)] = t.matrix[i][j];
307 }
308 }
309 matrix
310 }
311}
312
313impl From<Isometry3<f32>> for Transform3f {
314 fn from(isometry: Isometry3<f32>) -> Self {
315 Self::from_isometry(&isometry)
316 }
317}
318
319impl From<&Transform3f> for Isometry3<f32> {
320 fn from(transform: &Transform3f) -> Self {
321 transform.to_isometry()
322 }
323}
324
325impl From<Transform3f> for Isometry3<f32> {
326 fn from(transform: Transform3f) -> Self {
327 transform.to_isometry()
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_identity() {
337 let identity = Transform3f::identity();
338 assert_eq!(identity.translation(), Vector3::new(0.0, 0.0, 0.0));
339
340 let point = Vector3::new(1.0, 2.0, 3.0);
341 let transformed = identity.transform_point(&point);
342 assert_eq!(transformed, point);
343 }
344
345 #[test]
346 fn test_translation() {
347 let transform = Transform3f::from_translation(1.0, 2.0, 3.0);
348 assert_eq!(transform.translation(), Vector3::new(1.0, 2.0, 3.0));
349
350 let point = Vector3::new(0.0, 0.0, 0.0);
351 let transformed = transform.transform_point(&point);
352 assert_eq!(transformed, Vector3::new(1.0, 2.0, 3.0));
353 }
354
355 #[test]
356 fn test_translation_array() {
357 let translation = [1.0, 2.0, 3.0];
358 let transform = Transform3f::from_translation_array(translation);
359 assert_eq!(transform.translation(), Vector3::new(1.0, 2.0, 3.0));
360
361 let rotation = transform.rotation();
362 let expected_rotation = Matrix3::identity();
363 assert_eq!(rotation, expected_rotation);
364 }
365
366 #[test]
367 fn test_inverse() {
368 let transform = Transform3f::from_translation(1.0, 2.0, 3.0);
369 let inverse = transform.inverse();
370 let identity = transform.multiply(&inverse);
371
372 let point = Vector3::new(5.0, 6.0, 7.0);
374 let original = identity.transform_point(&point);
375 assert!((original.x - point.x).abs() < 1e-6);
376 assert!((original.y - point.y).abs() < 1e-6);
377 assert!((original.z - point.z).abs() < 1e-6);
378 }
379
380 #[test]
381 fn test_flat_conversion() {
382 let identity = Transform3f::identity();
383 let flat = identity.to_flat();
384 let restored = Transform3f::from_flat(&flat);
385 assert_eq!(identity, restored);
386 }
387
388 #[test]
389 fn test_nalgebra_conversion() {
390 let translation = Vector3::new(1.0, 2.0, 3.0);
391 let rotation = nalgebra::UnitQuaternion::from_euler_angles(0.1, 0.2, 0.3);
392 let isometry = Isometry3::from_parts(translation.into(), rotation);
393
394 let transform = Transform3f::from_isometry(&isometry);
395 let back_to_isometry = transform.to_isometry();
396
397 let diff = (isometry.to_homogeneous() - back_to_isometry.to_homogeneous()).abs();
399 for i in 0..4 {
400 for j in 0..4 {
401 assert!(
402 diff[(i, j)] < 1e-6,
403 "Matrix element ({}, {}) differs by {}",
404 i,
405 j,
406 diff[(i, j)]
407 );
408 }
409 }
410 }
411
412 #[test]
413 fn test_transform_composition() {
414 let t1 = Transform3f::from_translation(1.0, 0.0, 0.0);
415 let t2 = Transform3f::from_translation(0.0, 1.0, 0.0);
416 let composed = t1.compose(&t2);
417
418 let expected_translation = Vector3::new(1.0, 1.0, 0.0);
419 let actual_translation = composed.translation();
420
421 assert!((actual_translation - expected_translation).norm() < 1e-6);
422 }
423
424 #[test]
425 fn test_transform_inverse() {
426 let translation = [1.0, 2.0, 3.0];
427 let transform = Transform3f::from_translation_array(translation);
428 let inverse = transform.inverse();
429 let identity = transform.compose(&inverse);
430
431 let result_translation = identity.translation();
432 assert!(
433 result_translation.norm() < 1e-6,
434 "Translation should be near zero, got {result_translation:?}"
435 );
436 }
437}