geometry_algorithm/
closest_points.rs1use geometry_strategy::{CartesianClosestPoints, ClosestPointsStrategy};
15
16#[inline]
31#[must_use]
32pub fn closest_points<A, B>(
33 a: &A,
34 b: &B,
35) -> (
36 <CartesianClosestPoints as ClosestPointsStrategy<A, B>>::Out,
37 <CartesianClosestPoints as ClosestPointsStrategy<A, B>>::Out,
38)
39where
40 CartesianClosestPoints: ClosestPointsStrategy<A, B>,
41{
42 CartesianClosestPoints.closest_points(a, b)
43}
44
45#[cfg(test)]
46#[allow(
47 clippy::float_cmp,
48 reason = "Closest-point coordinates are exact for these inputs."
49)]
50mod tests {
51 use super::closest_points;
58 use geometry_cs::Cartesian;
59 use geometry_model::{Linestring, Point2D, Segment};
60 use geometry_strategy::{DistanceStrategy, Pythagoras};
61 use geometry_trait::Point as _;
62
63 type Pt = Point2D<f64, Cartesian>;
64
65 #[test]
66 fn point_above_segment_drops_perpendicular() {
67 let p = Pt::new(0., 5.);
69 let s = Segment::new(Pt::new(0., 0.), Pt::new(10., 0.));
70 let (a, b) = closest_points(&p, &s);
71 assert_eq!((a.get::<0>(), a.get::<1>()), (0., 5.));
72 assert_eq!((b.get::<0>(), b.get::<1>()), (0., 0.));
73 assert!((Pythagoras.distance(&a, &b) - 5.0).abs() < 1e-12);
74 }
75
76 #[test]
77 fn point_on_segment_returns_input() {
78 let p = Pt::new(1., 1.);
79 let s = Segment::new(Pt::new(0., 0.), Pt::new(3., 3.));
80 let (a, b) = closest_points(&p, &s);
81 assert!(Pythagoras.distance(&a, &b) < 1e-12);
82 }
83
84 #[test]
85 fn crossing_segments_share_intersection_point() {
86 let a = Segment::new(Pt::new(0., 0.), Pt::new(2., 2.));
88 let b = Segment::new(Pt::new(0., 2.), Pt::new(2., 0.));
89 let (ca, cb) = closest_points(&a, &b);
90 assert!((ca.get::<0>() - 1.0).abs() < 1e-12 && (ca.get::<1>() - 1.0).abs() < 1e-12);
91 assert!(Pythagoras.distance(&ca, &cb) < 1e-12);
92 }
93
94 #[test]
95 fn parallel_linestrings_closest_pair() {
96 let a: Linestring<Pt> =
99 Linestring::from_vec(alloc::vec![Pt::new(0., 0.), Pt::new(10., 0.),]);
100 let b: Linestring<Pt> =
101 Linestring::from_vec(alloc::vec![Pt::new(2., 3.), Pt::new(8., 3.),]);
102 let (ca, cb) = closest_points(&a, &b);
103 assert!((Pythagoras.distance(&ca, &cb) - 3.0).abs() < 1e-9);
104 }
105
106 #[test]
107 #[should_panic(expected = "empty or degenerate linestring in closest_points")]
108 fn degenerate_linestring_panics() {
109 let a: Linestring<Pt> = Linestring::from_vec(alloc::vec![Pt::new(0., 0.)]);
110 let b: Linestring<Pt> =
111 Linestring::from_vec(alloc::vec![Pt::new(0., 0.), Pt::new(1., 0.),]);
112 let _ = closest_points(&a, &b);
113 }
114}