bb_geometry/linear_algebra/matrix4x4/mod.rs
1use crate::linear_algebra::matrix3x3::Matrix3x3;
2use crate::linear_algebra::vector3::Vector3;
3use std::ops::{Index, IndexMut, Mul, Sub};
4use crate::linear_algebra::EPSILON;
5
6/// Represents a 4x4 linear_algebra with f32 values in row-major order.
7///
8/// Internal representation is `[[f32; 4]; 4]`.
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct Matrix4x4 {
11 /// The linear_algebra data stored as an array of arrays (rows).
12 data: [[f32; 4]; 4],
13}
14
15impl Matrix4x4 {
16 /// Creates a new 4x4 linear_algebra from the provided 2D array data.
17 ///
18 /// The data should be in row-major order: `data[row][column]`.
19 ///
20 /// # Example
21 /// ```
22 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
23 /// let linear_algebra = Matrix4x4::new([
24 /// [1.0, 2.0, 3.0, 4.0],
25 /// [5.0, 6.0, 7.0, 8.0],
26 /// [9.0, 10.0, 11.0, 12.0],
27 /// [13.0, 14.0, 15.0, 16.0],
28 /// ]);
29 /// assert_eq!(linear_algebra.get(0, 1), 2.0);
30 /// ```
31 pub fn new(data: [[f32; 4]; 4]) -> Self {
32 Self { data }
33 }
34
35 /// Creates an identity linear_algebra (1.0 on the diagonal, 0.0 elsewhere).
36 ///
37 /// # Example
38 /// ```
39 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
40 /// let identity = Matrix4x4::identity();
41 /// assert_eq!(identity.get(0, 0), 1.0);
42 /// assert_eq!(identity.get(1, 1), 1.0);
43 /// assert_eq!(identity.get(0, 1), 0.0);
44 /// ```
45 pub fn identity() -> Self {
46 Self {
47 data: [
48 [1.0, 0.0, 0.0, 0.0],
49 [0.0, 1.0, 0.0, 0.0],
50 [0.0, 0.0, 1.0, 0.0],
51 [0.0, 0.0, 0.0, 1.0],
52 ],
53 }
54 }
55
56 /// Creates a zero linear_algebra (all elements are 0.0).
57 ///
58 /// # Example
59 /// ```
60 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
61 /// let zero = Matrix4x4::zero();
62 /// assert_eq!(zero.get(2, 3), 0.0);
63 /// ```
64 pub fn zero() -> Self {
65 Self {
66 data: [[0.0; 4]; 4], // More concise way to initialize with zeros
67 }
68 }
69
70 /// Gets the value at the specified row and column using a method call.
71 ///
72 /// Prefer using the index operator `linear_algebra[(row, col)]` for more idiomatic access.
73 ///
74 /// # Panics
75 /// Panics if `row` or `col` are out of bounds (>= 4).
76 ///
77 /// # Example
78 /// ```
79 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
80 /// let linear_algebra = Matrix4x4::identity();
81 /// assert_eq!(linear_algebra.get(1, 1), 1.0);
82 /// ```
83 #[inline]
84 pub fn get(&self, row: usize, col: usize) -> f32 {
85 assert!(row < 4, "Row index out of bounds: {}", row);
86 assert!(col < 4, "Column index out of bounds: {}", col);
87 self.data[row][col]
88 }
89
90 /// Sets the value at the specified row and column using a method call.
91 ///
92 /// Prefer using the mutable index operator `linear_algebra[(row, col)] = value`
93 /// for more idiomatic modification.
94 ///
95 /// # Panics
96 /// Panics if `row` or `col` are out of bounds (>= 4).
97 ///
98 /// # Example
99 /// ```
100 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
101 /// let mut linear_algebra = Matrix4x4::zero();
102 /// linear_algebra.set(2, 3, 5.0);
103 /// assert_eq!(linear_algebra.get(2, 3), 5.0);
104 /// ```
105 #[inline]
106 pub fn set(&mut self, row: usize, col: usize, value: f32) {
107 assert!(row < 4, "Row index out of bounds: {}", row);
108 assert!(col < 4, "Column index out of bounds: {}", col);
109 self.data[row][col] = value;
110 }
111
112 /// Returns the transpose of the linear_algebra.
113 ///
114 /// The element at `(row, col)` in the original linear_algebra becomes
115 /// the element at `(col, row)` in the transposed linear_algebra.
116 /// Does not modify the original linear_algebra.
117 ///
118 /// # Example
119 /// ```
120 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
121 /// let linear_algebra = Matrix4x4::new([
122 /// [1.0, 2.0, 3.0, 4.0],
123 /// [5.0, 6.0, 7.0, 8.0],
124 /// [9.0, 10.0, 11.0, 12.0],
125 /// [13.0, 14.0, 15.0, 16.0],
126 /// ]);
127 /// let transposed = linear_algebra.transpose();
128 /// assert_eq!(transposed.get(0, 1), 5.0); // linear_algebra.get(1, 0)
129 /// assert_eq!(transposed.get(1, 0), 2.0); // linear_algebra.get(0, 1)
130 /// assert_eq!(transposed.get(3, 2), 12.0); // linear_algebra.get(2, 3)
131 /// ```
132 pub fn transpose(&self) -> Self {
133 let mut result_data = [[0.0f32; 4]; 4];
134 for i in 0..4 {
135 for j in 0..4 {
136 result_data[j][i] = self.data[i][j]; // Swap indices
137 }
138 }
139 Self { data: result_data }
140 }
141
142 /// Performs linear_algebra multiplication: `self * other`.
143 ///
144 /// This method delegates to the `*` operator implementation provided by `Mul`.
145 /// Does not modify the original matrices.
146 ///
147 /// # Example
148 /// ```
149 /// use crate::bb_geometry::linear_algebra::matrix4x4::*;
150 /// let a = Matrix4x4::new([
151 /// [1.0, 2.0, 0.0, 0.0],
152 /// [3.0, 4.0, 0.0, 0.0],
153 /// [0.0, 0.0, 1.0, 0.0],
154 /// [0.0, 0.0, 0.0, 1.0],
155 /// ]);
156 /// let b = Matrix4x4::new([
157 /// [5.0, 6.0, 0.0, 0.0],
158 /// [7.0, 8.0, 0.0, 0.0],
159 /// [0.0, 0.0, 1.0, 0.0],
160 /// [0.0, 0.0, 0.0, 1.0],
161 /// ]);
162 /// let result = a.multiply(&b);
163 /// // 1*5 + 2*7 = 19
164 /// assert_eq!(result.get(0, 0), 19.0);
165 /// // 1*6 + 2*8 = 22
166 /// assert_eq!(result.get(0, 1), 22.0);
167 /// // 3*5 + 4*7 = 15 + 28 = 43
168 /// assert_eq!(result.get(1, 0), 43.0);
169 /// // 3*6 + 4*8 = 18 + 32 = 50
170 /// assert_eq!(result.get(1, 1), 50.0);
171 /// ```
172 #[inline]
173 pub fn multiply(&self, other: &Self) -> Self {
174 // Delegate to the Mul trait implementation
175 self * other
176 }
177
178 pub fn is_almost_zero(&self) -> bool {
179 self.data
180 .iter()
181 .all(|row| row.iter().all(|&value| value.abs() <= EPSILON))
182 }
183
184 /// Creates an affine transformation (rotation and translation) in 3D as 4x4 linear_algebra
185 ///
186 /// # Example
187 ///```
188 /// use bb_geometry::linear_algebra::matrix4x4::*;
189 /// use bb_geometry::linear_algebra::matrix3x3::*;
190 /// use bb_geometry::linear_algebra::vector3::*;
191 /// use bb_geometry::linear_algebra::vector4::*;
192 ///
193 /// let r = Matrix3x3::rotation_from_euler_angles(0.2, 0.4, -0.2);
194 /// let a = Vector3::new([5.0, 4.0, 3.0]);
195 /// let affine_transformation = Matrix4x4::affine_transformation(&r, &a);
196 /// let inverse_affine_transformation = Matrix4x4::affine_transformation(&(r.transpose()), &-(r.transpose() * a));
197 ///
198 /// let should_be_zero_matrix = inverse_affine_transformation * affine_transformation - Matrix4x4::identity();
199 ///
200 /// assert!(should_be_zero_matrix.is_almost_zero());
201 /// ```
202 pub fn affine_transformation(rotation: &Matrix3x3, translation: &Vector3) -> Self {
203 Matrix4x4::new([
204 [
205 rotation[(0, 0)],
206 rotation[(0, 1)],
207 rotation[(0, 2)],
208 translation[0],
209 ],
210 [
211 rotation[(1, 0)],
212 rotation[(1, 1)],
213 rotation[(1, 2)],
214 translation[1],
215 ],
216 [
217 rotation[(2, 0)],
218 rotation[(2, 1)],
219 rotation[(2, 2)],
220 translation[2],
221 ],
222 [0.0, 0.0, 0.0, 1.0],
223 ])
224 }
225
226 /// Swaps basis directions
227 ///
228 /// # Example
229 /// ```
230 /// use bb_geometry::linear_algebra::matrix4x4::Matrix4x4;
231 /// use bb_geometry::linear_algebra::vector4::{E_W, E_X, E_Y, E_Z};
232 /// let swap_matrix = Matrix4x4::axes_swap(2, 3);
233 ///
234 /// let transformed_e_x = swap_matrix * E_X;
235 /// let transformed_e_y = swap_matrix * E_Y;
236 /// let transformed_e_z = swap_matrix * E_Z;
237 /// let transformed_e_w = swap_matrix * E_W;
238 /// assert!((transformed_e_x - E_X).is_almost_zero());
239 /// assert!((transformed_e_y - E_Y).is_almost_zero());
240 /// assert!((transformed_e_z - E_W).is_almost_zero());
241 /// assert!((transformed_e_w - E_Z).is_almost_zero());
242 /// ```
243 pub fn axes_swap(dir1: usize, dir2: usize) -> Self {
244 assert!(dir1 < 4, "Direction index out of bounds: {}", dir1);
245 assert!(dir2 < 4, "Direction index out of bounds: {}", dir2);
246 let mut swap_matrix = Matrix4x4::identity();
247 swap_matrix.set(dir1, dir1, 0.0);
248 swap_matrix.set(dir2, dir2, 0.0);
249 swap_matrix.set(dir1, dir2, 1.0);
250 swap_matrix.set(dir2, dir1, 1.0);
251 swap_matrix
252 }
253}
254
255// --- Trait Implementations ---
256
257/// Allows accessing linear_algebra elements using tuple indexing `linear_algebra[(row, col)]`.
258///
259/// # Panics
260/// Panics if `row` or `col` are out of bounds (>= 4).
261///
262/// # Example
263/// ```
264/// use crate::bb_geometry::linear_algebra::matrix4x4::*;
265/// let linear_algebra = Matrix4x4::new([
266/// [1.0, 2.0, 3.0, 4.0],
267/// [5.0, 6.0, 7.0, 8.0],
268/// [9.0, 10.0, 11.0, 12.0],
269/// [13.0, 14.0, 15.0, 16.0],
270/// ]);
271/// assert_eq!(linear_algebra[(1, 2)], 7.0);
272/// ```
273impl Index<(usize, usize)> for Matrix4x4 {
274 type Output = f32;
275
276 #[inline]
277 fn index(&self, index: (usize, usize)) -> &Self::Output {
278 assert!(index.0 < 4, "Row index out of bounds: {}", index.0);
279 assert!(index.1 < 4, "Column index out of bounds: {}", index.1);
280 &self.data[index.0][index.1]
281 }
282}
283
284/// Allows mutating linear_algebra elements using tuple indexing `linear_algebra[(row, col)] = value`.
285///
286/// # Panics
287/// Panics if `row` or `col` are out of bounds (>= 4).
288///
289/// # Example
290/// ```
291/// use crate::bb_geometry::linear_algebra::matrix4x4::*;
292/// let mut linear_algebra = Matrix4x4::zero();
293/// linear_algebra[(2, 1)] = 99.0;
294/// assert_eq!(linear_algebra[(2, 1)], 99.0);
295/// ```
296impl IndexMut<(usize, usize)> for Matrix4x4 {
297 #[inline]
298 fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
299 assert!(index.0 < 4, "Row index out of bounds: {}", index.0);
300 assert!(index.1 < 4, "Column index out of bounds: {}", index.1);
301 &mut self.data[index.0][index.1]
302 }
303}
304
305/// Implements linear_algebra multiplication using the `*` operator (`&Matrix4x4 * &Matrix4x4`).
306///
307/// This is often the most convenient way to perform linear_algebra multiplication.
308///
309/// # Example
310/// ```
311/// use crate::bb_geometry::linear_algebra::matrix4x4::*;
312/// let a = Matrix4x4::identity();
313/// let b = Matrix4x4::new([
314/// [1.0, 2.0, 3.0, 4.0],
315/// [5.0, 6.0, 7.0, 8.0],
316/// [9.0, 10.0, 11.0, 12.0],
317/// [13.0, 14.0, 15.0, 16.0],
318/// ]);
319/// let result = &a * &b; // Multiplying by identity returns the original
320/// assert_eq!(result, b);
321///
322/// let c = Matrix4x4::new([
323/// [2.0, 0.0, 0.0, 0.0],
324/// [0.0, 2.0, 0.0, 0.0],
325/// [0.0, 0.0, 2.0, 0.0],
326/// [0.0, 0.0, 0.0, 2.0],
327/// ]);
328/// let scaled_b = &b * &c; // Scale b by 2
329/// assert_eq!(scaled_b[(0, 0)], 2.0);
330/// assert_eq!(scaled_b[(1, 1)], 12.0);
331/// ```
332impl Mul<&Matrix4x4> for &Matrix4x4 {
333 type Output = Matrix4x4;
334
335 fn mul(self, rhs: &Matrix4x4) -> Self::Output {
336 let mut result_data = [[0.0f32; 4]; 4];
337 for i in 0..4 {
338 // Result row
339 for j in 0..4 {
340 // Result column
341 let mut sum = 0.0;
342 for k in 0..4 {
343 // Inner dimension K
344 // Accessing via index trait: sum += self[(i, k)] * rhs[(k, j)];
345 // Accessing via internal data:
346 sum += self.data[i][k] * rhs.data[k][j];
347 }
348 result_data[i][j] = sum;
349 }
350 }
351 Matrix4x4 { data: result_data }
352 }
353}
354
355// Optional: Implement multiplication for owned values as well, typically by delegating
356// It's common to implement M * M, M * &M, &M * M by calling the &M * &M version.
357impl Mul<Matrix4x4> for Matrix4x4 {
358 type Output = Matrix4x4;
359
360 #[inline]
361 fn mul(self, rhs: Matrix4x4) -> Self::Output {
362 // Delegate to the reference implementation
363 &self * &rhs
364 }
365}
366
367impl Mul<&Matrix4x4> for Matrix4x4 {
368 type Output = Matrix4x4;
369
370 #[inline]
371 fn mul(self, rhs: &Matrix4x4) -> Self::Output {
372 &self * rhs
373 }
374}
375
376impl Mul<Matrix4x4> for &Matrix4x4 {
377 type Output = Matrix4x4;
378
379 #[inline]
380 fn mul(self, rhs: Matrix4x4) -> Self::Output {
381 self * &rhs
382 }
383}
384
385
386impl Sub<&Matrix4x4> for &Matrix4x4 {
387 type Output = Matrix4x4;
388 #[inline]
389 fn sub(self, rhs: &Matrix4x4) -> Self::Output {
390 Matrix4x4::new([
391 [
392 self[(0, 0)] - rhs[(0, 0)],
393 self[(0, 1)] - rhs[(0, 1)],
394 self[(0, 2)] - rhs[(0, 2)],
395 self[(0, 3)] - rhs[(0, 3)],
396 ],
397 [
398 self[(1, 0)] - rhs[(1, 0)],
399 self[(1, 1)] - rhs[(1, 1)],
400 self[(1, 2)] - rhs[(1, 2)],
401 self[(1, 3)] - rhs[(1, 3)],
402 ],
403 [
404 self[(2, 0)] - rhs[(2, 0)],
405 self[(2, 1)] - rhs[(2, 1)],
406 self[(2, 2)] - rhs[(2, 2)],
407 self[(2, 3)] - rhs[(2, 3)],
408 ],
409 [
410 self[(3, 0)] - rhs[(3, 0)],
411 self[(3, 1)] - rhs[(3, 1)],
412 self[(3, 2)] - rhs[(3, 2)],
413 self[(3, 3)] - rhs[(3, 3)],
414 ],
415 ])
416 }
417}
418
419impl Sub<Matrix4x4> for Matrix4x4 {
420 type Output = Matrix4x4;
421 #[inline]
422 fn sub(self, rhs: Matrix4x4) -> Self::Output {
423 &self - &rhs
424 }
425}
426
427impl Sub<&Matrix4x4> for Matrix4x4 {
428 type Output = Matrix4x4;
429 #[inline]
430 fn sub(self, rhs: &Matrix4x4) -> Self::Output {
431 &self - rhs
432 }
433}
434
435impl Sub<Matrix4x4> for &Matrix4x4 {
436 type Output = Matrix4x4;
437 #[inline]
438 fn sub(self, rhs: Matrix4x4) -> Self::Output {
439 self - &rhs
440 }
441}