rustsim-geometry 0.0.1

2-D and 3-D geometric primitives and queries for rustsim (points, AABB, segments, rays, triangles, closest-point, raycast)
Documentation
//! 3-D vector helpers.

/// 3-D vector in metres.
pub type Vec3 = [f64; 3];

/// `a + b`.
#[inline]
pub fn add(a: Vec3, b: Vec3) -> Vec3 {
    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
}

/// `a - b`.
#[inline]
pub fn sub(a: Vec3, b: Vec3) -> Vec3 {
    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
}

/// `a * s`.
#[inline]
pub fn scale(a: Vec3, s: f64) -> Vec3 {
    [a[0] * s, a[1] * s, a[2] * s]
}

/// `a · b`.
#[inline]
pub fn dot(a: Vec3, b: Vec3) -> f64 {
    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}

/// `a × b`.
#[inline]
pub fn cross(a: Vec3, b: Vec3) -> Vec3 {
    [
        a[1] * b[2] - a[2] * b[1],
        a[2] * b[0] - a[0] * b[2],
        a[0] * b[1] - a[1] * b[0],
    ]
}

/// `|a|`.
#[inline]
pub fn norm(a: Vec3) -> f64 {
    dot(a, a).sqrt()
}

/// `|a|²`.
#[inline]
pub fn norm_squared(a: Vec3) -> f64 {
    dot(a, a)
}

/// Unit vector or `[0, 0, 0]` if `|a|` is below machine epsilon.
#[inline]
pub fn normalize(a: Vec3) -> Vec3 {
    let n = norm(a);
    if n < 1e-12 {
        [0.0, 0.0, 0.0]
    } else {
        [a[0] / n, a[1] / n, a[2] / n]
    }
}

/// Euclidean distance `|a - b|`.
#[inline]
pub fn distance(a: Vec3, b: Vec3) -> f64 {
    norm(sub(a, b))
}

/// Linear interpolation `a + t * (b - a)`.
#[inline]
pub fn lerp(a: Vec3, b: Vec3, t: f64) -> Vec3 {
    [
        a[0] + t * (b[0] - a[0]),
        a[1] + t * (b[1] - a[1]),
        a[2] + t * (b[2] - a[2]),
    ]
}

/// Project `v` onto the horizontal XY plane (drop Z).
#[inline]
pub fn xy(v: Vec3) -> [f64; 2] {
    [v[0], v[1]]
}

/// Lift a 2-D vector into 3-D with the given Z component.
#[inline]
pub fn from_xy(xy: [f64; 2], z: f64) -> Vec3 {
    [xy[0], xy[1], z]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn cross_of_basis_axes() {
        assert_eq!(cross([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]), [0.0, 0.0, 1.0]);
    }

    #[test]
    fn norm_of_unit_diagonal() {
        let n = norm([1.0, 1.0, 1.0]);
        assert!((n - 3f64.sqrt()).abs() < 1e-12);
    }

    #[test]
    fn xy_roundtrip() {
        let v: Vec3 = [1.0, 2.0, 3.0];
        assert_eq!(xy(v), [1.0, 2.0]);
        assert_eq!(from_xy(xy(v), v[2]), v);
    }
}