1use std::fmt;
4use std::ops::{Mul, MulAssign};
5
6use crate::{UnitVec3, Vec3, 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 {
84 return None;
85 }
86 let inv_d = 1.0 / d;
87 let [c0, c1, c2] = self.col;
88
89 let ic0 = Vec3::new(
92 (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, );
96 let ic1 = Vec3::new(
98 -(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, );
102 let ic2 = Vec3::new(
104 (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, );
108 Some(Self::from_cols(ic0, ic1, ic2))
109 }
110
111 #[inline]
113 pub fn apply(self, v: Vec3) -> Vec3 {
114 self.col[0] * v.x + self.col[1] * v.y + self.col[2] * v.z
115 }
116}
117
118impl Mul for Mat3 {
119 type Output = Self;
120 fn mul(self, rhs: Self) -> Self {
121 Self::from_cols(
122 self.apply(rhs.col[0]),
123 self.apply(rhs.col[1]),
124 self.apply(rhs.col[2]),
125 )
126 }
127}
128
129impl MulAssign for Mat3 {
130 fn mul_assign(&mut self, rhs: Self) {
131 *self = *self * rhs;
132 }
133}
134
135impl Mul<Vec3> for Mat3 {
136 type Output = Vec3;
137 #[inline]
138 fn mul(self, v: Vec3) -> Vec3 {
139 self.apply(v)
140 }
141}
142
143impl fmt::Debug for Mat3 {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 let [c0, c1, c2] = self.col;
146 write!(
147 f,
148 "Mat3[\n [{:.4} {:.4} {:.4}]\n [{:.4} {:.4} {:.4}]\n [{:.4} {:.4} {:.4}]\n]",
149 c0.x, c1.x, c2.x, c0.y, c1.y, c2.y, c0.z, c1.z, c2.z,
150 )
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::PI;
158
159 #[test]
160 fn rotation_90_deg_around_z() {
161 let m = Mat3::rotation(UnitVec3::Z, PI / 2.0);
162 let v = m * Vec3::X;
163 assert!((v - Vec3::Y).length() < 1e-12);
164 }
165
166 #[test]
167 fn inverse_times_original_is_identity() {
168 let m = Mat3::rotation(
169 UnitVec3::try_from_vec(Vec3::new(1.0, 2.0, 3.0).normalize()).unwrap(),
170 1.23,
171 );
172 let inv = m.try_inverse().unwrap();
173 let prod = m * inv;
174 for i in 0..3 {
175 let diff = prod.col[i] - Mat3::IDENTITY.col[i];
176 assert!(diff.length() < 1e-10, "column {i}: {:?}", diff);
177 }
178 }
179}