1use glam::Affine3A;
2use glam::Mat4;
3use glam::Vec3A;
4
5use crate::IsoTransform;
6use crate::Quat;
7use crate::Vec3;
8use crate::Vec3Ext;
9use crate::Vec4;
10use crate::Vec4Swizzles;
11
12#[derive(Clone, Copy, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "speedy", derive(speedy::Writable, speedy::Readable))]
18pub struct Conformal3 {
19 pub translation_and_scale: Vec4,
21 pub rotation: Quat,
22}
23
24impl Conformal3 {
25 pub const IDENTITY: Self = Self {
27 translation_and_scale: Vec4::W,
28 rotation: Quat::IDENTITY,
29 };
30
31 #[inline]
32 pub fn from_scale_rotation_translation(scale: f32, rotation: Quat, translation: Vec3) -> Self {
38 Self {
39 translation_and_scale: translation.extend(scale),
40 rotation,
41 }
42 }
43
44 #[inline]
50 pub fn from_rotation_translation(rotation: Quat, translation: Vec3) -> Self {
51 Self {
52 translation_and_scale: translation.extend(1.0),
53 rotation,
54 }
55 }
56
57 #[inline]
59 pub fn from_translation(translation: Vec3) -> Self {
60 Self {
61 translation_and_scale: translation.extend(1.0),
62 rotation: Quat::IDENTITY,
63 }
64 }
65
66 #[inline]
68 pub fn to_scale_rotation_translation(self) -> (f32, Quat, Vec3) {
69 (self.scale(), self.rotation(), self.translation())
70 }
71
72 #[inline]
74 pub fn from_quat(rotation: Quat) -> Self {
75 Self::from_scale_rotation_translation(1.0, rotation, Vec3::ZERO)
76 }
77
78 #[inline]
80 pub fn from_scale(scale: f32) -> Self {
81 Self::from_scale_rotation_translation(scale, Quat::IDENTITY, Vec3::ZERO)
82 }
83
84 #[inline]
86 pub fn inverse(&self) -> Self {
87 let inv_scale = self.inv_scale();
88 let inv_rotation = self.rotation.inverse();
89 let inv_translation = inv_scale * (inv_rotation * -self.translation());
90 Self::from_scale_rotation_translation(inv_scale, inv_rotation, inv_translation)
91 }
92
93 #[inline]
96 #[must_use]
97 pub fn normalize(&self) -> Self {
98 let scale = self.scale();
99 let rotation = self.rotation().normalize();
100 let translation = self.translation();
101 Self::from_scale_rotation_translation(scale, rotation, translation)
102 }
103
104 #[inline]
107 pub fn from_affine3a_lossy(transform: &crate::Affine3A) -> Self {
108 let (scale, rotation, translation) = transform.to_scale_rotation_translation();
109 Self {
110 translation_and_scale: translation.extend(scale.mean()),
111 rotation: rotation.normalize(),
112 }
113 }
114
115 #[inline]
117 pub fn to_affine3a(self) -> Affine3A {
118 Affine3A::from_scale_rotation_translation(
119 Vec3::splat(self.scale()),
120 self.rotation(),
121 self.translation(),
122 )
123 }
124
125 #[inline]
127 pub fn to_mat4(self) -> Mat4 {
128 Mat4::from_scale_rotation_translation(
129 Vec3::splat(self.scale()),
130 self.rotation(),
131 self.translation(),
132 )
133 }
134
135 #[inline]
137 pub fn transform_point3(&self, value: Vec3) -> Vec3 {
138 self.translation() + self.scale() * (self.rotation() * value)
139 }
140
141 #[inline]
143 pub fn transform_point3a(&self, value: Vec3A) -> Vec3A {
144 Vec3A::from(self.translation()) + self.scale() * (self.rotation() * value)
145 }
146
147 #[inline]
149 pub fn transform_vector3(&self, value: Vec3) -> Vec3 {
150 self.scale() * (self.rotation() * value)
151 }
152
153 #[inline]
155 pub fn transform_vector3a(&self, value: Vec3A) -> Vec3A {
156 self.scale() * (self.rotation() * value)
157 }
158
159 #[inline]
161 pub fn rotation(&self) -> Quat {
162 self.rotation
163 }
164
165 #[inline]
167 pub fn set_rotation(&mut self, rotation: Quat) {
168 self.rotation = rotation;
169 }
170
171 #[inline]
173 pub fn translation(&self) -> Vec3 {
174 self.translation_and_scale.xyz()
175 }
176
177 #[inline]
179 pub fn translation_and_scale(&self) -> Vec4 {
180 self.translation_and_scale
181 }
182
183 #[inline]
185 pub fn set_translation(&mut self, translation: Vec3) {
186 let scale = self.scale();
187 self.translation_and_scale = translation.extend(scale);
188 }
189
190 #[inline]
192 pub fn scale(&self) -> f32 {
193 self.translation_and_scale.w
194 }
195
196 #[inline]
198 pub fn inv_scale(&self) -> f32 {
199 if self.scale() == 0.0 {
200 f32::INFINITY
201 } else {
202 1.0 / self.scale()
203 }
204 }
205
206 #[inline]
208 pub fn set_scale(&mut self, scale: f32) {
209 self.translation_and_scale.w = scale;
210 }
211
212 #[inline]
214 pub fn from_iso_transform(t: IsoTransform) -> Self {
215 Self::from_rotation_translation(t.rotation(), t.translation())
216 }
217
218 pub fn to_iso_transform(self) -> IsoTransform {
220 IsoTransform::from_rotation_translation(self.rotation, self.translation())
221 }
222
223 #[cfg(not(target_arch = "spirv"))] #[inline]
233 pub fn look_at_rh(eye: Vec3, target: Vec3, up: Vec3) -> Option<Self> {
234 IsoTransform::look_at_rh(eye, target, up).map(Self::from_iso_transform)
235 }
236
237 pub fn is_finite(&self) -> bool {
241 self.translation_and_scale.is_finite() && self.rotation.is_finite()
242 }
243}
244
245impl core::ops::Mul for &Conformal3 {
246 type Output = Conformal3;
247
248 #[inline]
249 fn mul(self, rhs: &Conformal3) -> Conformal3 {
250 let translation = self.transform_point3(rhs.translation());
251 let rotation = self.rotation() * rhs.rotation();
252 let scale = self.scale() * rhs.scale();
253 Conformal3::from_scale_rotation_translation(scale, rotation, translation)
254 }
255}
256
257impl core::ops::Mul<Conformal3> for &Conformal3 {
258 type Output = Conformal3;
259
260 #[inline]
261 fn mul(self, rhs: Conformal3) -> Conformal3 {
262 self.mul(&rhs)
263 }
264}
265
266impl core::ops::Mul for Conformal3 {
267 type Output = Self;
268
269 #[inline]
270 fn mul(self, rhs: Self) -> Self {
271 (&self).mul(&rhs)
272 }
273}
274
275impl core::ops::Mul<Conformal3> for IsoTransform {
276 type Output = Conformal3;
277
278 #[inline]
279 fn mul(self, rhs: Conformal3) -> Conformal3 {
280 Conformal3::from_iso_transform(self).mul(rhs)
281 }
282}
283
284impl core::ops::Mul<IsoTransform> for Conformal3 {
285 type Output = Self;
286
287 #[inline]
288 fn mul(self, rhs: IsoTransform) -> Self {
289 self.mul(Self::from_iso_transform(rhs))
290 }
291}
292
293impl Default for Conformal3 {
295 #[inline]
297 fn default() -> Self {
298 Self::IDENTITY
299 }
300}
301
302impl From<Conformal3> for Mat4 {
303 #[inline]
304 fn from(c: Conformal3) -> Self {
305 c.to_mat4()
306 }
307}
308
309impl From<Conformal3> for crate::Affine3A {
310 #[inline]
311 fn from(c: Conformal3) -> Self {
312 c.to_affine3a()
313 }
314}
315
316impl From<IsoTransform> for Conformal3 {
317 #[inline]
318 fn from(c: IsoTransform) -> Self {
319 Self::from_iso_transform(c)
320 }
321}
322
323#[cfg(feature = "std")]
324impl core::fmt::Debug for Conformal3 {
325 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326 let (axis, angle) = self.rotation().to_axis_angle();
327 let translation = self.translation();
328 let scale = self.scale();
329 f.debug_struct("Conformal3")
330 .field(
331 "translation",
332 &format!("[{} {} {}]", translation[0], translation[1], translation[2]),
333 )
334 .field(
335 "rotation",
336 &format!(
337 "{:.1}° around [{} {} {}]",
338 angle.to_degrees(),
339 axis[0],
340 axis[1],
341 axis[2],
342 ),
343 )
344 .field("scale", &format!("{}", scale))
345 .finish()
346 }
347}
348
349#[cfg(test)]
350mod test {
351 use super::*;
352
353 fn approx_eq_transform(a: Conformal3, b: Conformal3) -> bool {
354 let max_abs_diff = 1e-6;
355 a.translation().abs_diff_eq(b.translation(), max_abs_diff)
356 && a.rotation().abs_diff_eq(b.rotation(), max_abs_diff)
357 && ((a.scale() - b.scale()).abs() < max_abs_diff)
358 }
359
360 macro_rules! assert_approx_eq_transform {
361 ($a: expr, $b: expr) => {
362 assert!(approx_eq_transform($a, $b), "{:#?} != {:#?}", $a, $b,);
363 };
364 }
365
366 #[test]
367 fn test_inverse() {
368 use crate::Conformal3;
369
370 let transform = Conformal3::from_scale_rotation_translation(
371 2.0,
372 Quat::from_rotation_y(std::f32::consts::PI),
373 Vec3::ONE,
374 );
375 let identity = transform * transform.inverse();
376 assert_approx_eq_transform!(identity, Conformal3::IDENTITY);
377
378 let transform = Conformal3::from_scale_rotation_translation(
379 10.0,
380 Quat::from_axis_angle(Vec3::ONE.normalize(), 1.234),
381 Vec3::new(1.0, 2.0, 3.0),
382 );
383 let identity = transform * transform.inverse();
384 assert_approx_eq_transform!(identity, Conformal3::IDENTITY);
385 }
386}