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
//! Line segments in 2-D and 3-D with closest-point queries.

use crate::vec2::{self, Vec2};
use crate::vec3::{self, Vec3};

/// 2-D segment from `a` to `b`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Segment2 {
    /// First endpoint.
    pub a: Vec2,
    /// Second endpoint.
    pub b: Vec2,
}

impl Segment2 {
    /// Closest point on the segment to `p`.
    pub fn closest_point(&self, p: Vec2) -> Vec2 {
        closest_point_on_segment_2(p, self.a, self.b)
    }

    /// Squared distance from `p` to the closest point on the segment.
    pub fn distance_squared(&self, p: Vec2) -> f64 {
        let q = self.closest_point(p);
        vec2::norm_squared(vec2::sub(p, q))
    }

    /// Segment length.
    pub fn length(&self) -> f64 {
        vec2::distance(self.a, self.b)
    }
}

/// 3-D segment from `a` to `b`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Segment3 {
    /// First endpoint.
    pub a: Vec3,
    /// Second endpoint.
    pub b: Vec3,
}

impl Segment3 {
    /// Closest point on the segment to `p`.
    pub fn closest_point(&self, p: Vec3) -> Vec3 {
        closest_point_on_segment_3(p, self.a, self.b)
    }

    /// Squared distance from `p` to the closest point on the segment.
    pub fn distance_squared(&self, p: Vec3) -> f64 {
        let q = self.closest_point(p);
        vec3::norm_squared(vec3::sub(p, q))
    }

    /// Segment length.
    pub fn length(&self) -> f64 {
        vec3::distance(self.a, self.b)
    }
}

/// Returns the closest point on segment `ab` to point `p` in 2-D.
#[inline]
pub fn closest_point_on_segment_2(p: Vec2, a: Vec2, b: Vec2) -> Vec2 {
    let ab = vec2::sub(b, a);
    let denom = vec2::dot(ab, ab);
    if denom < 1e-18 {
        return a;
    }
    let t = (vec2::dot(vec2::sub(p, a), ab) / denom).clamp(0.0, 1.0);
    vec2::add(a, vec2::scale(ab, t))
}

/// Returns the closest point on segment `ab` to point `p` in 3-D.
#[inline]
pub fn closest_point_on_segment_3(p: Vec3, a: Vec3, b: Vec3) -> Vec3 {
    let ab = vec3::sub(b, a);
    let denom = vec3::dot(ab, ab);
    if denom < 1e-18 {
        return a;
    }
    let t = (vec3::dot(vec3::sub(p, a), ab) / denom).clamp(0.0, 1.0);
    vec3::add(a, vec3::scale(ab, t))
}

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

    #[test]
    fn closest_point_on_2d_segment() {
        let s = Segment2 {
            a: [0.0, 0.0],
            b: [10.0, 0.0],
        };
        assert_eq!(s.closest_point([-5.0, 5.0]), [0.0, 0.0]);
        assert_eq!(s.closest_point([15.0, 5.0]), [10.0, 0.0]);
        assert_eq!(s.closest_point([5.0, 5.0]), [5.0, 0.0]);
    }

    #[test]
    fn closest_point_on_3d_segment() {
        let s = Segment3 {
            a: [0.0, 0.0, 0.0],
            b: [0.0, 0.0, 10.0],
        };
        assert_eq!(s.closest_point([5.0, 0.0, 5.0]), [0.0, 0.0, 5.0]);
    }
}