Skip to main content

rustsim_geometry/
vec3.rs

1//! 3-D vector helpers.
2
3/// 3-D vector in metres.
4pub type Vec3 = [f64; 3];
5
6/// `a + b`.
7#[inline]
8pub fn add(a: Vec3, b: Vec3) -> Vec3 {
9    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
10}
11
12/// `a - b`.
13#[inline]
14pub fn sub(a: Vec3, b: Vec3) -> Vec3 {
15    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
16}
17
18/// `a * s`.
19#[inline]
20pub fn scale(a: Vec3, s: f64) -> Vec3 {
21    [a[0] * s, a[1] * s, a[2] * s]
22}
23
24/// `a · b`.
25#[inline]
26pub fn dot(a: Vec3, b: Vec3) -> f64 {
27    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
28}
29
30/// `a × b`.
31#[inline]
32pub fn cross(a: Vec3, b: Vec3) -> Vec3 {
33    [
34        a[1] * b[2] - a[2] * b[1],
35        a[2] * b[0] - a[0] * b[2],
36        a[0] * b[1] - a[1] * b[0],
37    ]
38}
39
40/// `|a|`.
41#[inline]
42pub fn norm(a: Vec3) -> f64 {
43    dot(a, a).sqrt()
44}
45
46/// `|a|²`.
47#[inline]
48pub fn norm_squared(a: Vec3) -> f64 {
49    dot(a, a)
50}
51
52/// Unit vector or `[0, 0, 0]` if `|a|` is below machine epsilon.
53#[inline]
54pub fn normalize(a: Vec3) -> Vec3 {
55    let n = norm(a);
56    if n < 1e-12 {
57        [0.0, 0.0, 0.0]
58    } else {
59        [a[0] / n, a[1] / n, a[2] / n]
60    }
61}
62
63/// Euclidean distance `|a - b|`.
64#[inline]
65pub fn distance(a: Vec3, b: Vec3) -> f64 {
66    norm(sub(a, b))
67}
68
69/// Linear interpolation `a + t * (b - a)`.
70#[inline]
71pub fn lerp(a: Vec3, b: Vec3, t: f64) -> Vec3 {
72    [
73        a[0] + t * (b[0] - a[0]),
74        a[1] + t * (b[1] - a[1]),
75        a[2] + t * (b[2] - a[2]),
76    ]
77}
78
79/// Project `v` onto the horizontal XY plane (drop Z).
80#[inline]
81pub fn xy(v: Vec3) -> [f64; 2] {
82    [v[0], v[1]]
83}
84
85/// Lift a 2-D vector into 3-D with the given Z component.
86#[inline]
87pub fn from_xy(xy: [f64; 2], z: f64) -> Vec3 {
88    [xy[0], xy[1], z]
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn cross_of_basis_axes() {
97        assert_eq!(cross([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]), [0.0, 0.0, 1.0]);
98    }
99
100    #[test]
101    fn norm_of_unit_diagonal() {
102        let n = norm([1.0, 1.0, 1.0]);
103        assert!((n - 3f64.sqrt()).abs() < 1e-12);
104    }
105
106    #[test]
107    fn xy_roundtrip() {
108        let v: Vec3 = [1.0, 2.0, 3.0];
109        assert_eq!(xy(v), [1.0, 2.0]);
110        assert_eq!(from_xy(xy(v), v[2]), v);
111    }
112}