1use serde::{Deserialize, Serialize};
2use std::ops::{Add, Div, Mul, Neg, Sub};
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
6#[repr(C)]
7pub struct Vec2 {
8 pub x: f32,
9 pub y: f32,
10}
11
12#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
14#[repr(C, align(16))]
15pub struct Vec3 {
16 pub x: f32,
17 pub y: f32,
18 pub z: f32,
19}
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
23#[repr(C, align(16))]
24pub struct Vec4 {
25 pub x: f32,
26 pub y: f32,
27 pub z: f32,
28 pub w: f32,
29}
30
31impl Vec2 {
34 pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
36 pub const ONE: Self = Self { x: 1.0, y: 1.0 };
38 pub const X: Self = Self { x: 1.0, y: 0.0 };
40 pub const Y: Self = Self { x: 0.0, y: 1.0 };
42
43 #[inline(always)]
45 pub const fn new(x: f32, y: f32) -> Self {
46 Self { x, y }
47 }
48
49 #[inline(always)]
51 pub fn dot(self, rhs: Self) -> f32 {
52 self.x * rhs.x + self.y * rhs.y
53 }
54
55 #[inline(always)]
57 pub fn length(self) -> f32 {
58 self.dot(self).sqrt()
59 }
60
61 #[inline(always)]
63 pub fn normalize(self) -> Self {
64 let inv = 1.0 / self.length();
65 Self {
66 x: self.x * inv,
67 y: self.y * inv,
68 }
69 }
70
71 #[inline(always)]
73 pub fn distance(self, rhs: Self) -> f32 {
74 (self - rhs).length()
75 }
76}
77
78impl Add for Vec2 {
79 type Output = Self;
80 #[inline(always)]
81 fn add(self, rhs: Self) -> Self {
82 Self {
83 x: self.x + rhs.x,
84 y: self.y + rhs.y,
85 }
86 }
87}
88
89impl Sub for Vec2 {
90 type Output = Self;
91 #[inline(always)]
92 fn sub(self, rhs: Self) -> Self {
93 Self {
94 x: self.x - rhs.x,
95 y: self.y - rhs.y,
96 }
97 }
98}
99
100impl Mul<f32> for Vec2 {
101 type Output = Self;
102 #[inline(always)]
103 fn mul(self, rhs: f32) -> Self {
104 Self {
105 x: self.x * rhs,
106 y: self.y * rhs,
107 }
108 }
109}
110
111impl Neg for Vec2 {
112 type Output = Self;
113 #[inline(always)]
114 fn neg(self) -> Self {
115 Self {
116 x: -self.x,
117 y: -self.y,
118 }
119 }
120}
121
122impl Vec3 {
125 pub const ZERO: Self = Self {
127 x: 0.0,
128 y: 0.0,
129 z: 0.0,
130 };
131 pub const ONE: Self = Self {
133 x: 1.0,
134 y: 1.0,
135 z: 1.0,
136 };
137 pub const X: Self = Self {
139 x: 1.0,
140 y: 0.0,
141 z: 0.0,
142 };
143 pub const Y: Self = Self {
145 x: 0.0,
146 y: 1.0,
147 z: 0.0,
148 };
149 pub const Z: Self = Self {
151 x: 0.0,
152 y: 0.0,
153 z: 1.0,
154 };
155
156 #[inline(always)]
158 pub const fn new(x: f32, y: f32, z: f32) -> Self {
159 Self { x, y, z }
160 }
161
162 #[inline(always)]
164 pub fn dot(self, rhs: Self) -> f32 {
165 self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
166 }
167
168 #[inline(always)]
170 pub fn cross(self, rhs: Self) -> Self {
171 Self {
172 x: self.y * rhs.z - self.z * rhs.y,
173 y: self.z * rhs.x - self.x * rhs.z,
174 z: self.x * rhs.y - self.y * rhs.x,
175 }
176 }
177
178 #[inline(always)]
180 pub fn length(self) -> f32 {
181 self.dot(self).sqrt()
182 }
183
184 #[inline(always)]
186 pub fn length_squared(self) -> f32 {
187 self.dot(self)
188 }
189
190 #[inline(always)]
192 pub fn normalize(self) -> Self {
193 let inv = 1.0 / self.length();
194 Self {
195 x: self.x * inv,
196 y: self.y * inv,
197 z: self.z * inv,
198 }
199 }
200
201 #[inline(always)]
203 pub fn distance(self, rhs: Self) -> f32 {
204 (self - rhs).length()
205 }
206
207 #[inline(always)]
209 pub fn lerp(self, rhs: Self, t: f32) -> Self {
210 self + (rhs - self) * t
211 }
212
213 #[inline(always)]
215 pub fn min(self, rhs: Self) -> Self {
216 Self {
217 x: self.x.min(rhs.x),
218 y: self.y.min(rhs.y),
219 z: self.z.min(rhs.z),
220 }
221 }
222
223 #[inline(always)]
225 pub fn max(self, rhs: Self) -> Self {
226 Self {
227 x: self.x.max(rhs.x),
228 y: self.y.max(rhs.y),
229 z: self.z.max(rhs.z),
230 }
231 }
232
233 pub fn closest_line_param(
237 line_origin: Vec3,
238 line_dir: Vec3,
239 ray_origin: Vec3,
240 ray_dir: Vec3,
241 ) -> f32 {
242 let w0 = line_origin - ray_origin;
243 let a = line_dir.dot(line_dir);
244 let b = line_dir.dot(ray_dir);
245 let c = ray_dir.dot(ray_dir);
246 let d = line_dir.dot(w0);
247 let e = ray_dir.dot(w0);
248 let denom = a * c - b * b;
249 if denom.abs() < 1e-10 {
250 return 0.0; }
252 (b * e - c * d) / denom
253 }
254}
255
256impl Add for Vec3 {
257 type Output = Self;
258 #[inline(always)]
259 fn add(self, rhs: Self) -> Self {
260 Self {
261 x: self.x + rhs.x,
262 y: self.y + rhs.y,
263 z: self.z + rhs.z,
264 }
265 }
266}
267
268impl Sub for Vec3 {
269 type Output = Self;
270 #[inline(always)]
271 fn sub(self, rhs: Self) -> Self {
272 Self {
273 x: self.x - rhs.x,
274 y: self.y - rhs.y,
275 z: self.z - rhs.z,
276 }
277 }
278}
279
280impl Mul<f32> for Vec3 {
281 type Output = Self;
282 #[inline(always)]
283 fn mul(self, rhs: f32) -> Self {
284 Self {
285 x: self.x * rhs,
286 y: self.y * rhs,
287 z: self.z * rhs,
288 }
289 }
290}
291
292impl Div<f32> for Vec3 {
293 type Output = Self;
294 #[inline(always)]
295 fn div(self, rhs: f32) -> Self {
296 let inv = 1.0 / rhs;
297 Self {
298 x: self.x * inv,
299 y: self.y * inv,
300 z: self.z * inv,
301 }
302 }
303}
304
305impl Neg for Vec3 {
306 type Output = Self;
307 #[inline(always)]
308 fn neg(self) -> Self {
309 Self {
310 x: -self.x,
311 y: -self.y,
312 z: -self.z,
313 }
314 }
315}
316
317impl Vec4 {
320 pub const ZERO: Self = Self {
322 x: 0.0,
323 y: 0.0,
324 z: 0.0,
325 w: 0.0,
326 };
327 pub const ONE: Self = Self {
329 x: 1.0,
330 y: 1.0,
331 z: 1.0,
332 w: 1.0,
333 };
334
335 #[inline(always)]
337 pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
338 Self { x, y, z, w }
339 }
340
341 #[inline(always)]
343 pub fn dot(self, rhs: Self) -> f32 {
344 self.x * rhs.x + self.y * rhs.y + self.z * rhs.z + self.w * rhs.w
345 }
346
347 #[inline(always)]
349 pub fn length(self) -> f32 {
350 self.dot(self).sqrt()
351 }
352
353 #[inline(always)]
355 pub fn normalize(self) -> Self {
356 let inv = 1.0 / self.length();
357 Self {
358 x: self.x * inv,
359 y: self.y * inv,
360 z: self.z * inv,
361 w: self.w * inv,
362 }
363 }
364}
365
366impl Add for Vec4 {
367 type Output = Self;
368 #[inline(always)]
369 fn add(self, rhs: Self) -> Self {
370 Self {
371 x: self.x + rhs.x,
372 y: self.y + rhs.y,
373 z: self.z + rhs.z,
374 w: self.w + rhs.w,
375 }
376 }
377}
378
379impl Sub for Vec4 {
380 type Output = Self;
381 #[inline(always)]
382 fn sub(self, rhs: Self) -> Self {
383 Self {
384 x: self.x - rhs.x,
385 y: self.y - rhs.y,
386 z: self.z - rhs.z,
387 w: self.w - rhs.w,
388 }
389 }
390}
391
392impl Mul<f32> for Vec4 {
393 type Output = Self;
394 #[inline(always)]
395 fn mul(self, rhs: f32) -> Self {
396 Self {
397 x: self.x * rhs,
398 y: self.y * rhs,
399 z: self.z * rhs,
400 w: self.w * rhs,
401 }
402 }
403}
404
405impl Neg for Vec4 {
406 type Output = Self;
407 #[inline(always)]
408 fn neg(self) -> Self {
409 Self {
410 x: -self.x,
411 y: -self.y,
412 z: -self.z,
413 w: -self.w,
414 }
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn vec3_basic_ops() {
424 let a = Vec3::new(1.0, 2.0, 3.0);
425 let b = Vec3::new(4.0, 5.0, 6.0);
426 assert_eq!(a + b, Vec3::new(5.0, 7.0, 9.0));
427 assert_eq!(b - a, Vec3::new(3.0, 3.0, 3.0));
428 assert_eq!(a * 2.0, Vec3::new(2.0, 4.0, 6.0));
429 assert_eq!(-a, Vec3::new(-1.0, -2.0, -3.0));
430 }
431
432 #[test]
433 fn vec3_dot_cross() {
434 let a = Vec3::X;
435 let b = Vec3::Y;
436 assert_eq!(a.dot(b), 0.0);
437 assert_eq!(a.cross(b), Vec3::Z);
438 }
439
440 #[test]
441 fn vec3_length_normalize() {
442 let v = Vec3::new(3.0, 4.0, 0.0);
443 assert!((v.length() - 5.0).abs() < 1e-6);
444 let n = v.normalize();
445 assert!((n.length() - 1.0).abs() < 1e-6);
446 }
447
448 #[test]
449 fn vec2_distance() {
450 let a = Vec2::new(0.0, 0.0);
451 let b = Vec2::new(3.0, 4.0);
452 assert!((a.distance(b) - 5.0).abs() < 1e-6);
453 }
454
455 #[test]
456 fn vec3_lerp() {
457 let a = Vec3::ZERO;
458 let b = Vec3::new(10.0, 20.0, 30.0);
459 let mid = a.lerp(b, 0.5);
460 assert_eq!(mid, Vec3::new(5.0, 10.0, 15.0));
461 }
462
463 #[test]
464 fn vec3_div() {
465 let v = Vec3::new(10.0, 20.0, 30.0);
466 let result = v / 10.0;
467 assert!((result.x - 1.0).abs() < 1e-6);
468 }
469
470 #[test]
471 fn vec4_dot() {
472 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
473 let b = Vec4::new(5.0, 6.0, 7.0, 8.0);
474 assert_eq!(a.dot(b), 70.0);
475 }
476
477 #[test]
478 fn closest_line_param_perpendicular() {
479 let t = Vec3::closest_line_param(
481 Vec3::new(0.0, 1.0, 0.0),
482 Vec3::X,
483 Vec3::new(2.0, 0.0, 0.0),
484 Vec3::Y,
485 );
486 assert!((t - 2.0).abs() < 1e-6, "Expected t=2.0, got {t}");
487 }
488
489 #[test]
490 fn closest_line_param_parallel() {
491 let t = Vec3::closest_line_param(Vec3::ZERO, Vec3::X, Vec3::new(0.0, 1.0, 0.0), Vec3::X);
493 assert_eq!(t, 0.0);
494 }
495}