spatialrust_math/
transform.rs1use crate::{Mat4, Quat, Real, Vec3};
2
3#[derive(Clone, Copy, Debug, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Transform3<T: Real> {
7 matrix: Mat4<T>,
8}
9
10#[derive(Clone, Copy, Debug, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct Isometry3<T: Real> {
14 rotation: Quat<T>,
15 translation: Vec3<T>,
16}
17
18pub trait TransformPoint<T: Real> {
20 fn transform_point(&self, point: Vec3<T>) -> Vec3<T>;
22
23 fn transform_vector(&self, vector: Vec3<T>) -> Vec3<T>;
25}
26
27impl Transform3<f32> {
28 #[must_use]
30 pub const fn from_matrix(matrix: Mat4<f32>) -> Self {
31 Self { matrix }
32 }
33
34 #[must_use]
36 pub fn identity() -> Self {
37 Self::from_matrix(Mat4::<f32>::identity())
38 }
39
40 #[must_use]
42 pub const fn matrix(&self) -> Mat4<f32> {
43 self.matrix
44 }
45}
46
47impl Transform3<f64> {
48 #[must_use]
50 pub const fn from_matrix(matrix: Mat4<f64>) -> Self {
51 Self { matrix }
52 }
53
54 #[must_use]
56 pub fn identity() -> Self {
57 Self::from_matrix(Mat4::<f64>::identity())
58 }
59}
60
61impl TransformPoint<f32> for Transform3<f32> {
62 fn transform_point(&self, point: Vec3<f32>) -> Vec3<f32> {
63 self.matrix.transform_point(point)
64 }
65
66 fn transform_vector(&self, vector: Vec3<f32>) -> Vec3<f32> {
67 self.matrix.transform_vector(vector)
68 }
69}
70
71impl Isometry3<f32> {
72 #[must_use]
74 pub fn identity() -> Self {
75 Self::new(Quat::<f32>::identity(), Vec3::new(0.0, 0.0, 0.0))
76 }
77
78 #[must_use]
80 pub const fn new(rotation: Quat<f32>, translation: Vec3<f32>) -> Self {
81 Self { rotation, translation }
82 }
83
84 #[must_use]
86 pub const fn rotation(&self) -> Quat<f32> {
87 self.rotation
88 }
89
90 #[must_use]
92 pub const fn translation(&self) -> Vec3<f32> {
93 self.translation
94 }
95
96 #[must_use]
98 pub fn to_mat4(self) -> Mat4<f32> {
99 Mat4::<f32>::from_rotation_translation(self.rotation.to_mat3(), self.translation)
100 }
101
102 #[must_use]
104 pub fn compose(self, other: Self) -> Self {
105 let rotation = self.rotation.mul(other.rotation);
106 let translation = self.rotation.to_mat3().mul_vec3(other.translation) + self.translation;
107 Self { rotation, translation }
108 }
109
110 #[must_use]
112 pub fn inverse(self) -> Self {
113 let inv_rotation =
114 Quat::new(-self.rotation.x, -self.rotation.y, -self.rotation.z, self.rotation.w)
115 .normalize();
116 let inv_translation = inv_rotation.to_mat3().mul_vec3(Vec3::new(
117 -self.translation.x,
118 -self.translation.y,
119 -self.translation.z,
120 ));
121 Self { rotation: inv_rotation, translation: inv_translation }
122 }
123}
124
125impl TransformPoint<f32> for Isometry3<f32> {
126 fn transform_point(&self, point: Vec3<f32>) -> Vec3<f32> {
127 self.rotation.to_mat3().mul_vec3(point) + self.translation
128 }
129
130 fn transform_vector(&self, vector: Vec3<f32>) -> Vec3<f32> {
131 self.rotation.to_mat3().mul_vec3(vector)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::{Isometry3, TransformPoint};
138 use crate::tolerance::{approx_eq, f32_eps};
139 use crate::{Quat, Vec3};
140
141 #[test]
142 fn isometry_compose_and_inverse() {
143 let a = Isometry3::new(
144 Quat::from_axis_angle(Vec3::new(0.0, 0.0, 1.0), 0.5),
145 Vec3::new(1.0, 0.0, 0.0),
146 );
147 let b = Isometry3::new(Quat::<f32>::identity(), Vec3::new(0.0, 2.0, 0.0));
148 let composed = a.compose(b);
149 let point = Vec3::new(1.0, 1.0, 0.0);
150 let restored = composed.compose(composed.inverse()).transform_point(point);
151 assert!(approx_eq(restored.x, point.x, f32_eps()));
152 assert!(approx_eq(restored.y, point.y, f32_eps()));
153 assert!(approx_eq(restored.z, point.z, f32_eps()));
154 }
155}