Skip to main content

ferroui_base/numerics/
matrix4x4.rs

1//! A 4x4 matrix (a three-dimensional transform).
2
3use std::fmt;
4use std::ops::{Add, Mul, Neg, Sub};
5
6use super::single::InvariantF32;
7use super::{Matrix3x2, Quaternion, Vector3};
8
9/// A 4x4 matrix (a three-dimensional transform).
10///
11/// The layout is the one of the reference runtime and is relied on when the
12/// value is passed to a graphics API by address: sixteen consecutive singles
13/// in row order (`m11` to `m14`, then the second, third and fourth row).
14#[derive(Clone, Copy, Debug, PartialEq, Default)]
15#[repr(C)]
16pub struct Matrix4x4 {
17    /// The first element of the first row.
18    pub m11: f32,
19    /// The second element of the first row.
20    pub m12: f32,
21    /// The third element of the first row.
22    pub m13: f32,
23    /// The fourth element of the first row.
24    pub m14: f32,
25    /// The first element of the second row.
26    pub m21: f32,
27    /// The second element of the second row.
28    pub m22: f32,
29    /// The third element of the second row.
30    pub m23: f32,
31    /// The fourth element of the second row.
32    pub m24: f32,
33    /// The first element of the third row.
34    pub m31: f32,
35    /// The second element of the third row.
36    pub m32: f32,
37    /// The third element of the third row.
38    pub m33: f32,
39    /// The fourth element of the third row.
40    pub m34: f32,
41    /// The first element of the fourth row.
42    pub m41: f32,
43    /// The second element of the fourth row.
44    pub m42: f32,
45    /// The third element of the fourth row.
46    pub m43: f32,
47    /// The fourth element of the fourth row.
48    pub m44: f32,
49}
50
51impl Matrix4x4 {
52    /// The multiplicative identity matrix.
53    pub const IDENTITY: Matrix4x4 = Matrix4x4::new(
54        1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
55    );
56
57    /// Creates a 4x4 matrix from the specified components, given in row order.
58    #[inline]
59    #[allow(clippy::too_many_arguments)]
60    pub const fn new(
61        m11: f32,
62        m12: f32,
63        m13: f32,
64        m14: f32,
65        m21: f32,
66        m22: f32,
67        m23: f32,
68        m24: f32,
69        m31: f32,
70        m32: f32,
71        m33: f32,
72        m34: f32,
73        m41: f32,
74        m42: f32,
75        m43: f32,
76        m44: f32,
77    ) -> Self {
78        Self {
79            m11,
80            m12,
81            m13,
82            m14,
83            m21,
84            m22,
85            m23,
86            m24,
87            m31,
88            m32,
89            m33,
90            m34,
91            m41,
92            m42,
93            m43,
94            m44,
95        }
96    }
97
98    /// Creates a 4x4 matrix from the specified 3x2 matrix.
99    #[inline]
100    pub const fn from_matrix3x2(value: Matrix3x2) -> Self {
101        Self::new(
102            value.m11, value.m12, 0.0, 0.0, value.m21, value.m22, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, value.m31,
103            value.m32, 0.0, 1.0,
104        )
105    }
106
107    /// Indicates whether the current matrix is the identity matrix.
108    #[inline]
109    pub fn is_identity(&self) -> bool {
110        *self == Matrix4x4::IDENTITY
111    }
112
113    /// Gets the translation component of this matrix.
114    #[inline]
115    pub fn translation(&self) -> Vector3 {
116        Vector3::new(self.m41, self.m42, self.m43)
117    }
118
119    /// Sets the translation component of this matrix.
120    #[inline]
121    pub fn set_translation(&mut self, value: Vector3) {
122        self.m41 = value.x;
123        self.m42 = value.y;
124        self.m43 = value.z;
125    }
126
127    /// Creates a matrix that rotates around an arbitrary (unit) vector.
128    ///
129    /// As in the reference runtime, the matrix is built from the quaternion
130    /// of the rotation.
131    pub fn create_from_axis_angle(axis: Vector3, angle: f32) -> Matrix4x4 {
132        let q = Quaternion::create_from_axis_angle(axis, angle);
133        Matrix4x4::create_from_quaternion(q)
134    }
135
136    /// Creates a rotation matrix from the specified quaternion rotation value.
137    pub fn create_from_quaternion(quaternion: Quaternion) -> Matrix4x4 {
138        let mut result = Matrix4x4::IDENTITY;
139
140        let xx = quaternion.x * quaternion.x;
141        let yy = quaternion.y * quaternion.y;
142        let zz = quaternion.z * quaternion.z;
143
144        let xy = quaternion.x * quaternion.y;
145        let wz = quaternion.z * quaternion.w;
146        let xz = quaternion.z * quaternion.x;
147        let wy = quaternion.y * quaternion.w;
148        let yz = quaternion.y * quaternion.z;
149        let wx = quaternion.x * quaternion.w;
150
151        result.m11 = 1.0 - 2.0 * (yy + zz);
152        result.m12 = 2.0 * (xy + wz);
153        result.m13 = 2.0 * (xz - wy);
154
155        result.m21 = 2.0 * (xy - wz);
156        result.m22 = 1.0 - 2.0 * (zz + xx);
157        result.m23 = 2.0 * (yz + wx);
158
159        result.m31 = 2.0 * (xz + wy);
160        result.m32 = 2.0 * (yz - wx);
161        result.m33 = 1.0 - 2.0 * (yy + xx);
162
163        result
164    }
165
166    /// Creates a rotation matrix from the specified yaw, pitch, and roll.
167    #[inline]
168    pub fn create_from_yaw_pitch_roll(yaw: f32, pitch: f32, roll: f32) -> Matrix4x4 {
169        let q = Quaternion::create_from_yaw_pitch_roll(yaw, pitch, roll);
170        Matrix4x4::create_from_quaternion(q)
171    }
172
173    /// Creates a view matrix: a camera at `camera_position` that looks at
174    /// `camera_target`, with `camera_up_vector` as the direction that is up
175    /// from the camera's point of view (right-handed).
176    ///
177    /// As in the reference runtime, nothing is validated: a target at the
178    /// position of the camera, or an up vector parallel to the direction of
179    /// view, gives NaN components.
180    pub fn create_look_at(camera_position: Vector3, camera_target: Vector3, camera_up_vector: Vector3) -> Matrix4x4 {
181        let zaxis = Vector3::normalize(camera_position - camera_target);
182        let xaxis = Vector3::normalize(Vector3::cross(camera_up_vector, zaxis));
183        let yaxis = Vector3::cross(zaxis, xaxis);
184
185        // [  xaxis.x  yaxis.x  zaxis.x  0 ]
186        // [  xaxis.y  yaxis.y  zaxis.y  0 ]
187        // [  xaxis.z  yaxis.z  zaxis.z  0 ]
188        // [ -xaxis.p -yaxis.p -zaxis.p  1 ]   (`.p`: the dot product with the position)
189        Matrix4x4::new(
190            xaxis.x,
191            yaxis.x,
192            zaxis.x,
193            0.0,
194            xaxis.y,
195            yaxis.y,
196            zaxis.y,
197            0.0,
198            xaxis.z,
199            yaxis.z,
200            zaxis.z,
201            0.0,
202            -Vector3::dot(xaxis, camera_position),
203            -Vector3::dot(yaxis, camera_position),
204            -Vector3::dot(zaxis, camera_position),
205            1.0,
206        )
207    }
208
209    /// Creates a perspective projection matrix based on a field of view,
210    /// aspect ratio, and near and far view plane distances (right-handed,
211    /// with a depth range of zero to one).
212    ///
213    /// `field_of_view` is the field of view in the y direction, in radians.
214    /// A far plane at positive infinity is accepted and gives the limit of
215    /// the matrix (`m33` is -1 and `m43` is `-near_plane_distance`).
216    ///
217    /// # Panics
218    /// Panics where the reference runtime throws an
219    /// `ArgumentOutOfRangeException`: when `field_of_view` is less than or
220    /// equal to zero or greater than or equal to pi, when
221    /// `near_plane_distance` or `far_plane_distance` is less than or equal to
222    /// zero, and when `near_plane_distance` is greater than or equal to
223    /// `far_plane_distance`.
224    pub fn create_perspective_field_of_view(
225        field_of_view: f32,
226        aspect_ratio: f32,
227        near_plane_distance: f32,
228        far_plane_distance: f32,
229    ) -> Matrix4x4 {
230        // The comparisons are the ones of the reference runtime: a NaN
231        // argument passes every one of them.
232        if field_of_view <= 0.0 {
233            panic!("field_of_view ('{field_of_view}') must be greater than '0'.");
234        }
235        if field_of_view >= std::f32::consts::PI {
236            panic!("field_of_view ('{field_of_view}') must be less than '{}'.", std::f32::consts::PI);
237        }
238        if near_plane_distance <= 0.0 {
239            panic!("near_plane_distance ('{near_plane_distance}') must be greater than '0'.");
240        }
241        if far_plane_distance <= 0.0 {
242            panic!("far_plane_distance ('{far_plane_distance}') must be greater than '0'.");
243        }
244        if near_plane_distance >= far_plane_distance {
245            panic!("near_plane_distance ('{near_plane_distance}') must be less than '{far_plane_distance}'.");
246        }
247
248        let height = 1.0 / (field_of_view * 0.5).tan();
249        let width = height / aspect_ratio;
250        let range = if far_plane_distance == f32::INFINITY {
251            -1.0
252        } else {
253            far_plane_distance / (near_plane_distance - far_plane_distance)
254        };
255
256        // [  w  0  0   0 ]
257        // [  0  h  0   0 ]
258        // [  0  0  r  -1 ]
259        // [  0  0  rn  0 ]   (`rn`: the range times the near plane distance)
260        Matrix4x4::new(
261            width,
262            0.0,
263            0.0,
264            0.0,
265            0.0,
266            height,
267            0.0,
268            0.0,
269            0.0,
270            0.0,
271            range,
272            -1.0,
273            0.0,
274            0.0,
275            range * near_plane_distance,
276            0.0,
277        )
278    }
279
280    /// Creates a matrix for rotating points around the X axis.
281    pub fn create_rotation_x(radians: f32) -> Matrix4x4 {
282        let c = radians.cos();
283        let s = radians.sin();
284
285        // [  1  0  0  0 ]
286        // [  0  c  s  0 ]
287        // [  0 -s  c  0 ]
288        // [  0  0  0  1 ]
289        let mut result = Matrix4x4::IDENTITY;
290        result.m22 = c;
291        result.m23 = s;
292        result.m32 = -s;
293        result.m33 = c;
294        result
295    }
296
297    /// Creates a matrix for rotating points around the X axis from a center point.
298    pub fn create_rotation_x_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
299        let c = radians.cos();
300        let s = radians.sin();
301
302        let y = center_point.y * (1.0 - c) + center_point.z * s;
303        let z = center_point.z * (1.0 - c) - center_point.y * s;
304
305        // [  1  0  0  0 ]
306        // [  0  c  s  0 ]
307        // [  0 -s  c  0 ]
308        // [  0  y  z  1 ]
309        let mut result = Matrix4x4::IDENTITY;
310        result.m22 = c;
311        result.m23 = s;
312        result.m32 = -s;
313        result.m33 = c;
314        result.m42 = y;
315        result.m43 = z;
316        result
317    }
318
319    /// Creates a matrix for rotating points around the Y axis.
320    pub fn create_rotation_y(radians: f32) -> Matrix4x4 {
321        let c = radians.cos();
322        let s = radians.sin();
323
324        // [  c  0 -s  0 ]
325        // [  0  1  0  0 ]
326        // [  s  0  c  0 ]
327        // [  0  0  0  1 ]
328        let mut result = Matrix4x4::IDENTITY;
329        result.m11 = c;
330        result.m13 = -s;
331        result.m31 = s;
332        result.m33 = c;
333        result
334    }
335
336    /// Creates a matrix for rotating points around the Y axis from a center point.
337    pub fn create_rotation_y_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
338        let c = radians.cos();
339        let s = radians.sin();
340
341        let x = center_point.x * (1.0 - c) - center_point.z * s;
342        let z = center_point.z * (1.0 - c) + center_point.x * s;
343
344        // [  c  0 -s  0 ]
345        // [  0  1  0  0 ]
346        // [  s  0  c  0 ]
347        // [  x  0  z  1 ]
348        let mut result = Matrix4x4::IDENTITY;
349        result.m11 = c;
350        result.m13 = -s;
351        result.m31 = s;
352        result.m33 = c;
353        result.m41 = x;
354        result.m43 = z;
355        result
356    }
357
358    /// Creates a matrix for rotating points around the Z axis.
359    pub fn create_rotation_z(radians: f32) -> Matrix4x4 {
360        let c = radians.cos();
361        let s = radians.sin();
362
363        // [  c  s  0  0 ]
364        // [ -s  c  0  0 ]
365        // [  0  0  1  0 ]
366        // [  0  0  0  1 ]
367        let mut result = Matrix4x4::IDENTITY;
368        result.m11 = c;
369        result.m12 = s;
370        result.m21 = -s;
371        result.m22 = c;
372        result
373    }
374
375    /// Creates a matrix for rotating points around the Z axis from a center point.
376    pub fn create_rotation_z_at(radians: f32, center_point: Vector3) -> Matrix4x4 {
377        let c = radians.cos();
378        let s = radians.sin();
379
380        let x = center_point.x * (1.0 - c) + center_point.y * s;
381        let y = center_point.y * (1.0 - c) - center_point.x * s;
382
383        // [  c  s  0  0 ]
384        // [ -s  c  0  0 ]
385        // [  0  0  1  0 ]
386        // [  x  y  0  1 ]
387        let mut result = Matrix4x4::IDENTITY;
388        result.m11 = c;
389        result.m12 = s;
390        result.m21 = -s;
391        result.m22 = c;
392        result.m41 = x;
393        result.m42 = y;
394        result
395    }
396
397    /// Creates a scaling matrix from the specified vector scale.
398    #[inline]
399    pub fn create_scale(scales: Vector3) -> Matrix4x4 {
400        Matrix4x4::create_scale_xyz(scales.x, scales.y, scales.z)
401    }
402
403    /// Creates a scaling matrix from the specified X, Y, and Z components.
404    #[inline]
405    pub fn create_scale_xyz(x_scale: f32, y_scale: f32, z_scale: f32) -> Matrix4x4 {
406        let mut result = Matrix4x4::IDENTITY;
407        result.m11 = x_scale;
408        result.m22 = y_scale;
409        result.m33 = z_scale;
410        result
411    }
412
413    /// Creates a uniform scaling matrix that scales equally on each axis.
414    #[inline]
415    pub fn create_scale_uniform(scale: f32) -> Matrix4x4 {
416        Matrix4x4::create_scale_xyz(scale, scale, scale)
417    }
418
419    /// Creates a scaling matrix with a center point.
420    pub fn create_scale_at(scales: Vector3, center_point: Vector3) -> Matrix4x4 {
421        let tx = center_point.x * (1.0 - scales.x);
422        let ty = center_point.y * (1.0 - scales.y);
423        let tz = center_point.z * (1.0 - scales.z);
424
425        let mut result = Matrix4x4::IDENTITY;
426        result.m11 = scales.x;
427        result.m22 = scales.y;
428        result.m33 = scales.z;
429        result.m41 = tx;
430        result.m42 = ty;
431        result.m43 = tz;
432        result
433    }
434
435    /// Creates a translation matrix from the specified 3-dimensional vector.
436    #[inline]
437    pub fn create_translation(position: Vector3) -> Matrix4x4 {
438        Matrix4x4::create_translation_xyz(position.x, position.y, position.z)
439    }
440
441    /// Creates a translation matrix from the specified X, Y, and Z components.
442    #[inline]
443    pub fn create_translation_xyz(x_position: f32, y_position: f32, z_position: f32) -> Matrix4x4 {
444        let mut result = Matrix4x4::IDENTITY;
445        result.m41 = x_position;
446        result.m42 = y_position;
447        result.m43 = z_position;
448        result
449    }
450
451    /// Calculates the determinant of the current 4x4 matrix.
452    pub fn get_determinant(&self) -> f32 {
453        // | a b c d |     | f g h |     | e g h |     | e f h |     | e f g |
454        // | e f g h | = a | j k l | - b | i k l | + c | i j l | - d | i j k |
455        // | i j k l |     | n o p |     | m o p |     | m n p |     | m n o |
456        // | m n o p |
457        let a = self.m11;
458        let b = self.m12;
459        let c = self.m13;
460        let d = self.m14;
461        let e = self.m21;
462        let f = self.m22;
463        let g = self.m23;
464        let h = self.m24;
465        let i = self.m31;
466        let j = self.m32;
467        let k = self.m33;
468        let l = self.m34;
469        let m = self.m41;
470        let n = self.m42;
471        let o = self.m43;
472        let p = self.m44;
473
474        let kp_lo = k * p - l * o;
475        let jp_ln = j * p - l * n;
476        let jo_kn = j * o - k * n;
477        let ip_lm = i * p - l * m;
478        let io_km = i * o - k * m;
479        let in_jm = i * n - j * m;
480
481        a * (f * kp_lo - g * jp_ln + h * jo_kn) - b * (e * kp_lo - g * ip_lm + h * io_km)
482            + c * (e * jp_ln - f * ip_lm + h * in_jm)
483            - d * (e * jo_kn - f * io_km + g * in_jm)
484    }
485
486    /// Tries to invert the specified matrix. Returns `None` when the matrix is
487    /// singular; see [`Matrix4x4::invert_or_nan`] for the value the reference
488    /// runtime stores in that case.
489    #[allow(clippy::field_reassign_with_default)]
490    pub fn invert(matrix: Matrix4x4) -> Option<Matrix4x4> {
491        let a = matrix.m11;
492        let b = matrix.m12;
493        let c = matrix.m13;
494        let d = matrix.m14;
495        let e = matrix.m21;
496        let f = matrix.m22;
497        let g = matrix.m23;
498        let h = matrix.m24;
499        let i = matrix.m31;
500        let j = matrix.m32;
501        let k = matrix.m33;
502        let l = matrix.m34;
503        let m = matrix.m41;
504        let n = matrix.m42;
505        let o = matrix.m43;
506        let p = matrix.m44;
507
508        let kp_lo = k * p - l * o;
509        let jp_ln = j * p - l * n;
510        let jo_kn = j * o - k * n;
511        let ip_lm = i * p - l * m;
512        let io_km = i * o - k * m;
513        let in_jm = i * n - j * m;
514
515        let a11 = f * kp_lo - g * jp_ln + h * jo_kn;
516        let a12 = -(e * kp_lo - g * ip_lm + h * io_km);
517        let a13 = e * jp_ln - f * ip_lm + h * in_jm;
518        let a14 = -(e * jo_kn - f * io_km + g * in_jm);
519
520        let det = a * a11 + b * a12 + c * a13 + d * a14;
521
522        // The reference runtime compares against the smallest positive
523        // (subnormal) single.
524        if det.abs() < f32::from_bits(1) {
525            return None;
526        }
527
528        let inv_det = 1.0 / det;
529
530        let mut result = Matrix4x4::default();
531
532        result.m11 = a11 * inv_det;
533        result.m21 = a12 * inv_det;
534        result.m31 = a13 * inv_det;
535        result.m41 = a14 * inv_det;
536
537        result.m12 = -(b * kp_lo - c * jp_ln + d * jo_kn) * inv_det;
538        result.m22 = (a * kp_lo - c * ip_lm + d * io_km) * inv_det;
539        result.m32 = -(a * jp_ln - b * ip_lm + d * in_jm) * inv_det;
540        result.m42 = (a * jo_kn - b * io_km + c * in_jm) * inv_det;
541
542        let gp_ho = g * p - h * o;
543        let fp_hn = f * p - h * n;
544        let fo_gn = f * o - g * n;
545        let ep_hm = e * p - h * m;
546        let eo_gm = e * o - g * m;
547        let en_fm = e * n - f * m;
548
549        result.m13 = (b * gp_ho - c * fp_hn + d * fo_gn) * inv_det;
550        result.m23 = -(a * gp_ho - c * ep_hm + d * eo_gm) * inv_det;
551        result.m33 = (a * fp_hn - b * ep_hm + d * en_fm) * inv_det;
552        result.m43 = -(a * fo_gn - b * eo_gm + c * en_fm) * inv_det;
553
554        let gl_hk = g * l - h * k;
555        let fl_hj = f * l - h * j;
556        let fk_gj = f * k - g * j;
557        let el_hi = e * l - h * i;
558        let ek_gi = e * k - g * i;
559        let ej_fi = e * j - f * i;
560
561        result.m14 = -(b * gl_hk - c * fl_hj + d * fk_gj) * inv_det;
562        result.m24 = (a * gl_hk - c * el_hi + d * ek_gi) * inv_det;
563        result.m34 = -(a * fl_hj - b * el_hi + d * ej_fi) * inv_det;
564        result.m44 = (a * fk_gj - b * ek_gi + c * ej_fi) * inv_det;
565
566        Some(result)
567    }
568
569    /// Inverts the specified matrix; a singular matrix gives a matrix whose
570    /// components are all NaN (the `out` value of the reference runtime).
571    pub fn invert_or_nan(matrix: Matrix4x4) -> Matrix4x4 {
572        const NAN: f32 = f32::NAN;
573        Matrix4x4::invert(matrix).unwrap_or(Matrix4x4::new(
574            NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN,
575        ))
576    }
577
578    /// Performs a linear interpolation from one matrix to a second matrix
579    /// based on a value that specifies the weighting of the second matrix.
580    pub fn lerp(matrix1: Matrix4x4, matrix2: Matrix4x4, amount: f32) -> Matrix4x4 {
581        Matrix4x4::new(
582            matrix1.m11 + (matrix2.m11 - matrix1.m11) * amount,
583            matrix1.m12 + (matrix2.m12 - matrix1.m12) * amount,
584            matrix1.m13 + (matrix2.m13 - matrix1.m13) * amount,
585            matrix1.m14 + (matrix2.m14 - matrix1.m14) * amount,
586            matrix1.m21 + (matrix2.m21 - matrix1.m21) * amount,
587            matrix1.m22 + (matrix2.m22 - matrix1.m22) * amount,
588            matrix1.m23 + (matrix2.m23 - matrix1.m23) * amount,
589            matrix1.m24 + (matrix2.m24 - matrix1.m24) * amount,
590            matrix1.m31 + (matrix2.m31 - matrix1.m31) * amount,
591            matrix1.m32 + (matrix2.m32 - matrix1.m32) * amount,
592            matrix1.m33 + (matrix2.m33 - matrix1.m33) * amount,
593            matrix1.m34 + (matrix2.m34 - matrix1.m34) * amount,
594            matrix1.m41 + (matrix2.m41 - matrix1.m41) * amount,
595            matrix1.m42 + (matrix2.m42 - matrix1.m42) * amount,
596            matrix1.m43 + (matrix2.m43 - matrix1.m43) * amount,
597            matrix1.m44 + (matrix2.m44 - matrix1.m44) * amount,
598        )
599    }
600
601    /// Transposes the rows and columns of a matrix.
602    pub fn transpose(matrix: Matrix4x4) -> Matrix4x4 {
603        Matrix4x4::new(
604            matrix.m11, matrix.m21, matrix.m31, matrix.m41, matrix.m12, matrix.m22, matrix.m32, matrix.m42,
605            matrix.m13, matrix.m23, matrix.m33, matrix.m43, matrix.m14, matrix.m24, matrix.m34, matrix.m44,
606        )
607    }
608
609    fn to_array(self) -> [f32; 16] {
610        [
611            self.m11, self.m12, self.m13, self.m14, self.m21, self.m22, self.m23, self.m24, self.m31, self.m32,
612            self.m33, self.m34, self.m41, self.m42, self.m43, self.m44,
613        ]
614    }
615
616    fn from_array(v: [f32; 16]) -> Matrix4x4 {
617        Matrix4x4::new(
618            v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14],
619            v[15],
620        )
621    }
622}
623
624impl Add for Matrix4x4 {
625    type Output = Matrix4x4;
626    fn add(self, value2: Matrix4x4) -> Matrix4x4 {
627        let (a, b) = (self.to_array(), value2.to_array());
628        Matrix4x4::from_array(std::array::from_fn(|i| a[i] + b[i]))
629    }
630}
631
632impl Sub for Matrix4x4 {
633    type Output = Matrix4x4;
634    fn sub(self, value2: Matrix4x4) -> Matrix4x4 {
635        let (a, b) = (self.to_array(), value2.to_array());
636        Matrix4x4::from_array(std::array::from_fn(|i| a[i] - b[i]))
637    }
638}
639
640impl Neg for Matrix4x4 {
641    type Output = Matrix4x4;
642    fn neg(self) -> Matrix4x4 {
643        let a = self.to_array();
644        Matrix4x4::from_array(std::array::from_fn(|i| -a[i]))
645    }
646}
647
648impl Mul for Matrix4x4 {
649    type Output = Matrix4x4;
650    fn mul(self, value2: Matrix4x4) -> Matrix4x4 {
651        let value1 = self;
652        Matrix4x4::new(
653            // First row
654            value1.m11 * value2.m11 + value1.m12 * value2.m21 + value1.m13 * value2.m31 + value1.m14 * value2.m41,
655            value1.m11 * value2.m12 + value1.m12 * value2.m22 + value1.m13 * value2.m32 + value1.m14 * value2.m42,
656            value1.m11 * value2.m13 + value1.m12 * value2.m23 + value1.m13 * value2.m33 + value1.m14 * value2.m43,
657            value1.m11 * value2.m14 + value1.m12 * value2.m24 + value1.m13 * value2.m34 + value1.m14 * value2.m44,
658            // Second row
659            value1.m21 * value2.m11 + value1.m22 * value2.m21 + value1.m23 * value2.m31 + value1.m24 * value2.m41,
660            value1.m21 * value2.m12 + value1.m22 * value2.m22 + value1.m23 * value2.m32 + value1.m24 * value2.m42,
661            value1.m21 * value2.m13 + value1.m22 * value2.m23 + value1.m23 * value2.m33 + value1.m24 * value2.m43,
662            value1.m21 * value2.m14 + value1.m22 * value2.m24 + value1.m23 * value2.m34 + value1.m24 * value2.m44,
663            // Third row
664            value1.m31 * value2.m11 + value1.m32 * value2.m21 + value1.m33 * value2.m31 + value1.m34 * value2.m41,
665            value1.m31 * value2.m12 + value1.m32 * value2.m22 + value1.m33 * value2.m32 + value1.m34 * value2.m42,
666            value1.m31 * value2.m13 + value1.m32 * value2.m23 + value1.m33 * value2.m33 + value1.m34 * value2.m43,
667            value1.m31 * value2.m14 + value1.m32 * value2.m24 + value1.m33 * value2.m34 + value1.m34 * value2.m44,
668            // Fourth row
669            value1.m41 * value2.m11 + value1.m42 * value2.m21 + value1.m43 * value2.m31 + value1.m44 * value2.m41,
670            value1.m41 * value2.m12 + value1.m42 * value2.m22 + value1.m43 * value2.m32 + value1.m44 * value2.m42,
671            value1.m41 * value2.m13 + value1.m42 * value2.m23 + value1.m43 * value2.m33 + value1.m44 * value2.m43,
672            value1.m41 * value2.m14 + value1.m42 * value2.m24 + value1.m43 * value2.m34 + value1.m44 * value2.m44,
673        )
674    }
675}
676
677/// Multiplies every component by a scalar.
678impl Mul<f32> for Matrix4x4 {
679    type Output = Matrix4x4;
680    fn mul(self, value2: f32) -> Matrix4x4 {
681        let a = self.to_array();
682        Matrix4x4::from_array(std::array::from_fn(|i| a[i] * value2))
683    }
684}
685
686impl fmt::Display for Matrix4x4 {
687    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
688        write!(
689            f,
690            "{{ {{M11:{} M12:{} M13:{} M14:{}}} {{M21:{} M22:{} M23:{} M24:{}}} {{M31:{} M32:{} M33:{} M34:{}}} {{M41:{} M42:{} M43:{} M44:{}}} }}",
691            InvariantF32(self.m11),
692            InvariantF32(self.m12),
693            InvariantF32(self.m13),
694            InvariantF32(self.m14),
695            InvariantF32(self.m21),
696            InvariantF32(self.m22),
697            InvariantF32(self.m23),
698            InvariantF32(self.m24),
699            InvariantF32(self.m31),
700            InvariantF32(self.m32),
701            InvariantF32(self.m33),
702            InvariantF32(self.m34),
703            InvariantF32(self.m41),
704            InvariantF32(self.m42),
705            InvariantF32(self.m43),
706            InvariantF32(self.m44)
707        )
708    }
709}