1use std::fmt;
4use std::ops::{Mul, MulAssign};
5
6use crate::{Vec3, UnitVec3, EPS};
7
8#[derive(Clone, Copy, PartialEq)]
12pub struct Mat3 {
13 pub col: [Vec3; 3],
15}
16
17impl Mat3 {
18 pub const IDENTITY: Self = Self {
20 col: [Vec3::X, Vec3::Y, Vec3::Z],
21 };
22
23 pub const ZERO: Self = Self {
25 col: [Vec3::ZERO, Vec3::ZERO, Vec3::ZERO],
26 };
27
28 #[inline]
30 pub const fn from_cols(c0: Vec3, c1: Vec3, c2: Vec3) -> Self {
31 Self { col: [c0, c1, c2] }
32 }
33
34 #[inline]
36 pub fn from_rows(r0: Vec3, r1: Vec3, r2: Vec3) -> Self {
37 Self::from_cols(
38 Vec3::new(r0.x, r1.x, r2.x),
39 Vec3::new(r0.y, r1.y, r2.y),
40 Vec3::new(r0.z, r1.z, r2.z),
41 )
42 }
43
44 pub fn from_axes(right: UnitVec3, up: UnitVec3, forward: UnitVec3) -> Self {
50 Self::from_cols(right.as_vec(), up.as_vec(), forward.as_vec())
51 }
52
53 pub fn rotation(axis: UnitVec3, angle: f64) -> Self {
55 let (s, c) = angle.sin_cos();
56 let t = 1.0 - c;
57 let Vec3 { x, y, z } = axis.as_vec();
58 Self::from_rows(
59 Vec3::new(t*x*x + c, t*x*y - s*z, t*x*z + s*y),
60 Vec3::new(t*x*y + s*z, t*y*y + c, t*y*z - s*x),
61 Vec3::new(t*x*z - s*y, t*y*z + s*x, t*z*z + c ),
62 )
63 }
64
65 #[inline]
67 pub fn transpose(self) -> Self {
68 Self::from_rows(self.col[0], self.col[1], self.col[2])
69 }
70
71 pub fn det(self) -> f64 {
73 let [c0, c1, c2] = self.col;
74 c0.dot(c1.cross(c2))
75 }
76
77 pub fn try_inverse(self) -> Option<Self> {
82 let d = self.det();
83 if d.abs() < EPS { return None; }
84 let inv_d = 1.0 / d;
85 let [c0, c1, c2] = self.col;
86
87 let ic0 = Vec3::new(
90 ( c1.y * c2.z - c1.z * c2.y) * inv_d, -(c0.y * c2.z - c0.z * c2.y) * inv_d, ( c0.y * c1.z - c0.z * c1.y) * inv_d, );
94 let ic1 = Vec3::new(
96 -(c1.x * c2.z - c1.z * c2.x) * inv_d, ( c0.x * c2.z - c0.z * c2.x) * inv_d, -(c0.x * c1.z - c0.z * c1.x) * inv_d, );
100 let ic2 = Vec3::new(
102 ( c1.x * c2.y - c1.y * c2.x) * inv_d, -(c0.x * c2.y - c0.y * c2.x) * inv_d, ( c0.x * c1.y - c0.y * c1.x) * inv_d, );
106 Some(Self::from_cols(ic0, ic1, ic2))
107 }
108
109 #[inline]
111 pub fn apply(self, v: Vec3) -> Vec3 {
112 self.col[0] * v.x + self.col[1] * v.y + self.col[2] * v.z
113 }
114}
115
116impl Mul for Mat3 {
117 type Output = Self;
118 fn mul(self, rhs: Self) -> Self {
119 Self::from_cols(
120 self.apply(rhs.col[0]),
121 self.apply(rhs.col[1]),
122 self.apply(rhs.col[2]),
123 )
124 }
125}
126
127impl MulAssign for Mat3 {
128 fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
129}
130
131impl Mul<Vec3> for Mat3 {
132 type Output = Vec3;
133 #[inline] fn mul(self, v: Vec3) -> Vec3 { self.apply(v) }
134}
135
136impl fmt::Debug for Mat3 {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 let [c0, c1, c2] = self.col;
139 write!(f,
140 "Mat3[\n [{:.4} {:.4} {:.4}]\n [{:.4} {:.4} {:.4}]\n [{:.4} {:.4} {:.4}]\n]",
141 c0.x, c1.x, c2.x,
142 c0.y, c1.y, c2.y,
143 c0.z, c1.z, c2.z,
144 )
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::PI;
152
153 #[test]
154 fn rotation_90_deg_around_z() {
155 let m = Mat3::rotation(UnitVec3::Z, PI / 2.0);
156 let v = m * Vec3::X;
157 assert!((v - Vec3::Y).length() < 1e-12);
158 }
159
160 #[test]
161 fn inverse_times_original_is_identity() {
162 let m = Mat3::rotation(
163 UnitVec3::try_from_vec(Vec3::new(1.0, 2.0, 3.0).normalize()).unwrap(),
164 1.23,
165 );
166 let inv = m.try_inverse().unwrap();
167 let prod = m * inv;
168 for i in 0..3 {
169 let diff = prod.col[i] - Mat3::IDENTITY.col[i];
170 assert!(diff.length() < 1e-10, "column {i}: {:?}", diff);
171 }
172 }
173}