feo_math/utils/
space.rs

1//! Space a construct for storing everything about how an object is to be transformed.
2//! 
3//! A space is an area in which objects, such as spaces or the vertices of a mesh, can exist. 
4//! An issue with this visualization is however that the area itself is unlimited in size. If 
5//! a space is translated, rotated, or scaled the objects within it do not change their 
6//! position, rotation, or scaling relative to the space. Their position relative to the space
7//!  containing that space however will change. 
8//!
9//! > Spaces can also be joined to represent conversions between spaces.
10//!
11//! Standard examples of spaces include:
12//!  + Worldspace : The space in which all objects exist without their own space and any spaces containing their own space.
13//!  + Viewspace : The space the viewer perceives viewed objects in
14//!  + Cameraspace : The position relative to the camera. NOT the viewspace.
15//!  + Objectspace : every game-object has its own space
16//!
17//! 
18use std::ops::{Add, Sub};
19
20use crate::{Zero, linear_algebra::{
21        matrix3::Matrix3, 
22        matrix4::Matrix4, 
23        vector3::Vector3
24    }, rotation::{Rotation, quaternion::Quaternion}};
25
26#[derive(Debug, Clone, Copy)]
27pub struct Space {
28    pub center: Vector3<f32>,
29    pub rotation: Quaternion<f32>,
30    pub scale_factor: Matrix3<f32>
31}
32// todo move to src no utils should exist
33impl Space {
34    #[allow(clippy::redundant_closure)] // Linter error: .unwrap_or_else() wants FnOnce<_> closure
35    pub fn new(
36            center: Option<Vector3<f32>>, 
37            rotation: Option<Quaternion<f32>>, 
38            scale_factor: Option<Vector3<f32>>) -> Space {
39        let vec = scale_factor.unwrap_or_else(|| Vector3::new(1.0, 1.0, 1.0));
40        Space{
41            rotation: rotation.unwrap_or_else(|| Quaternion::f32_identity()),
42            center: center.unwrap_or(Vector3::ZERO),
43            scale_factor: Matrix3::new(
44                [vec.0, 0.0, 0.0],
45                [0.0, vec.1, 0.0],
46                [0.0, 0.0, vec.2],
47            )
48        }
49    }
50
51    pub fn identity() -> Space {
52        Space{
53            rotation: Quaternion::f32_identity(),
54            center: Vector3::ZERO,
55            scale_factor: Matrix3::identity()
56        }
57    }
58
59    // get a space representing both self and other
60    // self is in other
61    // move self to other
62    // the resulting matrix can represent the points in other's superspace (kind of)
63    // you see you now have the representation in others superspace but without the rotation
64    // that is only applied when you then go to the superspace and join it to
65    
66    // Think of a space like a 2D plane that can be moved anywhere and rotated around its center and scaled
67    // The joining of two spaces basically means that now the space is moved to where it should be in the 
68    // superspace of the other matrix it is being joined with and it has the rotation of other bc:
69    // __ no rotation
70    // \_ center rotated
71    // \
72    //  \ points in space rotated to match change
73    // Because the other space has some position within its superspace that is also added to the position
74    pub fn join(&self, other: Space) -> Space {
75        // Diagonalize scl mat
76        let sf = self.scale_factor.transpose().m;
77
78        let left = other.rotation * Quaternion::new_vector_real(Vector3::from(sf[0]), 0.0) * other.rotation.reciprocal();
79        let up = other.rotation * Quaternion::new_vector_real(Vector3::from(sf[1]), 0.0) * other.rotation.reciprocal();
80        let frd = other.rotation * Quaternion::new_vector_real(Vector3::from(sf[2]), 0.0) * other.rotation.reciprocal();
81
82        let scl_mat = Matrix3::new(
83            [left.0, up.0, frd.0],
84            [left.1, up.1, frd.1],
85            [left.2, up.2, frd.2]
86        );
87
88        let rotated_center = other.rotation * Quaternion::new_vector_real(self.center, 0.0) * other.rotation.reciprocal();
89
90        Space{
91            rotation: other.rotation * self.rotation,
92            center: other.center + Vector3(rotated_center.0, rotated_center.1, rotated_center.2),
93            scale_factor: Matrix3::from(other.rotation).transpose() * (scl_mat * other.scale_factor)
94        }
95    }
96    
97    // self in other
98    // move other to self
99    // Think of a space like a 2D plane that can be moved anywhere and rotated around its center and scaled
100    // The joining in reverse two spaces basically means that now the superspace is moved space is moved to where it should be in the 
101    // superspace of the other matrix it is being joined with and it has the rotation of other bc:
102    // __ no rotation
103    // \_ center rotated
104    // \
105    //  \ points in space rotated to match change
106    // Because the other space has some position within its superspace that is also added to the position
107    pub fn join_reverse(&self, other: Space) -> Space {
108        let sf = other.scale_factor.transpose().m;
109
110        let left = self.rotation.reciprocal() * Quaternion::new_vector_real(Vector3::from(sf[0]), 0.0) * self.rotation;
111        let up = self.rotation.reciprocal() * Quaternion::new_vector_real(Vector3::from(sf[1]), 0.0) * self.rotation;
112        let frd = self.rotation.reciprocal() * Quaternion::new_vector_real(Vector3::from(sf[2]), 0.0) * self.rotation;
113
114        let scl_mat = Matrix3::new(
115            [left.0, up.0, frd.0],
116            [left.1, up.1, frd.1],
117            [left.2, up.2, frd.2]
118        );
119
120        let rotated_center = self.rotation.reciprocal() * Quaternion::new_vector_real(other.center - self.center, 0.0) * self.rotation;
121        Space{
122            rotation: self.rotation.reciprocal() * other.rotation,
123            center: Vector3(rotated_center.0, rotated_center.1, rotated_center.2),
124            scale_factor: Matrix3::from(self.rotation) * (scl_mat * self.scale_factor.inverse())
125        }
126    }
127
128    pub fn build(&self) -> Matrix4<f32>{
129        let res: Matrix4<f32> = Matrix4::from(Matrix3::from(self.rotation)); // rot_mat
130
131        // res * scale_mat
132        let scale_mat = Matrix4::from(self.scale_factor);
133
134        // res.m[0][0] *= self.scale_factor.0;
135        // res.m[1][0] *= self.scale_factor.0;
136        // res.m[2][0] *= self.scale_factor.0;
137
138        // res.m[0][1] *= self.scale_factor.1;
139        // res.m[1][1] *= self.scale_factor.1;
140        // res.m[2][1] *= self.scale_factor.1;
141        
142        // res.m[0][2] *= self.scale_factor.2;
143        // res.m[1][2] *= self.scale_factor.2;
144        // res.m[2][2] *= self.scale_factor.2;
145        
146        let translation_mat = Matrix4::new(
147            [1.0, 0.0, 0.0, self.center.0],
148            [0.0, 1.0, 0.0, self.center.1],
149            [0.0, 0.0, 1.0, self.center.2],
150            [0.0, 0.0, 0.0, 1.0],
151        );
152        // // translation_mat * res
153        // res.m[0][3] = self.center.0;
154        // res.m[1][3] = self.center.1;
155        // res.m[2][3] = self.center.2;
156        
157        translation_mat * res * scale_mat
158    }
159
160    /// translate by a Vector3
161    pub fn translate(&mut self, vector: Vector3<f32>){
162        self.center = self.center + vector;
163    }
164
165    /// rotate by a quaternion
166    pub fn rotate(&mut self, quaternion: Quaternion<f32>){ // where the vector3 is normalized is quaternion normalized
167        self.rotation = quaternion.unit_quaternion() * self.rotation.unit_quaternion();
168    }
169
170    /// offset = camera offset from center of space
171    pub fn look_at(&mut self, look_at: Vector3<f32>){
172        self.rotation = Quaternion::camera_look_at_xy(self.center, look_at);
173    }
174}
175
176impl Add for Space {
177    type Output = Self;
178
179    fn add(self, rhs: Self) -> Self::Output {
180        self.join(rhs)
181    }
182}
183
184impl Sub for Space {
185    type Output = Self;
186
187    fn sub(self, rhs: Self) -> Self::Output {
188        self.join_reverse(rhs)
189    }
190}