Skip to main content

primase_core/
vector.rs

1//! Three-component vectors in f64, as plain arrays: the one set of the arithmetic every crate's geometry in f64 shares
2//! (f32 meshes have `primase_meshops::Vec3`). Each operation is one fixed order of operations, so any two crates
3//! computing the same thing get the same bits.
4
5/// A vector or point, x, y, z.
6pub type V3 = [f64; 3];
7
8pub fn add(a: V3, b: V3) -> V3 {
9    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
10}
11
12pub fn sub(a: V3, b: V3) -> V3 {
13    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
14}
15
16/// `a` times `s`.
17pub fn scale(a: V3, s: f64) -> V3 {
18    a.map(|x| x * s)
19}
20
21/// `a0 b0 + a1 b1 + a2 b2`, summed in that order.
22pub fn dot(a: V3, b: V3) -> f64 {
23    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
24}
25
26pub fn cross(a: V3, b: V3) -> V3 {
27    [
28        a[1] * b[2] - a[2] * b[1],
29        a[2] * b[0] - a[0] * b[2],
30        a[0] * b[1] - a[1] * b[0],
31    ]
32}
33
34/// The Euclidean length, `sqrt(a . a)`.
35pub fn length(a: V3) -> f64 {
36    dot(a, a).sqrt()
37}
38
39/// `a` divided by its length; `None` where the length is 0 or not finite (a vector with no direction), for the caller
40/// to say what that means where it can happen.
41pub fn unit(a: V3) -> Option<V3> {
42    let l = length(a);
43    (l > 0.0 && l.is_finite()).then(|| a.map(|x| x / l))
44}
45
46/// `a` divided by its length where that is above `shortest` (the caller's: below it a direction is noise), else `None`.
47pub fn unit_beyond(a: V3, shortest: f64) -> Option<V3> {
48    let l = length(a);
49    (l > shortest && l.is_finite()).then(|| a.map(|x| x / l))
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    /// Worked by hand: the operations, a unit vector of a 3-4-5 triangle, and no direction for zero or past a stated
57    /// floor.
58    #[test]
59    fn arithmetic_and_directions() {
60        let (a, b) = ([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]);
61        assert_eq!(add(a, b), [5.0, 7.0, 9.0]);
62        assert_eq!(sub(b, a), [3.0, 3.0, 3.0]);
63        assert_eq!(scale(a, 2.0), [2.0, 4.0, 6.0]);
64        assert_eq!(dot(a, b), 32.0);
65        assert_eq!(cross([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]), [0.0, 0.0, 1.0]);
66        assert_eq!(length([3.0, 4.0, 0.0]), 5.0);
67        assert_eq!(unit([3.0, 4.0, 0.0]), Some([0.6, 0.8, 0.0]));
68        assert_eq!(unit([0.0; 3]), None);
69        assert_eq!(unit([f64::INFINITY, 0.0, 0.0]), None);
70        assert_eq!(unit_beyond([1e-10, 0.0, 0.0], 1e-9), None);
71        assert_eq!(unit_beyond([2e-9, 0.0, 0.0], 1e-9), Some([1.0, 0.0, 0.0]));
72    }
73}