1use glam::{Vec3, Quat, Mat4};
30use crate::error::{AnvilKitError, Result};
31
32#[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 pub translation: Vec3,
53 pub rotation: Quat,
55 pub scale: Vec3,
57}
58
59impl Default for Transform {
60 fn default() -> Self {
61 Self::IDENTITY
62 }
63}
64
65impl Transform {
66 pub const IDENTITY: Self = Self {
68 translation: Vec3::ZERO,
69 rotation: Quat::IDENTITY,
70 scale: Vec3::ONE,
71 };
72
73 pub fn new(translation: Vec3, rotation: Quat, scale: Vec3) -> Self {
94 Self {
95 translation,
96 rotation,
97 scale,
98 }
99 }
100
101 pub fn from_translation(translation: Vec3) -> Self {
113 Self {
114 translation,
115 ..Self::IDENTITY
116 }
117 }
118
119 pub fn from_rotation(rotation: Quat) -> Self {
132 Self {
133 rotation,
134 ..Self::IDENTITY
135 }
136 }
137
138 pub fn from_scale(scale: Vec3) -> Self {
150 Self {
151 scale,
152 ..Self::IDENTITY
153 }
154 }
155
156 pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
169 Self::from_translation(Vec3::new(x, y, z))
170 }
171
172 pub fn from_xy(x: f32, y: f32) -> Self {
183 Self::from_translation(Vec3::new(x, y, 0.0))
184 }
185
186 pub fn with_translation(mut self, translation: Vec3) -> Self {
198 self.translation = translation;
199 self
200 }
201
202 pub fn with_rotation(mut self, rotation: Quat) -> Self {
204 self.rotation = rotation;
205 self
206 }
207
208 pub fn with_scale(mut self, scale: Vec3) -> Self {
210 self.scale = scale;
211 self
212 }
213
214 pub fn looking_at(eye: Vec3, target: Vec3, up: Vec3) -> Result<Self> {
235 let forward = (target - eye).normalize();
236
237 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 if !right.is_finite() || right.length_squared() < f32::EPSILON {
246 return Err(AnvilKitError::generic("无效的上方向向量:与前向向量平行"));
247 }
248
249 let up = right.cross(forward);
250
251 if !up.is_finite() {
253 return Err(AnvilKitError::generic("计算上方向向量时出现数值错误"));
254 }
255
256 let rotation_matrix = glam::Mat3::from_cols(right, up, -forward);
258 let rotation = Quat::from_mat3(&rotation_matrix);
259
260 if !rotation.is_finite() {
262 return Err(AnvilKitError::generic("计算旋转四元数时出现数值错误"));
263 }
264
265 Ok(Self::new(eye, rotation, Vec3::ONE))
266 }
267
268 pub fn compute_matrix(&self) -> Mat4 {
285 Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
286 }
287
288 pub fn transform_point(&self, point: Vec3) -> Vec3 {
303 self.compute_matrix().transform_point3(point)
304 }
305
306 pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
321 self.compute_matrix().transform_vector3(vector)
322 }
323
324 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 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 pub fn inverse(&self) -> Result<Self> {
377 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 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#[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 pub const IDENTITY: Self = Self(Mat4::IDENTITY);
421
422 pub fn from_matrix(matrix: Mat4) -> Self {
424 Self(matrix)
425 }
426
427 pub fn from_transform(transform: &Transform) -> Self {
429 Self(transform.compute_matrix())
430 }
431
432 pub fn matrix(&self) -> Mat4 {
434 self.0
435 }
436
437 pub fn translation(&self) -> Vec3 {
439 self.0.w_axis.truncate()
440 }
441
442 pub fn rotation(&self) -> Quat {
444 let (_, rotation, _) = self.0.to_scale_rotation_translation();
445 rotation
446 }
447
448 pub fn scale(&self) -> Vec3 {
450 let (scale, _, _) = self.0.to_scale_rotation_translation();
451 scale
452 }
453
454 pub fn transform_point(&self, point: Vec3) -> Vec3 {
456 self.0.transform_point3(point)
457 }
458
459 pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
461 self.0.transform_vector3(vector)
462 }
463
464 pub fn mul_transform(&self, other: &GlobalTransform) -> GlobalTransform {
466 GlobalTransform(self.0 * other.0)
467 }
468
469 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 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 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 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 assert!(Transform::looking_at(Vec3::ZERO, Vec3::ZERO, Vec3::Y).is_err());
617
618 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 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 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 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 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 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 let global = GlobalTransform::from_matrix(Mat4::ZERO);
736 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 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 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}