threecrate_core/
transform.rs

1//! 3D transformation utilities
2
3use nalgebra::{Point3, Vector3, Matrix4, Isometry3, Transform3, UnitQuaternion};
4use serde::{Deserialize, Serialize};
5
6/// A 3D transformation that can be applied to points and point clouds
7#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
8pub struct Transform3D {
9    pub matrix: Matrix4<f32>,
10}
11
12impl Transform3D {
13    /// Create an identity transformation
14    pub fn identity() -> Self {
15        Self {
16            matrix: Matrix4::identity(),
17        }
18    }
19
20    /// Create a translation transformation
21    pub fn translation(translation: Vector3<f32>) -> Self {
22        Self {
23            matrix: Matrix4::new_translation(&translation),
24        }
25    }
26
27    /// Create a rotation transformation from a quaternion
28    pub fn rotation(rotation: UnitQuaternion<f32>) -> Self {
29        Self {
30            matrix: rotation.to_homogeneous(),
31        }
32    }
33
34    /// Create a scaling transformation
35    pub fn scaling(scale: Vector3<f32>) -> Self {
36        Self {
37            matrix: Matrix4::new_nonuniform_scaling(&scale),
38        }
39    }
40
41    /// Create a uniform scaling transformation
42    pub fn uniform_scaling(scale: f32) -> Self {
43        Self {
44            matrix: Matrix4::new_scaling(scale),
45        }
46    }
47
48    /// Create a transformation from translation and rotation
49    pub fn from_translation_rotation(
50        translation: Vector3<f32>,
51        rotation: UnitQuaternion<f32>,
52    ) -> Self {
53        let isometry = Isometry3::from_parts(translation.into(), rotation);
54        Self {
55            matrix: isometry.to_homogeneous(),
56        }
57    }
58
59    /// Apply the transformation to a point
60    pub fn transform_point(&self, point: &Point3<f32>) -> Point3<f32> {
61        let homogeneous = self.matrix * point.to_homogeneous();
62        Point3::from_homogeneous(homogeneous).unwrap_or(*point)
63    }
64
65    /// Apply the transformation to a vector
66    pub fn transform_vector(&self, vector: &Vector3<f32>) -> Vector3<f32> {
67        let homogeneous = self.matrix.fixed_view::<3, 3>(0, 0) * vector;
68        homogeneous
69    }
70
71    /// Compose this transformation with another
72    pub fn compose(self, other: Self) -> Self {
73        Self {
74            matrix: self.matrix * other.matrix,
75        }
76    }
77
78    /// Get the inverse transformation
79    pub fn inverse(self) -> Option<Self> {
80        self.matrix.try_inverse().map(|inv_matrix| Self {
81            matrix: inv_matrix,
82        })
83    }
84
85    /// Check if this is approximately the identity transformation
86    pub fn is_identity(&self, epsilon: f32) -> bool {
87        let identity = Matrix4::identity();
88        (self.matrix - identity).norm() < epsilon
89    }
90}
91
92impl Default for Transform3D {
93    fn default() -> Self {
94        Self::identity()
95    }
96}
97
98impl std::ops::Mul for Transform3D {
99    type Output = Self;
100
101    fn mul(self, rhs: Self) -> Self::Output {
102        self.compose(rhs)
103    }
104}
105
106impl From<Matrix4<f32>> for Transform3D {
107    fn from(matrix: Matrix4<f32>) -> Self {
108        Self { matrix }
109    }
110}
111
112impl From<Isometry3<f32>> for Transform3D {
113    fn from(isometry: Isometry3<f32>) -> Self {
114        Self {
115            matrix: isometry.to_homogeneous(),
116        }
117    }
118}
119
120impl From<Transform3<f32>> for Transform3D {
121    fn from(transform: Transform3<f32>) -> Self {
122        Self {
123            matrix: transform.into_inner(),
124        }
125    }
126}