1use crate::math::sqrt;
9
10pub fn dot(a: [f32; 3], b: [f32; 3]) -> f32 {
12 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
13}
14
15pub fn cross(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
17 [
18 a[1] * b[2] - a[2] * b[1],
19 a[2] * b[0] - a[0] * b[2],
20 a[0] * b[1] - a[1] * b[0],
21 ]
22}
23
24pub fn sub(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
26 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
27}
28
29pub fn add(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
31 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
32}
33
34pub fn scale(v: [f32; 3], s: f32) -> [f32; 3] {
36 [v[0] * s, v[1] * s, v[2] * s]
37}
38
39pub fn length(v: [f32; 3]) -> f32 {
41 sqrt(dot(v, v))
42}
43
44pub fn lerp(a: [f32; 3], b: [f32; 3], t: f32) -> [f32; 3] {
46 [
47 a[0] + (b[0] - a[0]) * t,
48 a[1] + (b[1] - a[1]) * t,
49 a[2] + (b[2] - a[2]) * t,
50 ]
51}
52
53pub fn vec3_add(dst: &mut [f32; 3], src: [f32; 3]) {
56 dst[0] += src[0];
57 dst[1] += src[1];
58 dst[2] += src[2];
59}
60
61pub fn vec3_normalise(n: [f32; 3]) -> [f32; 3] {
64 let len = length(n);
65 if len < 1e-6 {
66 [0.0, 1.0, 0.0]
67 } else {
68 scale(n, 1.0 / len)
69 }
70}
71
72pub fn vec3_face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
75 vec3_normalise(cross(sub(b, a), sub(c, a)))
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn cross_is_right_handed() {
84 assert_eq!(cross([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]), [0.0, 0.0, 1.0]);
85 assert_eq!(cross([0.0, 1.0, 0.0], [0.0, 0.0, 1.0]), [1.0, 0.0, 0.0]);
86 }
87
88 #[test]
89 fn dot_and_length_agree() {
90 let v = [3.0, 4.0, 0.0];
91 assert_eq!(dot(v, v), 25.0);
92 assert_eq!(length(v), 5.0);
93 }
94
95 #[test]
96 fn component_ops_are_component_wise() {
97 assert_eq!(add([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]), [5.0, 7.0, 9.0]);
98 assert_eq!(sub([4.0, 5.0, 6.0], [1.0, 2.0, 3.0]), [3.0, 3.0, 3.0]);
99 assert_eq!(scale([1.0, 2.0, 3.0], 2.0), [2.0, 4.0, 6.0]);
100 assert_eq!(lerp([0.0, 0.0, 0.0], [2.0, 4.0, 6.0], 0.5), [1.0, 2.0, 3.0]);
101 }
102
103 #[test]
104 fn vec3_add_accumulates_in_place() {
105 let mut acc = [1.0, 1.0, 1.0];
106 vec3_add(&mut acc, [1.0, 2.0, 3.0]);
107 vec3_add(&mut acc, [1.0, 2.0, 3.0]);
108 assert_eq!(acc, [3.0, 5.0, 7.0]);
109 }
110
111 #[test]
112 fn normalise_falls_back_on_a_degenerate_vector() {
113 assert_eq!(vec3_normalise([0.0, 0.0, 0.0]), [0.0, 1.0, 0.0]);
114 assert_eq!(vec3_normalise([0.0, 0.0, 2.0]), [0.0, 0.0, 1.0]);
115 }
116
117 #[test]
118 fn face_normal_of_a_ccw_triangle_points_up() {
119 let n = vec3_face_normal([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0]);
120 assert!(n[1] > 0.99, "expected +Y, got {n:?}");
121 }
122}