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}