1use std::ops::{Add, Mul, Neg, Sub};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq)]
7pub struct Vec3 {
8 pub x: f32,
10 pub y: f32,
12 pub z: f32,
14}
15
16impl Vec3 {
17 pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
19
20 pub const fn new(x: f32, y: f32, z: f32) -> Self {
22 Self { x, y, z }
23 }
24
25 pub const fn to_array(self) -> [f32; 3] {
27 [self.x, self.y, self.z]
28 }
29
30 pub const fn from_array([x, y, z]: [f32; 3]) -> Self {
32 Self { x, y, z }
33 }
34
35 pub fn dot(self, other: Self) -> f32 {
37 self.x * other.x + self.y * other.y + self.z * other.z
38 }
39
40 pub fn cross(self, other: Self) -> Self {
42 Self::new(
43 self.y * other.z - self.z * other.y,
44 self.z * other.x - self.x * other.z,
45 self.x * other.y - self.y * other.x,
46 )
47 }
48
49 pub fn length(self) -> f32 {
51 self.dot(self).sqrt()
52 }
53
54 pub fn normalized(self) -> Option<Self> {
56 let length = self.length();
57 (length.is_normal()).then(|| self * (1.0 / length))
58 }
59
60 pub fn lerp(self, other: Self, t: f32) -> Self {
62 self + (other - self) * t
63 }
64
65 pub fn min(self, other: Self) -> Self {
67 Self::new(
68 self.x.min(other.x),
69 self.y.min(other.y),
70 self.z.min(other.z),
71 )
72 }
73
74 pub fn max(self, other: Self) -> Self {
76 Self::new(
77 self.x.max(other.x),
78 self.y.max(other.y),
79 self.z.max(other.z),
80 )
81 }
82}
83
84impl Add for Vec3 {
85 type Output = Self;
86
87 fn add(self, other: Self) -> Self {
88 Self::new(self.x + other.x, self.y + other.y, self.z + other.z)
89 }
90}
91
92impl Sub for Vec3 {
93 type Output = Self;
94
95 fn sub(self, other: Self) -> Self {
96 Self::new(self.x - other.x, self.y - other.y, self.z - other.z)
97 }
98}
99
100impl Mul<f32> for Vec3 {
101 type Output = Self;
102
103 fn mul(self, factor: f32) -> Self {
104 Self::new(self.x * factor, self.y * factor, self.z * factor)
105 }
106}
107
108impl Neg for Vec3 {
109 type Output = Self;
110
111 fn neg(self) -> Self {
112 Self::new(-self.x, -self.y, -self.z)
113 }
114}
115
116#[derive(Clone, Copy, Debug, PartialEq)]
119pub struct Mat4 {
120 rows: [[f32; 4]; 4],
121}
122
123impl Default for Mat4 {
124 fn default() -> Self {
125 Self::IDENTITY
126 }
127}
128
129impl Mat4 {
130 pub const IDENTITY: Self = Self::from_rows([
132 [1.0, 0.0, 0.0, 0.0],
133 [0.0, 1.0, 0.0, 0.0],
134 [0.0, 0.0, 1.0, 0.0],
135 [0.0, 0.0, 0.0, 1.0],
136 ]);
137
138 pub const fn from_rows(rows: [[f32; 4]; 4]) -> Self {
140 Self { rows }
141 }
142
143 pub const fn rows(&self) -> [[f32; 4]; 4] {
145 self.rows
146 }
147
148 pub const fn from_axes(x: Vec3, y: Vec3, z: Vec3, origin: Vec3) -> Self {
151 Self::from_rows([
152 [x.x, y.x, z.x, origin.x],
153 [x.y, y.y, z.y, origin.y],
154 [x.z, y.z, z.z, origin.z],
155 [0.0, 0.0, 0.0, 1.0],
156 ])
157 }
158
159 pub const fn translation(offset: Vec3) -> Self {
161 Self::from_axes(
162 Vec3::new(1.0, 0.0, 0.0),
163 Vec3::new(0.0, 1.0, 0.0),
164 Vec3::new(0.0, 0.0, 1.0),
165 offset,
166 )
167 }
168
169 pub const fn scaling(factors: Vec3) -> Self {
171 Self::from_axes(
172 Vec3::new(factors.x, 0.0, 0.0),
173 Vec3::new(0.0, factors.y, 0.0),
174 Vec3::new(0.0, 0.0, factors.z),
175 Vec3::ZERO,
176 )
177 }
178
179 pub fn rotation_x(radians: f32) -> Self {
182 let (sin, cos) = radians.sin_cos();
183 Self::from_axes(
184 Vec3::new(1.0, 0.0, 0.0),
185 Vec3::new(0.0, cos, sin),
186 Vec3::new(0.0, -sin, cos),
187 Vec3::ZERO,
188 )
189 }
190
191 pub fn rotation_y(radians: f32) -> Self {
194 let (sin, cos) = radians.sin_cos();
195 Self::from_axes(
196 Vec3::new(cos, 0.0, sin),
197 Vec3::new(0.0, 1.0, 0.0),
198 Vec3::new(-sin, 0.0, cos),
199 Vec3::ZERO,
200 )
201 }
202
203 pub fn rotation_z(radians: f32) -> Self {
206 let (sin, cos) = radians.sin_cos();
207 Self::from_axes(
208 Vec3::new(cos, sin, 0.0),
209 Vec3::new(-sin, cos, 0.0),
210 Vec3::new(0.0, 0.0, 1.0),
211 Vec3::ZERO,
212 )
213 }
214
215 pub fn then(&self, next: &Self) -> Self {
217 let (a, b) = (next.rows, self.rows);
218 Self::from_rows(std::array::from_fn(|row| {
219 std::array::from_fn(|column| (0..4).map(|k| a[row][k] * b[k][column]).sum())
220 }))
221 }
222
223 pub fn transform_point(&self, point: Vec3) -> Vec3 {
225 let [x, y, z, w] = self.transform_homogeneous(point);
226 if w == 1.0 || w == 0.0 {
227 Vec3::new(x, y, z)
228 } else {
229 Vec3::new(x / w, y / w, z / w)
230 }
231 }
232
233 pub fn transform_homogeneous(&self, point: Vec3) -> [f32; 4] {
236 self.rows
237 .map(|row| row[0] * point.x + row[1] * point.y + row[2] * point.z + row[3])
238 }
239
240 pub fn transform_vector(&self, vector: Vec3) -> Vec3 {
242 let [x, y, z, _] = self
243 .rows
244 .map(|row| row[0] * vector.x + row[1] * vector.y + row[2] * vector.z);
245 Vec3::new(x, y, z)
246 }
247
248 pub fn length_scale(&self) -> f32 {
251 let axis = |axis: Vec3| self.transform_vector(axis).length();
252 (axis(Vec3::new(1.0, 0.0, 0.0))
253 * axis(Vec3::new(0.0, 1.0, 0.0))
254 * axis(Vec3::new(0.0, 0.0, 1.0)))
255 .cbrt()
256 }
257
258 pub fn transpose(&self) -> Self {
260 Self::from_rows(std::array::from_fn(|row| {
261 std::array::from_fn(|column| self.rows[column][row])
262 }))
263 }
264
265 pub fn inverse(&self) -> Option<Self> {
267 let m = self.rows;
268 let cofactor = |row: usize, column: usize| {
269 let pick = |skip: usize| -> [usize; 3] {
270 let mut picked = [0; 3];
271 let mut next = 0;
272 for index in 0..4 {
273 if index != skip {
274 picked[next] = index;
275 next += 1;
276 }
277 }
278 picked
279 };
280 let [r0, r1, r2] = pick(row);
281 let [c0, c1, c2] = pick(column);
282 let minor = m[r0][c0] * (m[r1][c1] * m[r2][c2] - m[r1][c2] * m[r2][c1])
283 - m[r0][c1] * (m[r1][c0] * m[r2][c2] - m[r1][c2] * m[r2][c0])
284 + m[r0][c2] * (m[r1][c0] * m[r2][c1] - m[r1][c1] * m[r2][c0]);
285 if (row + column).is_multiple_of(2) {
286 minor
287 } else {
288 -minor
289 }
290 };
291 let cofactors: [[f32; 4]; 4] =
292 std::array::from_fn(|row| std::array::from_fn(|column| cofactor(row, column)));
293 let determinant: f32 = (0..4)
294 .map(|column| m[0][column] * cofactors[0][column])
295 .sum();
296 if !determinant.is_normal() {
297 return None;
298 }
299 let scale = 1.0 / determinant;
300 Some(Self::from_rows(std::array::from_fn(|row| {
301 std::array::from_fn(|column| cofactors[column][row] * scale)
302 })))
303 }
304}