1use geometry_strategy::{ConvexHullStrategy, MonotoneChain};
12use geometry_trait::Point;
13
14#[must_use]
22pub fn convex_hull<G, P>(g: &G) -> geometry_model::Ring<P, true, true>
23where
24 MonotoneChain: ConvexHullStrategy<G, Output = geometry_model::Ring<P, true, true>>,
25 P: Point,
26{
27 MonotoneChain.convex_hull(g)
28}
29
30#[cfg(test)]
31#[allow(
32 clippy::float_cmp,
33 reason = "Hull corner coordinates are exact literals."
34)]
35mod tests {
36 use super::convex_hull;
42 use geometry_cs::Cartesian;
43 use geometry_model::{MultiPoint, Point2D, Polygon, polygon};
44 use geometry_trait::{Point as _, Ring as _};
45
46 type Pt = Point2D<f64, Cartesian>;
47
48 #[test]
49 fn square_plus_interior_point_hull_has_four_corners() {
50 let mp = MultiPoint(alloc::vec![
51 Pt::new(0., 0.),
52 Pt::new(4., 0.),
53 Pt::new(4., 4.),
54 Pt::new(0., 4.),
55 Pt::new(2., 2.),
56 ]);
57 let hull = convex_hull(&mp);
58 assert_eq!(hull.points().count(), 5);
60 assert_eq!(
62 hull.0.first().unwrap().get::<0>(),
63 hull.0.last().unwrap().get::<0>()
64 );
65 assert_eq!(
66 hull.0.first().unwrap().get::<1>(),
67 hull.0.last().unwrap().get::<1>()
68 );
69 }
70
71 #[test]
72 fn concave_polygon_hull_drops_reflex_vertex() {
73 let pg: Polygon<Pt> =
76 polygon![[(0., 0.), (4., 0.), (2., 1.), (4., 4.), (0., 4.), (0., 0.)]];
77 let hull = convex_hull(&pg);
78 assert_eq!(hull.points().count(), 5);
80 let has_reflex = hull
81 .0
82 .iter()
83 .any(|p| p.get::<0>() == 2.0 && p.get::<1>() == 1.0);
84 assert!(!has_reflex);
85 }
86
87 #[test]
90 fn hull_of_fewer_than_three_points_is_the_input() {
91 let empty = MultiPoint::<Pt>(alloc::vec![]);
92 assert_eq!(convex_hull(&empty).points().count(), 0);
93 let one = MultiPoint(alloc::vec![Pt::new(3., 4.)]);
94 let hull = convex_hull(&one);
95 assert_eq!(hull.points().count(), 1);
96 assert_eq!(hull.0[0].get::<0>(), 3.0);
97 let two = MultiPoint(alloc::vec![Pt::new(0., 0.), Pt::new(1., 1.)]);
98 assert_eq!(convex_hull(&two).points().count(), 2);
99 }
100
101 #[test]
103 fn hull_of_linestring() {
104 use geometry_model::{Linestring, linestring};
105 let ls: Linestring<Pt> = linestring![(0., 0.), (4., 0.), (4., 4.), (2., 2.)];
106 let hull = convex_hull(&ls);
107 assert_eq!(hull.points().count(), 4);
109 }
110
111 #[test]
113 fn hull_of_ring() {
114 use geometry_model::Ring;
115 let r: Ring<Pt> = Ring::from_vec(alloc::vec![
116 Pt::new(0., 0.),
117 Pt::new(4., 0.),
118 Pt::new(2., 1.), Pt::new(4., 4.),
120 Pt::new(0., 4.),
121 Pt::new(0., 0.),
122 ]);
123 let hull = convex_hull(&r);
124 assert_eq!(hull.points().count(), 5); assert!(
126 !hull
127 .0
128 .iter()
129 .any(|p| p.get::<0>() == 2.0 && p.get::<1>() == 1.0)
130 );
131 }
132
133 #[test]
136 fn hull_of_polygon_ignores_holes() {
137 let pg: Polygon<Pt> = polygon![
138 [(0., 0.), (4., 0.), (4., 4.), (0., 4.), (0., 0.)],
139 [(1., 1.), (2., 1.), (2., 2.), (1., 2.), (1., 1.)],
140 ];
141 let hull = convex_hull(&pg);
142 assert_eq!(hull.points().count(), 5);
143 assert!(
144 !hull
145 .0
146 .iter()
147 .any(|p| p.get::<0>() == 1.0 && p.get::<1>() == 1.0)
148 );
149 }
150}