1#![allow(dead_code)]
2
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5#[derive(Debug, Clone, PartialEq)]
6pub struct TransformationMatrix {
7 matrix: [[f64; 4]; 3],
9}
10
11impl TransformationMatrix {
12 #[must_use]
14 pub const fn matrix(&self) -> [[f64; 4]; 3] {
15 self.matrix
16 }
17
18 #[must_use]
20 pub fn matrix_mut(&mut self) -> &mut [[f64; 4]; 3] {
21 &mut self.matrix
22 }
23
24 pub fn set_matrix(&mut self, new_matrix: [[f64; 4]; 3]) {
26 self.matrix = new_matrix;
27 }
28
29 #[must_use]
31 pub const fn identity() -> Self {
32 Self {
33 matrix: [
34 [1.0, 0.0, 0.0, 0.0],
35 [0.0, 1.0, 0.0, 0.0],
36 [0.0, 0.0, 1.0, 0.0],
37 ],
38 }
39 }
40
41 #[must_use]
43 pub const fn from_matrix(matrix: [[f64; 4]; 3]) -> Self {
44 Self { matrix }
45 }
46
47 #[must_use]
53 pub fn rotation_x(deg: f64) -> Self {
54 assert!(deg.is_finite(), "The amount of degrees is not finite");
55 let (s, c) = deg.to_radians().sin_cos();
56 Self {
57 matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, c, -s, 0.0], [0.0, s, c, 0.0]],
58 }
59 }
60
61 #[must_use]
67 pub fn rotation_y(deg: f64) -> Self {
68 assert!(deg.is_finite(), "The amount of degrees is not finite");
69 let (s, c) = deg.to_radians().sin_cos();
70 Self {
71 matrix: [[c, 0.0, s, 0.0], [0.0, 1.0, 0.0, 0.0], [-s, 0.0, c, 0.0]],
72 }
73 }
74
75 #[must_use]
81 pub fn rotation_z(deg: f64) -> Self {
82 assert!(deg.is_finite(), "The amount of degrees is not finite");
83 let c = deg.to_radians().cos();
84 let s = deg.to_radians().sin();
85 Self {
86 matrix: [[c, -s, 0.0, 0.0], [s, c, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]],
87 }
88 }
89
90 #[must_use]
94 pub fn translation(x: f64, y: f64, z: f64) -> Self {
95 assert!(
96 x.is_finite() && y.is_finite() && z.is_finite(),
97 "One or more of the arguments is not finite"
98 );
99 Self {
100 matrix: [[1.0, 0.0, 0.0, x], [0.0, 1.0, 0.0, y], [0.0, 0.0, 1.0, z]],
101 }
102 }
103
104 #[must_use]
110 pub fn magnify(f: f64) -> Self {
111 assert!(f.is_finite(), "The factor is not finite");
112 Self {
113 matrix: [[f, 0.0, 0.0, 0.0], [0.0, f, 0.0, 0.0], [0.0, 0.0, f, 0.0]],
114 }
115 }
116
117 #[must_use]
125 pub fn scale(x: f64, y: f64, z: f64) -> Self {
126 assert!(
127 x.is_finite() && y.is_finite() && z.is_finite(),
128 "One or more of the arguments is not finite"
129 );
130 Self {
131 matrix: [[x, 0.0, 0.0, 0.0], [0.0, y, 0.0, 0.0], [0.0, 0.0, z, 0.0]],
132 }
133 }
134
135 pub fn multiply_translation(&mut self, factors: (f64, f64, f64)) {
138 self.matrix[0][3] *= factors.0;
139 self.matrix[1][3] *= factors.1;
140 self.matrix[2][3] *= factors.2;
141 }
142
143 #[must_use]
148 pub fn apply(&self, pos: (f64, f64, f64)) -> (f64, f64, f64) {
149 (
150 pos.2.mul_add(
151 self.matrix[0][2],
152 pos.0.mul_add(self.matrix[0][0], pos.1 * self.matrix[0][1]),
153 ) + self.matrix[0][3],
154 pos.2.mul_add(
155 self.matrix[1][2],
156 pos.0.mul_add(self.matrix[1][0], pos.1 * self.matrix[1][1]),
157 ) + self.matrix[1][3],
158 pos.2.mul_add(
159 self.matrix[2][2],
160 pos.0.mul_add(self.matrix[2][0], pos.1 * self.matrix[2][1]),
161 ) + self.matrix[2][3],
162 )
163 }
164
165 #[must_use]
168 pub fn combine(&self, other: &Self) -> Self {
169 Self {
170 matrix: [
171 [
172 other.matrix[0][2].mul_add(
173 self.matrix[2][0],
174 other.matrix[0][0]
175 .mul_add(self.matrix[0][0], other.matrix[0][1] * self.matrix[1][0]),
176 ),
177 other.matrix[0][2].mul_add(
178 self.matrix[2][1],
179 other.matrix[0][0]
180 .mul_add(self.matrix[0][1], other.matrix[0][1] * self.matrix[1][1]),
181 ),
182 other.matrix[0][2].mul_add(
183 self.matrix[2][2],
184 other.matrix[0][0]
185 .mul_add(self.matrix[0][2], other.matrix[0][1] * self.matrix[1][2]),
186 ),
187 other.matrix[0][2].mul_add(
188 self.matrix[2][3],
189 other.matrix[0][0]
190 .mul_add(self.matrix[0][3], other.matrix[0][1] * self.matrix[1][3]),
191 ) + other.matrix[0][3],
192 ],
193 [
194 other.matrix[1][2].mul_add(
195 self.matrix[2][0],
196 other.matrix[1][0]
197 .mul_add(self.matrix[0][0], other.matrix[1][1] * self.matrix[1][0]),
198 ),
199 other.matrix[1][2].mul_add(
200 self.matrix[2][1],
201 other.matrix[1][0]
202 .mul_add(self.matrix[0][1], other.matrix[1][1] * self.matrix[1][1]),
203 ),
204 other.matrix[1][2].mul_add(
205 self.matrix[2][2],
206 other.matrix[1][0]
207 .mul_add(self.matrix[0][2], other.matrix[1][1] * self.matrix[1][2]),
208 ),
209 other.matrix[1][2].mul_add(
210 self.matrix[2][3],
211 other.matrix[1][0]
212 .mul_add(self.matrix[0][3], other.matrix[1][1] * self.matrix[1][3]),
213 ) + other.matrix[1][3],
214 ],
215 [
216 other.matrix[2][2].mul_add(
217 self.matrix[2][0],
218 other.matrix[2][0]
219 .mul_add(self.matrix[0][0], other.matrix[2][1] * self.matrix[1][0]),
220 ),
221 other.matrix[2][2].mul_add(
222 self.matrix[2][1],
223 other.matrix[2][0]
224 .mul_add(self.matrix[0][1], other.matrix[2][1] * self.matrix[1][1]),
225 ),
226 other.matrix[2][2].mul_add(
227 self.matrix[2][2],
228 other.matrix[2][0]
229 .mul_add(self.matrix[0][2], other.matrix[2][1] * self.matrix[1][2]),
230 ),
231 other.matrix[2][2].mul_add(
232 self.matrix[2][3],
233 other.matrix[2][0]
234 .mul_add(self.matrix[0][3], other.matrix[2][1] * self.matrix[1][3]),
235 ) + other.matrix[2][3],
236 ],
237 ],
238 }
239 }
240}
241
242#[cfg(test)]
243#[allow(clippy::print_stdout, clippy::use_debug)]
244mod tests {
245 use super::TransformationMatrix;
246
247 #[test]
248 fn identity() {
249 let pos = (1.0, 1.0, 1.0);
250 let new_pos = TransformationMatrix::identity().apply(pos);
251 assert_eq!(pos, new_pos);
252 }
253
254 #[test]
255 fn combination() {
256 let pos = (10.0, 0.0, 0.0);
257 let new_pos = TransformationMatrix::rotation_y(90.0)
258 .combine(&TransformationMatrix::translation(0.0, 0.0, 10.0))
259 .apply(pos);
260 assert!(close_tuple(new_pos, (0.0, 0.0, 0.0)));
261 }
262 #[test]
263 fn rot_x() {
264 let mut pos = (0.0, 10.0, 0.0);
266 let mut new_pos = TransformationMatrix::rotation_x(90.0).apply(pos);
267 println!("{new_pos:?}");
268 assert!(close_tuple(new_pos, (0.0, 0.0, 10.0)));
269 pos = (0.0, 0.0, 10.0);
271 new_pos = TransformationMatrix::rotation_x(90.0).apply(pos);
272 println!("{new_pos:?}");
273 assert!(close_tuple(new_pos, (0.0, -10.0, 0.0)));
274 pos = (0.0, 0.0, 10.0);
276 new_pos = TransformationMatrix::rotation_x(-90.0).apply(pos);
277 println!("{new_pos:?}");
278 assert!(close_tuple(new_pos, (0.0, 10.0, 0.0)));
279 pos = (0.0, 0.0, 10.0);
281 new_pos = TransformationMatrix::rotation_x(180.0).apply(pos);
282 println!("{new_pos:?}");
283 assert!(close_tuple(new_pos, (0.0, 0.0, -10.0)));
284 pos = (0.0, 0.0, 10.0);
286 new_pos = TransformationMatrix::rotation_x(-180.0).apply(pos);
287 println!("{new_pos:?}");
288 assert!(close_tuple(new_pos, (0.0, 0.0, -10.0)));
289 pos = (0.0, 0.0, 10.0);
291 new_pos = TransformationMatrix::rotation_x(360.0).apply(pos);
292 println!("{new_pos:?}");
293 assert!(close_tuple(new_pos, (0.0, 0.0, 10.0)));
294 pos = (0.0, 0.0, -1.0);
296 new_pos = TransformationMatrix::rotation_x(44.5).apply(pos);
297 let end = (
298 0.0,
299 44.5_f64.to_radians().sin(),
300 (-44.5_f64).to_radians().cos(),
301 );
302 println!("{new_pos:?} vs {end:?}");
303 assert!(close_tuple(new_pos, end));
304 pos = (0.0, 0.0, -1.0);
306 new_pos = TransformationMatrix::rotation_x(44.5)
307 .combine(&TransformationMatrix::rotation_x(45.5))
308 .apply(pos);
309 println!("{new_pos:?}");
310 assert!(close_tuple(new_pos, (0.0, 1.0, 0.0)));
311 }
312 #[test]
313 fn rot_y() {
314 let mut pos = (10.0, 0.0, 0.0);
316 let mut new_pos = TransformationMatrix::rotation_y(90.0).apply(pos);
317 println!("{new_pos:?}");
318 assert!(close_tuple(new_pos, (0.0, 0.0, -10.0)));
319 pos = (0.0, 0.0, 10.0);
321 new_pos = TransformationMatrix::rotation_y(90.0).apply(pos);
322 println!("{new_pos:?}");
323 assert!(close_tuple(new_pos, (10.0, 0.0, 0.0)));
324 pos = (0.0, 0.0, 10.0);
326 new_pos = TransformationMatrix::rotation_y(-90.0).apply(pos);
327 println!("{new_pos:?}");
328 assert!(close_tuple(new_pos, (-10.0, 0.0, 0.0)));
329 pos = (0.0, 0.0, 10.0);
331 new_pos = TransformationMatrix::rotation_y(180.0).apply(pos);
332 println!("{new_pos:?}");
333 assert!(close_tuple(new_pos, (0.0, 0.0, -10.0)));
334 pos = (0.0, 0.0, 10.0);
336 new_pos = TransformationMatrix::rotation_y(-180.0).apply(pos);
337 println!("{new_pos:?}");
338 assert!(close_tuple(new_pos, (0.0, 0.0, -10.0)));
339 pos = (0.0, 0.0, 10.0);
341 new_pos = TransformationMatrix::rotation_y(360.0).apply(pos);
342 println!("{new_pos:?}");
343 assert!(close_tuple(new_pos, (0.0, 0.0, 10.0)));
344 pos = (0.0, 0.0, -1.0);
346 new_pos = TransformationMatrix::rotation_y(44.5).apply(pos);
347 let end = (
348 (-44.5_f64).to_radians().sin(),
349 0.0,
350 (-44.5_f64).to_radians().cos(),
351 );
352 println!("{new_pos:?} vs {end:?}");
353 assert!(close_tuple(new_pos, end));
354 pos = (0.0, 0.0, -1.0);
356 new_pos = TransformationMatrix::rotation_y(44.5)
357 .combine(&TransformationMatrix::rotation_y(45.5))
358 .apply(pos);
359 println!("{new_pos:?}");
360 assert!(close_tuple(new_pos, (-1.0, 0.0, 0.0)));
361 }
362
363 #[test]
364 fn rot_z() {
365 let mut pos = (10.0, 0.0, 0.0);
367 let mut new_pos = TransformationMatrix::rotation_z(90.0).apply(pos);
368 println!("{new_pos:?}");
369 assert!(close_tuple(new_pos, (0.0, 10.0, 0.0)));
370 pos = (0.0, 10.0, 0.0);
372 new_pos = TransformationMatrix::rotation_z(90.0).apply(pos);
373 println!("{new_pos:?}");
374 assert!(close_tuple(new_pos, (-10.0, 0.0, 0.0)));
375 pos = (0.0, 10.0, 0.0);
377 new_pos = TransformationMatrix::rotation_z(-90.0).apply(pos);
378 println!("{new_pos:?}");
379 assert!(close_tuple(new_pos, (10.0, 0.0, 0.0)));
380 pos = (0.0, 10.0, 0.0);
382 new_pos = TransformationMatrix::rotation_z(180.0).apply(pos);
383 println!("{new_pos:?}");
384 assert!(close_tuple(new_pos, (0.0, -10.0, 0.0)));
385 pos = (0.0, 10.0, 0.0);
387 new_pos = TransformationMatrix::rotation_z(-180.0).apply(pos);
388 println!("{new_pos:?}");
389 assert!(close_tuple(new_pos, (0.0, -10.0, 0.0)));
390 pos = (10.0, 0.0, 0.0);
392 new_pos = TransformationMatrix::rotation_z(360.0).apply(pos);
393 println!("{new_pos:?}");
394 assert!(close_tuple(new_pos, (10.0, 0.0, 0.0)));
395 pos = (0.0, -1.0, 0.0);
397 new_pos = TransformationMatrix::rotation_z(44.5).apply(pos);
398 let end = (
399 44.5_f64.to_radians().sin(),
400 (-44.5_f64).to_radians().cos(),
401 0.0,
402 );
403 println!("{new_pos:?} vs {end:?}");
404 assert!(close_tuple(new_pos, end));
405 pos = (0.0, -1.0, 0.0);
407 new_pos = TransformationMatrix::rotation_z(44.5)
408 .combine(&TransformationMatrix::rotation_z(45.5))
409 .apply(pos);
410 println!("{new_pos:?}");
411 assert!(close_tuple(new_pos, (1.0, 0.0, 0.0)));
412 }
413
414 #[test]
415 fn translation() {
416 let mut pos = (10.0, 0.0, 0.0);
417 let mut new_pos = TransformationMatrix::translation(-10.0, 0.0, 0.0).apply(pos);
418 assert!(close_tuple(new_pos, (0.0, 0.0, 0.0)));
419 pos = (-897.0, 0.0023, 1.0);
420 new_pos = TransformationMatrix::translation(897.0, -0.0023, -1.0).apply(pos);
421 assert!(close_tuple(new_pos, (0.0, 0.0, 0.0)));
422 pos = (0.0, 0.0, 0.0);
423 new_pos = TransformationMatrix::translation(0.0, 0.0, 0.0).apply(pos);
424 assert!(close_tuple(new_pos, (0.0, 0.0, 0.0)));
425 }
426
427 #[test]
428 fn magnification() {
429 let mut pos = (10.0, 0.0, 0.0);
430 let mut new_pos = TransformationMatrix::magnify(10.0).apply(pos);
431 assert!(close_tuple(new_pos, (100.0, 0.0, 0.0)));
432 pos = (-897.0, 0.0023, 1.0);
433 new_pos = TransformationMatrix::magnify(0.1).apply(pos);
434 assert!(close_tuple(new_pos, (-89.7, 0.00023, 0.1)));
435 pos = (0.0, 1.0, 0.0);
436 new_pos = TransformationMatrix::magnify(2.5).apply(pos);
437 assert!(close_tuple(new_pos, (0.0, 2.5, 0.0)));
438 }
439
440 #[test]
441 fn multiply_translation() {
442 let pos = (0.0, 0.0, 0.0);
443 let mut matrix = TransformationMatrix::translation(1.0, 2.0, -5.0);
444 matrix.multiply_translation((10.0, 5.0, -2.0));
445 let new_pos = matrix.apply(pos);
446 assert!(close_tuple(new_pos, (10.0, 10.0, 10.0)));
447 assert_eq!(
448 matrix.matrix(),
449 TransformationMatrix::translation(10.0, 10.0, 10.0).matrix()
450 );
451 }
452
453 #[test]
454 fn matrix() {
455 let normal = TransformationMatrix::rotation_x(45.0);
456 let raw = normal.matrix();
457 let from_matrix = TransformationMatrix::from_matrix(raw);
458 let mut set = TransformationMatrix::identity();
459 set.set_matrix(raw);
460 assert_eq!(normal, from_matrix);
461 assert_eq!(from_matrix, set);
462 assert_eq!(normal, set);
463 }
464
465 fn close_tuple(a: (f64, f64, f64), b: (f64, f64, f64)) -> bool {
466 close(a.0, b.0) && close(a.1, b.1) && close(a.2, b.2)
467 }
468
469 fn close(a: f64, b: f64) -> bool {
470 #[allow(clippy::float_cmp)]
471 if a == b {
472 true
473 } else if a == 0.0 || b == 0.0 {
474 (a - b) > -0.00000000000001 && (b - a) < 0.00000000000001
475 } else {
476 let dif = a / b;
477 (1.0 - dif) > -0.00000000000001 && (dif - 1.0) < 0.00000000000001
478 }
479 }
480}