use crate::vec2::{self, Vec2};
use crate::vec3::{self, Vec3};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Segment2 {
pub a: Vec2,
pub b: Vec2,
}
impl Segment2 {
pub fn closest_point(&self, p: Vec2) -> Vec2 {
closest_point_on_segment_2(p, self.a, self.b)
}
pub fn distance_squared(&self, p: Vec2) -> f64 {
let q = self.closest_point(p);
vec2::norm_squared(vec2::sub(p, q))
}
pub fn length(&self) -> f64 {
vec2::distance(self.a, self.b)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Segment3 {
pub a: Vec3,
pub b: Vec3,
}
impl Segment3 {
pub fn closest_point(&self, p: Vec3) -> Vec3 {
closest_point_on_segment_3(p, self.a, self.b)
}
pub fn distance_squared(&self, p: Vec3) -> f64 {
let q = self.closest_point(p);
vec3::norm_squared(vec3::sub(p, q))
}
pub fn length(&self) -> f64 {
vec3::distance(self.a, self.b)
}
}
#[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))
}
#[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]);
}
}