#![allow(dead_code)]
use crate::prelude::*;
#[derive(Debug, PartialEq)]
pub enum DistSegmentCircleConfig {
OnePoint(f64, Point),
TwoPoints(f64, Point, Point),
}
const ZERO: f64 = 0.0;
const ONE: f64 = 1.0;
pub fn dist_segment_circle(seg: &Segment, circle: &Circle) -> DistSegmentCircleConfig {
let line = line(seg.a, seg.b - seg.a);
let dlc = dist_line_circle(&line, circle);
match dlc {
DistLineCircleConfig::TwoPairs(
_,
param0,
param1,
_closest00,
closest01,
_closest10,
closest11,
) => {
if param0 > ONE && param1 > ONE {
let (dist, p0, _) = dist_to_circle(seg, circle);
DistSegmentCircleConfig::OnePoint(dist, p0)
} else
if param0 >= ZERO && param0 <= ONE && param1 > ONE {
DistSegmentCircleConfig::OnePoint(ZERO, closest01)
} else
if param0 >= ZERO && param0 <= ONE && param1 >= ZERO && param1 <= ONE {
DistSegmentCircleConfig::TwoPoints(ZERO, closest01, closest11)
} else
if param0 < ZERO && param1 > ONE {
let (dist, p0, _) = dist_to_circle(seg, circle);
DistSegmentCircleConfig::OnePoint(dist, p0)
} else
if param0 < ZERO && param1 >= ZERO && param1 <= ONE {
DistSegmentCircleConfig::OnePoint(ZERO, closest11)
} else {
let (dist, p0, _) = dist_to_circle(seg, circle);
DistSegmentCircleConfig::OnePoint(dist, p0)
}
}
DistLineCircleConfig::OnePair(d, param, _, closest1) => {
if param < ZERO {
let (dist, closest, _) = dist_point_circle(&seg.a, circle);
DistSegmentCircleConfig::OnePoint(dist, closest)
} else if param > ONE {
let (dist0, closest0, _) = dist_point_circle(&seg.a, circle);
let (dist1, closest1, _) = dist_point_circle(&seg.b, circle);
if dist0 <= dist1 {
DistSegmentCircleConfig::OnePoint(dist0, closest0)
} else {
DistSegmentCircleConfig::OnePoint(dist1, closest1)
}
} else {
DistSegmentCircleConfig::OnePoint(d, closest1)
}
}
}
}
fn dist_to_circle(seg: &Segment, circle: &Circle) -> (f64, Point, Point) {
let (dist0, p0, _) = dist_point_circle(&seg.a, circle);
let (dist1, p1, _) = dist_point_circle(&seg.b, circle);
if dist0 <= dist1 {
(dist0, p0, p1)
} else {
(dist1, p1, p0)
}
}
#[cfg(test)]
mod test_dist_segment_circle {
use crate::{
circle::circle,
distance::dist_segment_circle::DistSegmentCircleConfig,
point::point,
segment::{Segment, segment},
};
fn rev(seg: Segment) -> Segment {
segment(seg.b, seg.a)
}
#[test]
fn test_p0_p1_outside_segment_outside_circle_01() {
let c = circle(point(0.0, 0.0), 2.0);
let seg = segment(point(-5.0, 1.0), point(-4.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(
2.1231056256176606,
point(-1.9402850002906638, 0.48507125007266594)
)
);
let seg = rev(seg);
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(
2.1231056256176606,
point(-1.9402850002906638, 0.48507125007266594)
)
);
}
#[test]
fn test_p0_outside_p1_inside_circle() {
let c = circle(point(0.0, 0.0), 2.0);
let seg = segment(point(-3.0, 1.0), point(1.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(0.0, point(-1.7320508075688772, 1.0))
);
let seg = rev(seg);
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(0.0, point(-1.7320508075688772, 1.0))
);
}
#[test]
fn test_p0_outside_p1_outside_segment_inside_circle() {
let c = circle(point(0.0, 0.0), 2.0);
let seg = segment(point(-3.0, 1.0), point(3.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::TwoPoints(
0.0,
point(-1.7320508075688774, 1.0),
point(1.7320508075688767, 1.0)
)
);
let seg = rev(seg);
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::TwoPoints(
0.0,
point(1.7320508075688774, 1.0),
point(-1.7320508075688767, 1.0)
)
);
}
#[test]
fn test_p0_inside_p1_inside_circle() {
let c = circle(point(0.0, 0.0), 2.0);
let seg = segment(point(-1.0, 1.0), point(1.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(
0.5857864376269049,
point(-1.414213562373095, 1.414213562373095)
)
);
let seg = rev(seg);
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(
0.5857864376269049,
point(1.414213562373095, 1.414213562373095)
)
);
}
#[test]
fn test_segment_tangent_to_circle() {
let c = circle(point(0.0, 0.0), 1.0);
let seg = segment(point(-2.0, 1.0), point(2.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(0.0, point(0.0, 1.0))
);
}
#[test]
fn test_closest_point_outside_segment_left() {
let c = circle(point(0.0, 0.0), 1.0);
let seg = segment(point(2.0, 1.0), point(4.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
let (expected_dist, expected_closest, _) = super::super::dist_point_circle::dist_point_circle(&point(2.0, 1.0), &c);
assert_eq!(dist, DistSegmentCircleConfig::OnePoint(expected_dist, expected_closest));
}
#[test]
fn test_closest_point_outside_segment_right() {
let c = circle(point(0.0, 0.0), 1.0);
let seg = segment(point(-2.0, 1.0), point(-1.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
let (_, _, _) = super::super::dist_point_circle::dist_point_circle(&seg.a, &c);
let (dist_b, closest_b, _) = super::super::dist_point_circle::dist_point_circle(&seg.b, &c);
assert_eq!(
dist,
DistSegmentCircleConfig::OnePoint(dist_b, closest_b)
);
}
#[test]
fn test_tangent_line_param_greater_than_one_dist0_less_equal() {
let c = circle(point(0.0, 0.0), 1.0);
let seg = segment(point(-1.0, 0.5), point(-1.0, 1.0));
let dist = super::dist_segment_circle(&seg, &c);
let (dist_a, closest_a, _) = super::super::dist_point_circle::dist_point_circle(&seg.a, &c);
let (dist_b, _, _) = super::super::dist_point_circle::dist_point_circle(&seg.b, &c);
assert!(dist_a < dist_b);
assert_eq!(dist, DistSegmentCircleConfig::OnePoint(dist_a, closest_a));
}
}