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}