parry2d/transformation/
convex_hull2.rs1use alloc::vec::Vec;
6
7use crate::math::{Real, Vector2};
8use num_traits::Zero;
9
10fn support_point_id_2d(direction: Vector2, points: &[Vector2]) -> Option<usize> {
13 let mut argmax = None;
14 let mut max = -Real::MAX;
15
16 for (id, pt) in points.iter().enumerate() {
17 let dot = direction.dot(*pt);
18 if dot > max {
19 argmax = Some(id);
20 max = dot;
21 }
22 }
23
24 argmax
25}
26
27fn indexed_support_point_id_2d<I>(direction: Vector2, points: &[Vector2], idx: I) -> Option<usize>
28where
29 I: Iterator<Item = usize>,
30{
31 let mut argmax = None;
32 let mut max = -Real::MAX;
33
34 for i in idx {
35 let dot = direction.dot(points[i]);
36 if dot > max {
37 argmax = Some(i);
38 max = dot;
39 }
40 }
41
42 argmax
43}
44
45#[cfg(feature = "dim2")]
49pub fn convex_hull2(points: &[Vector2]) -> Vec<Vector2> {
50 convex_hull2_idx(points)
51 .into_iter()
52 .map(|id| points[id])
53 .collect()
54}
55
56pub fn convex_hull2_idx(points: &[Vector2]) -> Vec<usize> {
61 let mut undecidable_points = Vec::new();
62 let mut segments = get_initial_polyline(points, &mut undecidable_points);
63
64 let mut i = 0;
65 while i != segments.len() {
66 if !segments[i].valid {
67 i += 1;
68 continue;
69 }
70
71 let pt_id = indexed_support_point_id_2d(
72 segments[i].normal,
73 points,
74 segments[i].visible_points.iter().copied(),
75 );
76
77 if let Some(point) = pt_id {
78 segments[i].valid = false;
79
80 attach_and_push_facets2(
81 segments[i].prev,
82 segments[i].next,
83 point,
84 points,
85 &mut segments,
86 i,
87 &mut undecidable_points,
88 );
89 }
90
91 i += 1;
92 }
93
94 let mut idx = Vec::new();
95 let mut curr_facet = 0;
96
97 while !segments[curr_facet].valid {
98 curr_facet += 1
99 }
100
101 let first_facet = curr_facet;
102
103 loop {
104 let curr = &segments[curr_facet];
105
106 if curr.valid {
107 idx.push(curr.pts[0]);
108 }
109
110 curr_facet = curr.next;
111
112 if curr_facet == first_facet {
113 break;
114 }
115 }
116
117 idx
118}
119
120fn get_initial_polyline(points: &[Vector2], undecidable: &mut Vec<usize>) -> Vec<SegmentFacet> {
121 let mut res = Vec::new();
122
123 assert!(points.len() >= 2);
124
125 let p1 = support_point_id_2d(Vector2::X, points).unwrap();
126 let mut p2 = p1;
127
128 let direction = [-Vector2::X, -Vector2::Y, Vector2::Y];
129
130 for dir in direction.iter() {
131 p2 = support_point_id_2d(*dir, points).unwrap();
132
133 let p1p2 = points[p2] - points[p1];
134
135 if !p1p2.length_squared().is_zero() {
136 break;
137 }
138 }
139
140 assert!(
141 p1 != p2,
142 "Failed to build the 2d convex hull of this point cloud."
143 );
144
145 let mut f1 = SegmentFacet::new(p1, p2, 1, 1, points);
147 let mut f2 = SegmentFacet::new(p2, p1, 0, 0, points);
148
149 for i in 0..points.len() {
151 if i == p1 || i == p2 {
152 continue;
153 }
154 if f1.can_be_seen_by(i, points) {
155 f1.visible_points.push(i);
156 } else if f2.can_be_seen_by(i, points) {
157 f2.visible_points.push(i);
158 } else {
159 undecidable.push(i);
161 }
162 }
163
164 res.push(f1);
165 res.push(f2);
166
167 res
168}
169
170fn attach_and_push_facets2(
171 prev_facet: usize,
172 next_facet: usize,
173 point: usize,
174 points: &[Vector2],
175 segments: &mut Vec<SegmentFacet>,
176 removed_facet: usize,
177 undecidable: &mut Vec<usize>,
178) {
179 let new_facet1_id = segments.len();
180 let new_facet2_id = new_facet1_id + 1;
181 let prev_pt = segments[prev_facet].pts[1];
182 let next_pt = segments[next_facet].pts[0];
183
184 let mut new_facet1 = SegmentFacet::new(prev_pt, point, prev_facet, new_facet2_id, points);
185 let mut new_facet2 = SegmentFacet::new(point, next_pt, new_facet1_id, next_facet, points);
186
187 segments[prev_facet].next = new_facet1_id;
188 segments[next_facet].prev = new_facet2_id;
189
190 for visible_point in segments[removed_facet].visible_points.iter() {
192 if *visible_point == point {
193 continue;
194 }
195
196 if new_facet1.can_be_seen_by(*visible_point, points) {
197 new_facet1.visible_points.push(*visible_point);
198 } else if new_facet2.can_be_seen_by(*visible_point, points) {
199 new_facet2.visible_points.push(*visible_point);
200 }
201 }
203
204 let mut i = 0;
206
207 while i != undecidable.len() {
208 if new_facet1.can_be_seen_by(undecidable[i], points) {
209 new_facet1.visible_points.push(undecidable[i]);
210 let _ = undecidable.swap_remove(i);
211 } else if new_facet2.can_be_seen_by(undecidable[i], points) {
212 new_facet2.visible_points.push(undecidable[i]);
213 let _ = undecidable.swap_remove(i);
214 } else {
215 i += 1;
216 }
217 }
218
219 segments.push(new_facet1);
220 segments.push(new_facet2);
221}
222
223struct SegmentFacet {
224 pub valid: bool,
225 pub normal: Vector2,
226 pub next: usize,
227 pub prev: usize,
228 pub pts: [usize; 2],
229 pub visible_points: Vec<usize>,
230}
231
232impl SegmentFacet {
233 pub fn new(p1: usize, p2: usize, prev: usize, next: usize, points: &[Vector2]) -> SegmentFacet {
234 let p1p2 = points[p2] - points[p1];
235
236 let normal = Vector2::new(p1p2.y, -p1p2.x);
237 let norm = normal.length();
238 let normalized = if norm > 0.0 {
239 normal / norm
240 } else {
241 Vector2::ZERO
242 };
243
244 SegmentFacet {
245 valid: norm != 0.0,
246 normal: normalized,
247 prev,
248 next,
249 pts: [p1, p2],
250 visible_points: Vec::new(),
251 }
252 }
253
254 pub fn can_be_seen_by(&self, point: usize, points: &[Vector2]) -> bool {
255 let p0 = &points[self.pts[0]];
256 let pt = &points[point];
257
258 let _eps = crate::math::DEFAULT_EPSILON;
259
260 (*pt - *p0).dot(self.normal) > _eps * 100.0
261 }
262}