iron_shapes/algorithms/
convex_hull.rs1use crate::prelude::{Edge, Point, Side, SimplePolygon};
9use num_traits::Num;
10
11pub fn convex_hull<T>(points: Vec<Point<T>>) -> SimplePolygon<T>
16where
17 T: Num + Ord + Copy,
18{
19 let p = {
21 let mut p = points;
22 p.sort();
23 p
24 };
25
26 assert!(p.len() >= 2);
27
28 let minmin = 0;
30 let p_minmin = p[minmin];
31 let maxmax = p.len() - 1;
33 let p_maxmax = p[maxmax];
34 let minmax = p
36 .iter()
37 .enumerate()
38 .take_while(|(_, p)| p.x == p_minmin.x)
39 .last()
40 .unwrap()
41 .0;
42 let p_minmax = p[minmax];
43 let maxmin = p
45 .iter()
46 .enumerate()
47 .rev()
48 .take_while(|(_, p)| p.x == p_maxmax.x)
49 .last()
50 .unwrap()
51 .0;
52 let p_maxmin = p[maxmin];
53
54 debug_assert!(minmin <= minmax);
55 debug_assert!(minmax <= maxmin || p_minmax.x == p_maxmin.x);
56 debug_assert!(maxmin <= maxmax);
57
58 debug_assert!(p.iter().all(|&p| p_minmin <= p));
59 debug_assert!(p.iter().all(|&p| p_maxmax >= p));
60 debug_assert!(p
61 .iter()
62 .all(|&p| p_minmax.x < p.x || (p_minmax.x == p.x && p_minmax.y >= p.y)));
63 debug_assert!(p
64 .iter()
65 .all(|&p| p_maxmin.x > p.x || (p_maxmin.x == p.x && p_maxmin.y <= p.y)));
66
67 if p_minmin.x == p_maxmax.x {
69 if p_minmin.y == p_maxmax.y {
70 SimplePolygon::new(vec![p_minmin])
71 } else {
72 SimplePolygon::new(vec![p_minmin, p_maxmax])
73 }
74 } else {
75 let build_half_hull = |l: Edge<T>, points: &[Point<T>]| {
76 let mut stack = vec![l.start];
78
79 for &pi in points {
80 if l.side_of(pi) == Side::Right {
82 while stack.len() >= 2 {
83 let pt1 = stack[stack.len() - 1];
84 let pt2 = stack[stack.len() - 2];
85
86 if Edge::new(pt2, pt1).side_of(pi) == Side::Left {
87 break;
90 }
91 stack.pop();
92 }
93 stack.push(pi);
94 }
95 }
96
97 stack
98 };
99
100 let l_min = Edge::new(p_minmin, p_maxmin);
102 let mut lower_half_hull = build_half_hull(l_min, &p[minmax + 1..maxmin]);
103
104 if p_maxmin != p_maxmax {
106 lower_half_hull.push(p_maxmin);
107 }
108
109 let l_max = Edge::new(p_maxmax, p_minmax);
111 let mut upper_half_hull = build_half_hull(l_max, &p[minmax + 1..maxmin]);
112
113 if p_minmax != p_minmin {
115 upper_half_hull.push(p_minmax);
116 }
117
118 lower_half_hull.append(&mut upper_half_hull);
120
121 SimplePolygon::new(lower_half_hull).normalized()
122 }
123}