Skip to main content

anvilkit_core/math/
transform.rs

1//! # 变换系统
2//! 
3//! 提供 3D 空间中的位置、旋转和缩放变换,以及层次变换的支持。
4//! 
5//! ## 核心概念
6//! 
7//! - [`Transform`]: 本地变换,相对于父对象的变换
8//! - [`GlobalTransform`]: 全局变换,世界空间中的最终变换
9//! 
10//! ## 使用示例
11//! 
12//! ```rust
13//! use anvilkit_core::math::Transform;
14//! use glam::{Vec3, Quat};
15//! 
16//! // 创建基础变换
17//! let mut transform = Transform::from_xyz(1.0, 2.0, 3.0);
18//! 
19//! // 链式调用设置属性
20//! transform = transform
21//!     .with_rotation(Quat::from_rotation_y(std::f32::consts::PI / 4.0))
22//!     .with_scale(Vec3::splat(2.0));
23//! 
24//! // 应用变换到点
25//! let point = Vec3::ZERO;
26//! let transformed_point = transform.transform_point(point);
27//! ```
28
29use glam::{Vec3, Quat, Mat4};
30use crate::error::{AnvilKitError, Result};
31
32/// 表示 3D 空间中位置、旋转和缩放的变换组件。
33/// 
34/// `Transform` 是 AnvilKit 中用于表示对象空间变换的基础类型。
35/// 它支持 2D 和 3D 使用场景,对于 2D 对象,通常将 Z 分量设置为 0。
36/// 
37/// ## 内存布局
38/// 
39/// 该结构体使用紧凑的内存布局,总大小为 40 字节:
40/// - `translation`: 12 字节 (3 × f32)
41/// - `rotation`: 16 字节 (4 × f32)
42/// - `scale`: 12 字节 (3 × f32)
43/// 
44/// ## 线程安全
45/// 
46/// `Transform` 实现了 `Send` 和 `Sync`,可以安全地在线程间传递。
47#[derive(Debug, Clone, Copy, PartialEq)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
50pub struct Transform {
51    /// 世界空间中的位置
52    pub translation: Vec3,
53    /// 四元数表示的旋转
54    pub rotation: Quat,
55    /// 各轴的缩放因子
56    pub scale: Vec3,
57}
58
59impl Default for Transform {
60    fn default() -> Self {
61        Self::IDENTITY
62    }
63}
64
65impl Transform {
66    /// 单位变换(无平移、旋转或缩放)
67    pub const IDENTITY: Self = Self {
68        translation: Vec3::ZERO,
69        rotation: Quat::IDENTITY,
70        scale: Vec3::ONE,
71    };
72
73    /// 创建一个新的变换实例
74    /// 
75    /// # 参数
76    /// 
77    /// - `translation`: 位置向量
78    /// - `rotation`: 旋转四元数
79    /// - `scale`: 缩放向量
80    /// 
81    /// # 示例
82    /// 
83    /// ```rust
84    /// use anvilkit_core::math::Transform;
85    /// use glam::{Vec3, Quat};
86    /// 
87    /// let transform = Transform::new(
88    ///     Vec3::new(1.0, 2.0, 3.0),
89    ///     Quat::IDENTITY,
90    ///     Vec3::ONE
91    /// );
92    /// ```
93    pub fn new(translation: Vec3, rotation: Quat, scale: Vec3) -> Self {
94        Self {
95            translation,
96            rotation,
97            scale,
98        }
99    }
100
101    /// 从位置创建变换
102    /// 
103    /// # 示例
104    /// 
105    /// ```rust
106    /// use anvilkit_core::math::Transform;
107    /// use glam::Vec3;
108    /// 
109    /// let transform = Transform::from_translation(Vec3::new(1.0, 2.0, 3.0));
110    /// assert_eq!(transform.translation, Vec3::new(1.0, 2.0, 3.0));
111    /// ```
112    pub fn from_translation(translation: Vec3) -> Self {
113        Self {
114            translation,
115            ..Self::IDENTITY
116        }
117    }
118
119    /// 从旋转创建变换
120    /// 
121    /// # 示例
122    /// 
123    /// ```rust
124    /// use anvilkit_core::math::Transform;
125    /// use glam::Quat;
126    /// 
127    /// let rotation = Quat::from_rotation_y(std::f32::consts::PI / 4.0);
128    /// let transform = Transform::from_rotation(rotation);
129    /// assert_eq!(transform.rotation, rotation);
130    /// ```
131    pub fn from_rotation(rotation: Quat) -> Self {
132        Self {
133            rotation,
134            ..Self::IDENTITY
135        }
136    }
137
138    /// 从缩放创建变换
139    /// 
140    /// # 示例
141    /// 
142    /// ```rust
143    /// use anvilkit_core::math::Transform;
144    /// use glam::Vec3;
145    /// 
146    /// let transform = Transform::from_scale(Vec3::splat(2.0));
147    /// assert_eq!(transform.scale, Vec3::splat(2.0));
148    /// ```
149    pub fn from_scale(scale: Vec3) -> Self {
150        Self {
151            scale,
152            ..Self::IDENTITY
153        }
154    }
155
156    /// 从 XYZ 坐标创建变换
157    /// 
158    /// # 示例
159    /// 
160    /// ```rust
161    /// use anvilkit_core::math::Transform;
162    /// 
163    /// let transform = Transform::from_xyz(1.0, 2.0, 3.0);
164    /// assert_eq!(transform.translation.x, 1.0);
165    /// assert_eq!(transform.translation.y, 2.0);
166    /// assert_eq!(transform.translation.z, 3.0);
167    /// ```
168    pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
169        Self::from_translation(Vec3::new(x, y, z))
170    }
171
172    /// 从 XY 坐标创建 2D 变换(Z = 0)
173    /// 
174    /// # 示例
175    /// 
176    /// ```rust
177    /// use anvilkit_core::math::Transform;
178    /// 
179    /// let transform = Transform::from_xy(1.0, 2.0);
180    /// assert_eq!(transform.translation.z, 0.0);
181    /// ```
182    pub fn from_xy(x: f32, y: f32) -> Self {
183        Self::from_translation(Vec3::new(x, y, 0.0))
184    }
185
186    /// 设置位置(链式调用)
187    /// 
188    /// # 示例
189    /// 
190    /// ```rust
191    /// use anvilkit_core::math::Transform;
192    /// use glam::Vec3;
193    /// 
194    /// let transform = Transform::IDENTITY
195    ///     .with_translation(Vec3::new(1.0, 2.0, 3.0));
196    /// ```
197    pub fn with_translation(mut self, translation: Vec3) -> Self {
198        self.translation = translation;
199        self
200    }
201
202    /// 设置旋转(链式调用)
203    pub fn with_rotation(mut self, rotation: Quat) -> Self {
204        self.rotation = rotation;
205        self
206    }
207
208    /// 设置缩放(链式调用)
209    pub fn with_scale(mut self, scale: Vec3) -> Self {
210        self.scale = scale;
211        self
212    }
213
214    /// 创建朝向目标的变换
215    /// 
216    /// # 参数
217    /// 
218    /// - `eye`: 观察者位置
219    /// - `target`: 目标位置
220    /// - `up`: 上方向向量
221    /// 
222    /// # 示例
223    /// 
224    /// ```rust
225    /// use anvilkit_core::math::Transform;
226    /// use glam::Vec3;
227    /// 
228    /// let transform = Transform::looking_at(
229    ///     Vec3::new(0.0, 0.0, 5.0),  // 相机位置
230    ///     Vec3::ZERO,                // 看向原点
231    ///     Vec3::Y                    // 上方向
232    /// );
233    /// ```
234    pub fn looking_at(eye: Vec3, target: Vec3, up: Vec3) -> Result<Self> {
235        let forward = (target - eye).normalize();
236        
237        // 检查前向向量是否有效
238        if !forward.is_finite() || forward.length_squared() < f32::EPSILON {
239            return Err(AnvilKitError::generic("无效的朝向向量:目标和眼睛位置相同或无效"));
240        }
241
242        let right = forward.cross(up).normalize();
243
244        // 检查右向向量是否有效(避免平行向量)
245        if !right.is_finite() || right.length_squared() < f32::EPSILON {
246            return Err(AnvilKitError::generic("无效的上方向向量:与前向向量平行"));
247        }
248
249        let up = right.cross(forward);
250
251        // 检查上向向量是否有效
252        if !up.is_finite() {
253            return Err(AnvilKitError::generic("计算上方向向量时出现数值错误"));
254        }
255
256        // 创建旋转矩阵并转换为四元数
257        let rotation_matrix = glam::Mat3::from_cols(right, up, -forward);
258        let rotation = Quat::from_mat3(&rotation_matrix);
259
260        // 检查四元数是否有效
261        if !rotation.is_finite() {
262            return Err(AnvilKitError::generic("计算旋转四元数时出现数值错误"));
263        }
264        
265        Ok(Self::new(eye, rotation, Vec3::ONE))
266    }
267
268    /// 将变换转换为 4x4 变换矩阵
269    /// 
270    /// 矩阵的计算顺序为:缩放 → 旋转 → 平移
271    /// 
272    /// # 示例
273    /// 
274    /// ```rust
275    /// use anvilkit_core::math::Transform;
276    /// use glam::Vec3;
277    /// 
278    /// let transform = Transform::from_xyz(1.0, 2.0, 3.0);
279    /// let matrix = transform.compute_matrix();
280    /// 
281    /// // 验证平移部分
282    /// assert_eq!(matrix.w_axis.truncate(), Vec3::new(1.0, 2.0, 3.0));
283    /// ```
284    pub fn compute_matrix(&self) -> Mat4 {
285        Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
286    }
287
288    /// 应用变换到点
289    /// 
290    /// # 示例
291    /// 
292    /// ```rust
293    /// use anvilkit_core::math::Transform;
294    /// use glam::Vec3;
295    /// 
296    /// let transform = Transform::from_xyz(1.0, 2.0, 3.0);
297    /// let point = Vec3::ZERO;
298    /// let transformed = transform.transform_point(point);
299    /// 
300    /// assert_eq!(transformed, Vec3::new(1.0, 2.0, 3.0));
301    /// ```
302    pub fn transform_point(&self, point: Vec3) -> Vec3 {
303        self.compute_matrix().transform_point3(point)
304    }
305
306    /// 应用变换到向量(忽略平移)
307    /// 
308    /// # 示例
309    /// 
310    /// ```rust
311    /// use anvilkit_core::math::Transform;
312    /// use glam::Vec3;
313    /// 
314    /// let transform = Transform::from_scale(Vec3::splat(2.0));
315    /// let vector = Vec3::new(1.0, 1.0, 1.0);
316    /// let transformed = transform.transform_vector(vector);
317    /// 
318    /// assert_eq!(transformed, Vec3::new(2.0, 2.0, 2.0));
319    /// ```
320    pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
321        self.compute_matrix().transform_vector3(vector)
322    }
323
324    /// 组合两个变换(self * other)
325    /// 
326    /// # 示例
327    /// 
328    /// ```rust
329    /// use anvilkit_core::math::Transform;
330    /// use glam::Vec3;
331    /// 
332    /// let parent = Transform::from_xyz(1.0, 0.0, 0.0);
333    /// let child = Transform::from_xyz(0.0, 1.0, 0.0);
334    /// let combined = parent.mul_transform(&child);
335    /// 
336    /// assert_eq!(combined.translation, Vec3::new(1.0, 1.0, 0.0));
337    /// ```
338    pub fn mul_transform(&self, other: &Transform) -> Transform {
339        let matrix = self.compute_matrix() * other.compute_matrix();
340        Transform::from_matrix(matrix)
341    }
342
343    /// 从变换矩阵创建变换
344    /// 
345    /// # 注意
346    /// 
347    /// 如果矩阵包含非均匀缩放或剪切,可能会丢失信息。
348    pub fn from_matrix(matrix: Mat4) -> Self {
349        let (scale, rotation, translation) = matrix.to_scale_rotation_translation();
350        Self {
351            translation,
352            rotation,
353            scale,
354        }
355    }
356
357    /// 获取变换的逆变换
358    ///
359    /// # 错误
360    ///
361    /// 如果变换不可逆(例如缩放为零),返回错误。
362    ///
363    /// # 示例
364    ///
365    /// ```rust
366    /// use anvilkit_core::math::Transform;
367    /// use glam::Vec3;
368    ///
369    /// let transform = Transform::from_xyz(1.0, 2.0, 3.0);
370    /// let inverse = transform.inverse().unwrap();
371    /// let identity = transform.mul_transform(&inverse);
372    ///
373    /// // 结果应该接近单位变换
374    /// assert!((identity.translation.length() < 1e-5));
375    /// ```
376    pub fn inverse(&self) -> Result<Self> {
377        // 检查缩放是否为零
378        if self.scale.x.abs() < f32::EPSILON ||
379           self.scale.y.abs() < f32::EPSILON ||
380           self.scale.z.abs() < f32::EPSILON {
381            return Err(AnvilKitError::generic("无法计算逆变换:缩放包含零值"));
382        }
383
384        let inv_scale = Vec3::new(1.0 / self.scale.x, 1.0 / self.scale.y, 1.0 / self.scale.z);
385        let inv_rotation = self.rotation.inverse();
386        let inv_translation = -(inv_rotation * (self.translation * inv_scale));
387
388        Ok(Self {
389            translation: inv_translation,
390            rotation: inv_rotation,
391            scale: inv_scale,
392        })
393    }
394
395    /// 检查变换是否有效(不包含 NaN 或无穷大)
396    pub fn is_finite(&self) -> bool {
397        self.translation.is_finite() && 
398        self.rotation.is_finite() && 
399        self.scale.is_finite()
400    }
401}
402
403/// 全局变换组件,表示世界空间中的最终变换。
404/// 
405/// `GlobalTransform` 通常由层次变换系统计算,表示对象在世界空间中的最终位置、旋转和缩放。
406/// 它使用 4x4 矩阵存储,以提供高效的变换操作。
407#[derive(Debug, Clone, Copy, PartialEq)]
408#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
409#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
410pub struct GlobalTransform(pub Mat4);
411
412impl Default for GlobalTransform {
413    fn default() -> Self {
414        Self::IDENTITY
415    }
416}
417
418impl GlobalTransform {
419    /// 单位全局变换
420    pub const IDENTITY: Self = Self(Mat4::IDENTITY);
421
422    /// 从变换矩阵创建全局变换
423    pub fn from_matrix(matrix: Mat4) -> Self {
424        Self(matrix)
425    }
426
427    /// 从本地变换创建全局变换
428    pub fn from_transform(transform: &Transform) -> Self {
429        Self(transform.compute_matrix())
430    }
431
432    /// 获取变换矩阵
433    pub fn matrix(&self) -> Mat4 {
434        self.0
435    }
436
437    /// 获取位置分量
438    pub fn translation(&self) -> Vec3 {
439        self.0.w_axis.truncate()
440    }
441
442    /// 获取旋转分量
443    pub fn rotation(&self) -> Quat {
444        let (_, rotation, _) = self.0.to_scale_rotation_translation();
445        rotation
446    }
447
448    /// 获取缩放分量
449    pub fn scale(&self) -> Vec3 {
450        let (scale, _, _) = self.0.to_scale_rotation_translation();
451        scale
452    }
453
454    /// 应用全局变换到点
455    pub fn transform_point(&self, point: Vec3) -> Vec3 {
456        self.0.transform_point3(point)
457    }
458
459    /// 应用全局变换到向量(忽略平移)
460    pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
461        self.0.transform_vector3(vector)
462    }
463
464    /// 组合全局变换
465    pub fn mul_transform(&self, other: &GlobalTransform) -> GlobalTransform {
466        GlobalTransform(self.0 * other.0)
467    }
468
469    /// 获取全局变换的逆变换
470    pub fn inverse(&self) -> Result<Self> {
471        let inv_matrix = self.0.inverse();
472        if !inv_matrix.is_finite() {
473            return Err(AnvilKitError::generic("无法计算全局变换的逆变换"));
474        }
475        Ok(Self(inv_matrix))
476    }
477
478    /// 检查全局变换是否有效
479    pub fn is_finite(&self) -> bool {
480        self.0.is_finite()
481    }
482}
483
484impl From<Transform> for GlobalTransform {
485    fn from(transform: Transform) -> Self {
486        Self::from_transform(&transform)
487    }
488}
489
490impl From<Mat4> for GlobalTransform {
491    fn from(matrix: Mat4) -> Self {
492        Self::from_matrix(matrix)
493    }
494}
495
496#[cfg(test)]
497mod tests {
498    use super::*;
499    use glam::Vec3;
500
501    // 自定义的近似相等比较函数
502    fn vec3_approx_eq(a: Vec3, b: Vec3, epsilon: f32) -> bool {
503        (a - b).length() < epsilon
504    }
505
506    fn quat_approx_eq(a: glam::Quat, b: glam::Quat, epsilon: f32) -> bool {
507        (a.dot(b) - 1.0).abs() < epsilon || (a.dot(b) + 1.0).abs() < epsilon
508    }
509
510    #[test]
511    fn test_transform_identity() {
512        let transform = Transform::IDENTITY;
513        assert_eq!(transform.translation, Vec3::ZERO);
514        assert_eq!(transform.rotation, Quat::IDENTITY);
515        assert_eq!(transform.scale, Vec3::ONE);
516    }
517
518    #[test]
519    fn test_transform_creation() {
520        let transform = Transform::from_xyz(1.0, 2.0, 3.0);
521        assert_eq!(transform.translation, Vec3::new(1.0, 2.0, 3.0));
522        
523        let transform = Transform::from_xy(1.0, 2.0);
524        assert_eq!(transform.translation, Vec3::new(1.0, 2.0, 0.0));
525    }
526
527    #[test]
528    fn test_transform_chaining() {
529        let transform = Transform::IDENTITY
530            .with_translation(Vec3::new(1.0, 2.0, 3.0))
531            .with_scale(Vec3::splat(2.0));
532        
533        assert_eq!(transform.translation, Vec3::new(1.0, 2.0, 3.0));
534        assert_eq!(transform.scale, Vec3::splat(2.0));
535    }
536
537    #[test]
538    fn test_transform_point() {
539        let transform = Transform::from_xyz(1.0, 2.0, 3.0);
540        let point = Vec3::ZERO;
541        let transformed = transform.transform_point(point);
542        
543        assert!(vec3_approx_eq(transformed, Vec3::new(1.0, 2.0, 3.0), 1e-6));
544    }
545
546    #[test]
547    fn test_transform_vector() {
548        let transform = Transform::from_scale(Vec3::splat(2.0));
549        let vector = Vec3::new(1.0, 1.0, 1.0);
550        let transformed = transform.transform_vector(vector);
551        
552        assert!(vec3_approx_eq(transformed, Vec3::new(2.0, 2.0, 2.0), 1e-6));
553    }
554
555    #[test]
556    fn test_transform_composition() {
557        let parent = Transform::from_xyz(1.0, 0.0, 0.0);
558        let child = Transform::from_xyz(0.0, 1.0, 0.0);
559        let combined = parent.mul_transform(&child);
560        
561        assert!(vec3_approx_eq(combined.translation, Vec3::new(1.0, 1.0, 0.0), 1e-6));
562    }
563
564    #[test]
565    fn test_transform_matrix_roundtrip() {
566        let original = Transform::from_xyz(1.0, 2.0, 3.0)
567            .with_rotation(Quat::from_rotation_y(0.5))
568            .with_scale(Vec3::new(2.0, 1.5, 0.5));
569        
570        let matrix = original.compute_matrix();
571        let reconstructed = Transform::from_matrix(matrix);
572        
573        assert!(vec3_approx_eq(original.translation, reconstructed.translation, 1e-5));
574        assert!(quat_approx_eq(original.rotation, reconstructed.rotation, 1e-5));
575        assert!(vec3_approx_eq(original.scale, reconstructed.scale, 1e-5));
576    }
577
578    #[test]
579    fn test_transform_inverse() {
580        let transform = Transform::from_xyz(1.0, 2.0, 3.0)
581            .with_rotation(Quat::from_rotation_y(0.5))
582            .with_scale(Vec3::splat(2.0));
583        
584        let inverse = transform.inverse().unwrap();
585        let identity = transform.mul_transform(&inverse);
586        
587        assert!(vec3_approx_eq(identity.translation, Vec3::ZERO, 1e-5));
588        assert!(quat_approx_eq(identity.rotation, Quat::IDENTITY, 1e-5));
589        assert!(vec3_approx_eq(identity.scale, Vec3::ONE, 1e-5));
590    }
591
592    #[test]
593    fn test_transform_inverse_zero_scale() {
594        let transform = Transform::from_scale(Vec3::new(0.0, 1.0, 1.0));
595        assert!(transform.inverse().is_err());
596    }
597
598    #[test]
599    fn test_looking_at() {
600        let transform = Transform::looking_at(
601            Vec3::new(0.0, 0.0, 5.0),
602            Vec3::ZERO,
603            Vec3::Y
604        ).unwrap();
605        
606        // 验证变换后的前向向量指向目标
607        let forward = transform.transform_vector(-Vec3::Z);
608        let expected_direction = (Vec3::ZERO - Vec3::new(0.0, 0.0, 5.0)).normalize();
609        
610        assert!(vec3_approx_eq(forward, expected_direction, 1e-5));
611    }
612
613    #[test]
614    fn test_looking_at_invalid() {
615        // 相同的眼睛和目标位置
616        assert!(Transform::looking_at(Vec3::ZERO, Vec3::ZERO, Vec3::Y).is_err());
617
618        // 平行的前向和上向量
619        // 从 (0,0,0) 看向 (0,0,1),前向向量是 (0,0,1)
620        // 如果上向量也是 (0,0,1),那么它们平行,叉积为零
621        let result = Transform::looking_at(Vec3::ZERO, Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0));
622        assert!(result.is_err(), "Expected error for parallel forward and up vectors, but got: {:?}", result);
623
624        // 另一个平行向量的例子:前向和上向都是 Y 轴
625        assert!(Transform::looking_at(Vec3::ZERO, Vec3::Y, Vec3::Y).is_err());
626    }
627
628    #[test]
629    fn test_global_transform() {
630        let transform = Transform::from_xyz(1.0, 2.0, 3.0);
631        let global = GlobalTransform::from_transform(&transform);
632        
633        assert_eq!(global.translation(), Vec3::new(1.0, 2.0, 3.0));
634    }
635
636    #[test]
637    fn test_global_transform_composition() {
638        let global1 = GlobalTransform::from_matrix(Mat4::from_translation(Vec3::X));
639        let global2 = GlobalTransform::from_matrix(Mat4::from_translation(Vec3::Y));
640        let combined = global1.mul_transform(&global2);
641        
642        assert!(vec3_approx_eq(combined.translation(), Vec3::new(1.0, 1.0, 0.0), 1e-6));
643    }
644
645    #[test]
646    fn test_finite_checks() {
647        let valid_transform = Transform::from_xyz(1.0, 2.0, 3.0);
648        assert!(valid_transform.is_finite());
649
650        let invalid_transform = Transform::from_xyz(f32::NAN, 2.0, 3.0);
651        assert!(!invalid_transform.is_finite());
652    }
653
654    #[test]
655    fn test_transform_nan_input() {
656        let transform = Transform::from_xyz(f32::NAN, 0.0, 0.0);
657        assert!(!transform.is_finite());
658
659        let transform = Transform::from_xyz(f32::INFINITY, 0.0, 0.0);
660        assert!(!transform.is_finite());
661    }
662
663    #[test]
664    fn test_transform_deep_chain_precision() {
665        // 多层链式组合后的精度测试
666        let mut composed = Transform::IDENTITY;
667        let step = Transform::from_xyz(0.1, 0.0, 0.0)
668            .with_rotation(Quat::from_rotation_z(0.01));
669
670        for _ in 0..100 {
671            composed = composed.mul_transform(&step);
672        }
673
674        // 验证结果仍然是有限的
675        assert!(composed.is_finite());
676        assert!(composed.translation.is_finite());
677    }
678
679    #[test]
680    fn test_transform_inverse_roundtrip_with_rotation_and_scale() {
681        // Use uniform scale to avoid matrix decomposition errors with non-uniform scale + rotation
682        let transform = Transform::new(
683            Vec3::new(3.0, -7.0, 11.0),
684            Quat::from_euler(glam::EulerRot::YXZ, 0.5, 0.3, 0.7),
685            Vec3::splat(2.0),
686        );
687
688        let inverse = transform.inverse().unwrap();
689        let identity = transform.mul_transform(&inverse);
690
691        assert!(vec3_approx_eq(identity.translation, Vec3::ZERO, 1e-4));
692        assert!(quat_approx_eq(identity.rotation, Quat::IDENTITY, 1e-4));
693        assert!(vec3_approx_eq(identity.scale, Vec3::ONE, 1e-4));
694    }
695
696    #[test]
697    fn test_transform_from_matrix_identity() {
698        let transform = Transform::from_matrix(Mat4::IDENTITY);
699        assert!(vec3_approx_eq(transform.translation, Vec3::ZERO, 1e-6));
700        assert!(quat_approx_eq(transform.rotation, Quat::IDENTITY, 1e-6));
701        assert!(vec3_approx_eq(transform.scale, Vec3::ONE, 1e-6));
702    }
703
704    #[test]
705    fn test_transform_all_axes_inverse() {
706        // 测试每个轴为零时的逆变换错误
707        assert!(Transform::from_scale(Vec3::new(0.0, 1.0, 1.0)).inverse().is_err());
708        assert!(Transform::from_scale(Vec3::new(1.0, 0.0, 1.0)).inverse().is_err());
709        assert!(Transform::from_scale(Vec3::new(1.0, 1.0, 0.0)).inverse().is_err());
710        assert!(Transform::from_scale(Vec3::new(0.0, 0.0, 0.0)).inverse().is_err());
711    }
712
713    #[test]
714    fn test_global_transform_from_transform_conversion() {
715        let transform = Transform::from_xyz(5.0, 10.0, 15.0)
716            .with_scale(Vec3::splat(2.0));
717        let global: GlobalTransform = transform.into();
718
719        assert!(vec3_approx_eq(global.translation(), Vec3::new(5.0, 10.0, 15.0), 1e-6));
720        assert!(vec3_approx_eq(global.scale(), Vec3::splat(2.0), 1e-6));
721    }
722
723    #[test]
724    fn test_global_transform_inverse() {
725        let global = GlobalTransform::from_matrix(Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0)));
726        let inverse = global.inverse().unwrap();
727        let identity = global.mul_transform(&inverse);
728
729        assert!(vec3_approx_eq(identity.translation(), Vec3::ZERO, 1e-5));
730    }
731
732    #[test]
733    fn test_global_transform_inverse_singular() {
734        // 零矩阵不可逆
735        let global = GlobalTransform::from_matrix(Mat4::ZERO);
736        // Mat4::ZERO.inverse() 可能产生非有限值
737        let result = global.inverse();
738        assert!(result.is_err());
739    }
740
741    #[test]
742    fn test_transform_default() {
743        let transform = Transform::default();
744        assert_eq!(transform, Transform::IDENTITY);
745    }
746
747    #[test]
748    fn test_global_transform_default() {
749        let global = GlobalTransform::default();
750        assert_eq!(global, GlobalTransform::IDENTITY);
751    }
752
753    #[test]
754    fn test_transform_point_with_scale_and_rotation() {
755        let transform = Transform::from_scale(Vec3::splat(2.0))
756            .with_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2));
757
758        let point = Vec3::new(1.0, 0.0, 0.0);
759        let transformed = transform.transform_point(point);
760
761        // 先缩放到 (2,0,0) 再绕Z旋转90度得到 (0,2,0)
762        assert!(vec3_approx_eq(transformed, Vec3::new(0.0, 2.0, 0.0), 1e-5));
763    }
764
765    #[test]
766    fn test_transform_vector_ignores_translation() {
767        let transform = Transform::from_xyz(100.0, 200.0, 300.0);
768        let vector = Vec3::new(1.0, 0.0, 0.0);
769        let transformed = transform.transform_vector(vector);
770
771        // 向量变换应该忽略平移
772        assert!(vec3_approx_eq(transformed, Vec3::new(1.0, 0.0, 0.0), 1e-6));
773    }
774
775    #[test]
776    fn test_global_transform_transform_point() {
777        let global = GlobalTransform::from_matrix(
778            Mat4::from_translation(Vec3::new(10.0, 20.0, 30.0))
779        );
780        let point = Vec3::ZERO;
781        let result = global.transform_point(point);
782        assert!(vec3_approx_eq(result, Vec3::new(10.0, 20.0, 30.0), 1e-6));
783    }
784
785    #[test]
786    fn test_global_transform_rotation_extraction() {
787        let rotation = Quat::from_rotation_y(std::f32::consts::FRAC_PI_4);
788        let transform = Transform::from_rotation(rotation);
789        let global = GlobalTransform::from_transform(&transform);
790
791        assert!(quat_approx_eq(global.rotation(), rotation, 1e-5));
792    }
793}