use crate::maths::vec::Vec3;
pub fn longest_distance_between_lines_squared(
a_start: &[f32; 3],
a_end: &[f32; 3],
b_start: &[f32; 3],
b_end: &[f32; 3],
) -> f64 {
let a_start_distance =
shortest_distance_from_point_to_line_segment_squared(a_start, b_start, b_end);
let a_end_distance =
shortest_distance_from_point_to_line_segment_squared(a_end, b_start, b_end);
return a_start_distance.max(a_end_distance);
}
pub fn shortest_line_distance_squared(
a_start: &[f32; 3],
a_end: &[f32; 3],
b_start: &[f32; 3],
b_end: &[f32; 3],
) -> f64 {
let a_start = Vec3::new(a_start[0] as f64, a_start[1] as f64, a_start[2] as f64);
let a_end = Vec3::new(a_end[0] as f64, a_end[1] as f64, a_end[2] as f64);
let b_start = Vec3::new(b_start[0] as f64, b_start[1] as f64, b_start[2] as f64);
let b_end = Vec3::new(b_end[0] as f64, b_end[1] as f64, b_end[2] as f64);
let (closest_on_a, closest_on_b) = closest_points_on_lines(a_start, a_end, b_start, b_end);
return (closest_on_a - closest_on_b).length_squared();
}
pub fn closest_points_on_lines(
a_start: Vec3,
a_end: Vec3,
b_start: Vec3,
b_end: Vec3,
) -> (Vec3, Vec3) {
let a = a_end - a_start;
let b = b_end - b_start;
let c = b_start - a_start;
let a_dot_a = a.dot(&a);
let a_dot_b = a.dot(&b);
let b_dot_b = b.dot(&b);
let a_dot_c = a.dot(&c);
let b_dot_c = b.dot(&c);
let mut s: f64;
let mut t: f64;
let denominator = a_dot_a * b_dot_b - a_dot_b * a_dot_b;
if denominator != 0.0 {
s = (b_dot_b * a_dot_c - a_dot_b * b_dot_c) / denominator;
s = s.clamp(0.0, 1.0);
} else {
s = 0.0;
}
if b_dot_b != 0.0 {
t = (s * a_dot_b - b_dot_c) / b_dot_b;
if t < 0.0 {
t = 0.0;
if a_dot_a != 0.0 {
s = a_dot_c / a_dot_a;
s = s.clamp(0.0, 1.0);
} else {
s = 0.0;
}
} else if t > 1.0 {
t = 1.0;
if a_dot_a != 0.0 {
s = (a_dot_b + a_dot_c) / a_dot_a;
s = s.clamp(0.0, 1.0);
} else {
s = 0.0;
}
}
} else {
t = 0.0;
if a_dot_a != 0.0 {
s = a_dot_c / a_dot_a;
s = s.clamp(0.0, 1.0);
} else {
s = 0.0;
}
}
let closest_on_a = a_start + s * a;
let closest_on_b = b_start + t * b;
return (closest_on_a, closest_on_b);
}
pub fn shortest_distance_from_point_to_line_segment_squared(
point: &[f32; 3],
line_start: &[f32; 3],
line_end: &[f32; 3],
) -> f64 {
let line_start = Vec3::new(
line_start[0] as f64,
line_start[1] as f64,
line_start[2] as f64,
);
let line_end = Vec3::new(line_end[0] as f64, line_end[1] as f64, line_end[2] as f64);
let point = Vec3::new(point[0] as f64, point[1] as f64, point[2] as f64);
return shortest_distance_from_point_to_line_segment_squared_vec(point, line_start, line_end);
}
pub fn shortest_distance_from_point_to_line_segment_squared_vec(
point: Vec3,
line_start: Vec3,
line_end: Vec3,
) -> f64 {
let line = line_end - line_start;
let start_to_point = point - line_start;
let t = start_to_point.dot(&line) / line.dot(&line);
let t = t.clamp(0.0, 1.0);
let closest_point_on_segment = line_start + t * line;
return (point - closest_point_on_segment).length_squared();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_shortest_distance_between_lines_that_are_parallel() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let (b_start, b_end) = ([3.0, 0.0, 0.0], [3.0, 1.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 3.0);
}
#[test]
fn test_get_shortest_distance_between_lines_that_are_parallel_where_one_is_longer() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let (b_start, b_end) = ([3.0, -10.0, 0.0], [3.0, 10.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 3.0);
}
#[test]
fn test_get_shortest_distance_between_collinear_lines_that_meet_at_a_vertex() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
let (b_start, b_end) = ([1.0, 1.0, 1.0], [2.0, 2.0, 2.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_get_shortest_distance_between_lines_that_meet_at_a_vertex_but_are_not_parallel() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_get_shortest_distance_between_lines_that_are_the_same() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_get_shortest_distance_between_lines_that_intersect_orthogonally() {
let (a_start, a_end) = ([-1.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([0.0, -1.0, 0.0], [0.0, 1.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_get_shortest_distance_between_lines_that_intersect_but_are_not_orthogonal() {
let (a_start, a_end) = ([-1.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([-1.0, -1.0, 0.0], [1.0, 1.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_get_shortest_distance_between_lines_where_line_edge_and_line_vertex_are_closest() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([0.5, 1.0, 0.0], [1.0, 2.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 1.0);
}
#[test]
fn test_get_shortest_distance_between_lines_where_vertices_are_closest() {
let (a_start, a_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let (b_start, b_end) = ([5.0, 0.0, 0.0], [10.0, 0.0, 0.0]);
let distance_squared = shortest_line_distance_squared(&a_start, &a_end, &b_start, &b_end);
assert_eq!(distance_squared.sqrt(), 4.0);
}
#[test]
fn test_shortest_distance_from_point_to_line_segment_squared() {
let point = [0.5, 1.0, 0.0];
let (line_start, line_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let distance_squared =
shortest_distance_from_point_to_line_segment_squared(&point, &line_start, &line_end);
assert_eq!(distance_squared.sqrt(), 1.0);
}
#[test]
fn test_shortest_distance_from_point_to_line_segment_squared_where_point_is_segment_vertex() {
let point = [0.0, 0.0, 0.0];
let (line_start, line_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let distance_squared =
shortest_distance_from_point_to_line_segment_squared(&point, &line_start, &line_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_shortest_distance_from_point_to_line_segment_squared_where_point_is_on_segment() {
let point = [0.5, 0.0, 0.0];
let (line_start, line_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let distance_squared =
shortest_distance_from_point_to_line_segment_squared(&point, &line_start, &line_end);
assert_eq!(distance_squared.sqrt(), 0.0);
}
#[test]
fn test_shortest_distance_from_point_to_line_segment_squared_where_point_is_outside_segment_but_collinear()
{
let point = [2.0, 0.0, 0.0];
let (line_start, line_end) = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
let distance_squared =
shortest_distance_from_point_to_line_segment_squared(&point, &line_start, &line_end);
assert_eq!(distance_squared.sqrt(), 1.0);
}
}