parry3d/transformation/convex_hull3/
convex_hull.rs

1use super::InitialMesh;
2use super::{ConvexHullError, TriangleFacet};
3use crate::math::Real;
4use crate::transformation::convex_hull_utils::indexed_support_point_nth;
5use crate::transformation::convex_hull_utils::{indexed_support_point_id, normalize};
6use crate::utils;
7use alloc::{vec, vec::Vec};
8use na::{self, Point3};
9
10/// Computes the convex hull of a set of 3d points.
11pub fn convex_hull(points: &[Point3<Real>]) -> (Vec<Point3<Real>>, Vec<[u32; 3]>) {
12    try_convex_hull(points).unwrap()
13}
14
15/// Computes the convex hull of a set of 3d points.
16pub fn try_convex_hull(
17    points: &[Point3<Real>],
18) -> Result<(Vec<Point3<Real>>, Vec<[u32; 3]>), ConvexHullError> {
19    if points.len() < 3 {
20        return Err(ConvexHullError::IncompleteInput);
21    }
22
23    // print_buildable_vec("input", points);
24
25    let mut normalized_points = points.to_vec();
26    let _ = normalize(&mut normalized_points[..]);
27
28    let mut undecidable_points = Vec::new();
29    let mut silhouette_loop_facets_and_idx = Vec::new();
30    let mut removed_facets = Vec::new();
31
32    let mut triangles;
33
34    match super::try_get_initial_mesh(points, &mut normalized_points[..], &mut undecidable_points)?
35    {
36        InitialMesh::Facets(facets) => {
37            triangles = facets;
38        }
39        InitialMesh::ResultMesh(vertices, indices) => {
40            return Ok((vertices, indices));
41        }
42    }
43
44    let mut i = 0;
45    while i != triangles.len() {
46        silhouette_loop_facets_and_idx.clear();
47
48        if !triangles[i].valid || triangles[i].affinely_dependent {
49            i += 1;
50            continue;
51        }
52
53        // TODO: use triangles[i].furthest_point instead.
54        let pt_id = indexed_support_point_id(
55            &triangles[i].normal,
56            &normalized_points[..],
57            triangles[i].visible_points[..].iter().copied(),
58        );
59
60        if let Some(point) = pt_id {
61            triangles[i].valid = false;
62
63            removed_facets.clear();
64            removed_facets.push(i);
65
66            for j in 0usize..3 {
67                // println!(">> loop;");
68                compute_silhouette(
69                    triangles[i].adj[j],
70                    triangles[i].indirect_adj_id[j],
71                    point,
72                    &mut silhouette_loop_facets_and_idx,
73                    &normalized_points[..],
74                    &mut removed_facets,
75                    &mut triangles[..],
76                );
77            }
78
79            // In some degenerate cases (because of float rounding problems), the silhouette may:
80            // 1. Contain self-intersections (i.e. a single vertex is used by more than two edges).
81            // 2. Contain multiple disjoint (but nested) loops.
82            fix_silhouette_topology(
83                &normalized_points,
84                &mut silhouette_loop_facets_and_idx,
85                &mut removed_facets,
86                &mut triangles[..],
87            )?;
88
89            // Check that the silhouette is valid.
90            // TODO: remove this debug code.
91            // {
92            //     for (facet, id) in &silhouette_loop_facets_and_idx {
93            //         assert!(triangles[*facet].valid);
94            //         assert!(!triangles[triangles[*facet].adj[*id]].valid);
95            //     }
96            // }
97
98            if silhouette_loop_facets_and_idx.is_empty() {
99                // Due to inaccuracies, the silhouette could not be computed
100                // (the point seems to be visible from… every triangle).
101                let mut any_valid = false;
102                for triangle in &triangles[i + 1..] {
103                    if triangle.valid && !triangle.affinely_dependent {
104                        any_valid = true;
105                    }
106                }
107
108                if any_valid {
109                    return Err(ConvexHullError::InternalError(
110                        "Internal error: exiting an unfinished work.",
111                    ));
112                }
113
114                // TODO: this is very harsh.
115                triangles[i].valid = true;
116                break;
117            }
118
119            attach_and_push_facets(
120                &silhouette_loop_facets_and_idx[..],
121                point,
122                &normalized_points[..],
123                &mut triangles,
124                &removed_facets[..],
125                &mut undecidable_points,
126            );
127
128            // println!("Verifying facets at iteration: {}, k: {}", i, k);
129            // for i in 0..triangles.len() {
130            //     if triangles[i].valid {
131            //         super::check_facet_links(i, &triangles[..]);
132            //     }
133            // }
134        }
135
136        i += 1;
137    }
138
139    let mut idx = Vec::new();
140
141    for facet in triangles.iter() {
142        if facet.valid {
143            idx.push([
144                facet.pts[0] as u32,
145                facet.pts[1] as u32,
146                facet.pts[2] as u32,
147            ]);
148        }
149    }
150
151    let mut points = points.to_vec();
152    utils::remove_unused_points(&mut points, &mut idx[..]);
153    // super::check_convex_hull(&points, &idx);
154
155    Ok((points, idx))
156}
157
158fn compute_silhouette(
159    facet: usize,
160    indirect_id: usize,
161    point: usize,
162    out_facets_and_idx: &mut Vec<(usize, usize)>,
163    points: &[Point3<Real>],
164    removed_facets: &mut Vec<usize>,
165    triangles: &mut [TriangleFacet],
166) {
167    if triangles[facet].valid {
168        if !triangles[facet].order_independent_can_be_seen_by_point(point, points) {
169            // println!("triangles: {}, valid: true, keep: true", facet);
170            // println!(
171            //     "Taking edge: [{}, {}]",
172            //     triangles[facet].second_point_from_edge(indirect_id),
173            //     triangles[facet].first_point_from_edge(indirect_id)
174            // );
175            out_facets_and_idx.push((facet, indirect_id));
176        } else {
177            triangles[facet].valid = false; // The facet must be removed from the convex hull.
178            removed_facets.push(facet);
179            // println!("triangles: {}, valid: true, keep: false", facet);
180
181            compute_silhouette(
182                triangles[facet].adj[(indirect_id + 1) % 3],
183                triangles[facet].indirect_adj_id[(indirect_id + 1) % 3],
184                point,
185                out_facets_and_idx,
186                points,
187                removed_facets,
188                triangles,
189            );
190
191            compute_silhouette(
192                triangles[facet].adj[(indirect_id + 2) % 3],
193                triangles[facet].indirect_adj_id[(indirect_id + 2) % 3],
194                point,
195                out_facets_and_idx,
196                points,
197                removed_facets,
198                triangles,
199            );
200        }
201    } else {
202        // println!("triangles: {}, valid: false, keep: false", facet);
203    }
204}
205
206fn fix_silhouette_topology(
207    points: &[Point3<Real>],
208    out_facets_and_idx: &mut Vec<(usize, usize)>,
209    removed_facets: &mut Vec<usize>,
210    triangles: &mut [TriangleFacet],
211) -> Result<(), ConvexHullError> {
212    // TODO: don't allocate this everytime.
213    let mut workspace = vec![0; points.len()];
214    let mut needs_fixing = false;
215
216    // NOTE: we wore with the second_point_from_edge instead
217    // of the first one, because when we traverse the silhouette
218    // we see the second edge point before the first.
219    for (facet, adj_id) in &*out_facets_and_idx {
220        let p = triangles[*facet].second_point_from_edge(*adj_id);
221        workspace[p] += 1;
222
223        if workspace[p] > 1 {
224            needs_fixing = true;
225        }
226    }
227
228    // We detected a topological problem, i.e., we have
229    // multiple loops.
230    if needs_fixing {
231        // First, we need to know which loop is the one we
232        // need to keep.
233        let mut loop_start = 0;
234        for (facet, adj_id) in &*out_facets_and_idx {
235            let p1 = points[triangles[*facet].second_point_from_edge(*adj_id)];
236            let p2 = points[triangles[*facet].first_point_from_edge(*adj_id)];
237            let supp = indexed_support_point_nth(
238                &(p2 - p1),
239                points,
240                out_facets_and_idx
241                    .iter()
242                    .map(|(f, ai)| triangles[*f].second_point_from_edge(*ai)),
243            )
244            .ok_or(ConvexHullError::MissingSupportPoint)?;
245            let selected = &out_facets_and_idx[supp];
246            if workspace[triangles[selected.0].second_point_from_edge(selected.1)] == 1 {
247                // This is a valid point to start with.
248                loop_start = supp;
249                break;
250            }
251        }
252
253        let mut removing = None;
254        let old_facets_and_idx = core::mem::take(out_facets_and_idx);
255
256        for i in 0..old_facets_and_idx.len() {
257            let facet_id = (loop_start + i) % old_facets_and_idx.len();
258            let (facet, adj_id) = old_facets_and_idx[facet_id];
259
260            match removing {
261                Some(p) => {
262                    let p1 = triangles[facet].second_point_from_edge(adj_id);
263                    if p == p1 {
264                        removing = None;
265                    }
266                }
267                _ => {
268                    let p1 = triangles[facet].second_point_from_edge(adj_id);
269                    if workspace[p1] > 1 {
270                        removing = Some(p1);
271                    }
272                }
273            }
274
275            if removing.is_some() {
276                if triangles[facet].valid {
277                    triangles[facet].valid = false;
278                    removed_facets.push(facet);
279                }
280            } else {
281                out_facets_and_idx.push((facet, adj_id));
282            }
283
284            // // Debug
285            // {
286            //     let p1 = triangles[facet].second_point_from_edge(adj_id);
287            //     let p2 = triangles[facet].first_point_from_edge(adj_id);
288            //     if removing.is_some() {
289            //         print!("/{}, {}\\ ", p1, p2);
290            //     } else {
291            //         print!("[{}, {}] ", p1, p2);
292            //     }
293            // }
294        }
295
296        // println!();
297    }
298
299    Ok(())
300}
301
302fn attach_and_push_facets(
303    silhouette_loop_facets_and_idx: &[(usize, usize)],
304    point: usize,
305    points: &[Point3<Real>],
306    triangles: &mut Vec<TriangleFacet>,
307    removed_facets: &[usize],
308    undecidable: &mut Vec<usize>,
309) {
310    // The silhouette is built to be in CCW order.
311    let mut new_facets = Vec::with_capacity(silhouette_loop_facets_and_idx.len());
312
313    // Create new facets.
314    let mut adj_facet: usize;
315    let mut indirect_id: usize;
316
317    for silhouette_loop_facets_and_id in silhouette_loop_facets_and_idx {
318        adj_facet = silhouette_loop_facets_and_id.0;
319        indirect_id = silhouette_loop_facets_and_id.1;
320
321        // print!(
322        //     "[{}, {}] ",
323        //     triangles[adj_facet].second_point_from_edge(indirect_id),
324        //     triangles[adj_facet].first_point_from_edge(indirect_id)
325        // );
326
327        let facet = TriangleFacet::new(
328            point,
329            triangles[adj_facet].second_point_from_edge(indirect_id),
330            triangles[adj_facet].first_point_from_edge(indirect_id),
331            points,
332        );
333        new_facets.push(facet);
334    }
335    // println!();
336
337    // Link the facets together.
338    for i in 0..silhouette_loop_facets_and_idx.len() {
339        let prev_facet = if i == 0 {
340            triangles.len() + silhouette_loop_facets_and_idx.len() - 1
341        } else {
342            triangles.len() + i - 1
343        };
344
345        let (middle_facet, middle_id) = silhouette_loop_facets_and_idx[i];
346        let next_facet = triangles.len() + (i + 1) % silhouette_loop_facets_and_idx.len();
347
348        new_facets[i].set_facets_adjascency(prev_facet, middle_facet, next_facet, 2, middle_id, 0);
349        assert!(!triangles[triangles[middle_facet].adj[middle_id]].valid); // Check that we are not overwriting a valid link.
350        triangles[middle_facet].adj[middle_id] = triangles.len() + i; // The future id of curr_facet.
351        triangles[middle_facet].indirect_adj_id[middle_id] = 1;
352    }
353
354    // Assign to each facets some of the points which can see it.
355    // TODO: refactor this with the others.
356    for curr_facet in removed_facets.iter() {
357        for visible_point in triangles[*curr_facet].visible_points.iter() {
358            if points[*visible_point] == points[point] {
359                continue;
360            }
361
362            let mut furthest = usize::MAX;
363            let mut furthest_dist = 0.0;
364
365            for (i, curr_facet) in new_facets.iter_mut().enumerate() {
366                if !curr_facet.affinely_dependent {
367                    let distance = curr_facet.distance_to_point(*visible_point, points);
368
369                    if distance > furthest_dist {
370                        furthest = i;
371                        furthest_dist = distance;
372                    }
373                }
374            }
375
376            if furthest != usize::MAX && new_facets[furthest].can_see_point(*visible_point, points)
377            {
378                new_facets[furthest].add_visible_point(*visible_point, points);
379            }
380
381            // If none of the facet can be seen from the point, it is implicitly
382            // deleted because it won't be referenced by any facet.
383        }
384    }
385
386    // Try to assign collinear points to one of the new facets.
387    let mut i = 0;
388
389    while i != undecidable.len() {
390        let mut furthest = usize::MAX;
391        let mut furthest_dist = 0.0;
392        let undecidable_point = undecidable[i];
393
394        for (j, curr_facet) in new_facets.iter_mut().enumerate() {
395            if curr_facet.can_see_point(undecidable_point, points) {
396                let distance = curr_facet.distance_to_point(undecidable_point, points);
397
398                if distance > furthest_dist {
399                    furthest = j;
400                    furthest_dist = distance;
401                }
402            }
403        }
404
405        if furthest != usize::MAX {
406            new_facets[furthest].add_visible_point(undecidable_point, points);
407            let _ = undecidable.swap_remove(i);
408        } else {
409            i += 1;
410        }
411    }
412
413    // Push facets.
414    // TODO: can we avoid the tmp vector `new_facets` ?
415    triangles.append(&mut new_facets);
416}
417
418#[cfg(test)]
419mod test {
420    use crate::transformation;
421    #[cfg(feature = "dim2")]
422    use na::Point2;
423
424    #[cfg(feature = "dim2")]
425    #[test]
426    fn test_simple_convex_hull() {
427        let points = [
428            Point2::new(4.723881f32, 3.597233),
429            Point2::new(3.333363, 3.429991),
430            Point2::new(3.137215, 2.812263),
431        ];
432
433        let chull = transformation::convex_hull(points.as_slice());
434
435        assert!(chull.coords.len() == 3);
436    }
437
438    #[cfg(feature = "dim3")]
439    #[test]
440    fn test_ball_convex_hull() {
441        use crate::shape::Ball;
442
443        // This triggered a failure to an affinely dependent facet.
444        let (points, _) = Ball::new(0.4).to_trimesh(20, 20);
445        let (vertices, _) = transformation::convex_hull(points.as_slice());
446
447        // dummy test, we are just checking that the construction did not fail.
448        assert!(vertices.len() == vertices.len());
449    }
450}