ncollide3d/transformation/
convex_hull2.rs

1use std::marker::PhantomData;
2
3#[cfg(feature = "dim2")]
4use crate::procedural::Polyline;
5use crate::transformation::convex_hull_utils::{indexed_support_point_id, support_point_id};
6use na::{self, Point2, Vector2};
7use simba::scalar::RealField;
8
9/// Computes the convex hull of a set of 2d points.
10#[cfg(feature = "dim2")]
11pub fn convex_hull2<N: RealField + Copy>(points: &[Point2<N>]) -> Polyline<N> {
12    let idx = convex_hull2_idx(points);
13    let mut pts = Vec::new();
14
15    for id in idx.into_iter() {
16        pts.push(points[id].clone());
17    }
18
19    Polyline::new(pts, None)
20}
21
22/// Computes the convex hull of a set of 2d points and returns only the indices of the hull
23/// vertices.
24pub fn convex_hull2_idx<N: RealField + Copy>(points: &[Point2<N>]) -> Vec<usize> {
25    let mut undecidable_points = Vec::new();
26    let mut segments = get_initial_polyline(points, &mut undecidable_points);
27
28    let mut i = 0;
29    while i != segments.len() {
30        if !segments[i].valid {
31            i = i + 1;
32            continue;
33        }
34
35        let pt_id =
36            indexed_support_point_id(&segments[i].normal, points, &segments[i].visible_points[..]);
37
38        if let Some(point) = pt_id {
39            segments[i].valid = false;
40
41            attach_and_push_facets2(
42                segments[i].prev,
43                segments[i].next,
44                point,
45                &points[..],
46                &mut segments,
47                i,
48                &mut undecidable_points,
49            );
50        }
51
52        i = i + 1;
53    }
54
55    let mut idx = Vec::new();
56    let mut curr_facet = 0;
57
58    while !segments[curr_facet].valid {
59        curr_facet = curr_facet + 1
60    }
61
62    let first_facet = curr_facet;
63
64    loop {
65        let curr = &segments[curr_facet];
66
67        assert!(curr.valid);
68
69        idx.push(curr.pts[0]);
70
71        curr_facet = curr.next;
72
73        if curr_facet == first_facet {
74            break;
75        }
76    }
77
78    idx
79}
80
81fn get_initial_polyline<N: RealField + Copy>(
82    points: &[Point2<N>],
83    undecidable: &mut Vec<usize>,
84) -> Vec<SegmentFacet<N>> {
85    let mut res = Vec::new();
86
87    assert!(points.len() >= 2);
88
89    let p1 = support_point_id(&Vector2::x(), points).unwrap();
90    let mut p2 = p1;
91
92    let direction = [-Vector2::x(), Vector2::y(), -Vector2::y()];
93
94    for dir in direction.iter() {
95        p2 = support_point_id(dir, points).unwrap();
96
97        let p1p2 = points[p2] - points[p1];
98
99        if !p1p2.norm_squared().is_zero() {
100            break;
101        }
102    }
103
104    assert!(
105        p1 != p2,
106        "Failed to build the 2d convex hull of this point cloud."
107    );
108
109    // Build two facets with opposite normals.
110    let mut f1 = SegmentFacet::new(p1, p2, 1, 1, points);
111    let mut f2 = SegmentFacet::new(p2, p1, 0, 0, points);
112
113    // Attribute points to each facet.
114    for i in 0..points.len() {
115        if i == p1 || i == p2 {
116            continue;
117        }
118        if f1.can_be_seen_by(i, points) {
119            f1.visible_points.push(i);
120        } else if f2.can_be_seen_by(i, points) {
121            f2.visible_points.push(i);
122        } else {
123            // The point is collinear.
124            undecidable.push(i);
125        }
126    }
127
128    res.push(f1);
129    res.push(f2);
130
131    res
132}
133
134fn attach_and_push_facets2<N: RealField + Copy>(
135    prev_facet: usize,
136    next_facet: usize,
137    point: usize,
138    points: &[Point2<N>],
139    segments: &mut Vec<SegmentFacet<N>>,
140    removed_facet: usize,
141    undecidable: &mut Vec<usize>,
142) {
143    let new_facet1_id = segments.len();
144    let new_facet2_id = new_facet1_id + 1;
145    let prev_pt = segments[prev_facet].pts[1];
146    let next_pt = segments[next_facet].pts[0];
147
148    let mut new_facet1 = SegmentFacet::new(prev_pt, point, prev_facet, new_facet2_id, points);
149    let mut new_facet2 = SegmentFacet::new(point, next_pt, new_facet1_id, next_facet, points);
150
151    segments[prev_facet].next = new_facet1_id;
152    segments[next_facet].prev = new_facet2_id;
153
154    // Assign to each facets some of the points which can see it.
155    for visible_point in segments[removed_facet].visible_points.iter() {
156        if *visible_point == point {
157            continue;
158        }
159
160        if new_facet1.can_be_seen_by(*visible_point, points) {
161            new_facet1.visible_points.push(*visible_point);
162        } else if new_facet2.can_be_seen_by(*visible_point, points) {
163            new_facet2.visible_points.push(*visible_point);
164        }
165        // If none of the facet can be seen from the point, it is naturally deleted.
166    }
167
168    // Try to assign collinear points to one of the new facets
169    let mut i = 0;
170
171    while i != undecidable.len() {
172        if new_facet1.can_be_seen_by(undecidable[i], points) {
173            new_facet1.visible_points.push(undecidable[i]);
174            let _ = undecidable.swap_remove(i);
175        } else if new_facet2.can_be_seen_by(undecidable[i], points) {
176            new_facet2.visible_points.push(undecidable[i]);
177            let _ = undecidable.swap_remove(i);
178        } else {
179            i = i + 1;
180        }
181    }
182
183    segments.push(new_facet1);
184    segments.push(new_facet2);
185}
186
187struct SegmentFacet<N: RealField + Copy> {
188    pub valid: bool,
189    pub normal: Vector2<N>,
190    pub next: usize,
191    pub prev: usize,
192    pub pts: [usize; 2],
193    pub visible_points: Vec<usize>,
194    pt_type: PhantomData<Point2<N>>,
195}
196
197impl<N: RealField + Copy> SegmentFacet<N> {
198    pub fn new(
199        p1: usize,
200        p2: usize,
201        prev: usize,
202        next: usize,
203        points: &[Point2<N>],
204    ) -> SegmentFacet<N> {
205        let p1p2 = points[p2] - points[p1];
206
207        let mut normal = Vector2::new(-p1p2.y, p1p2.x);
208
209        if normal.normalize_mut().is_zero() {
210            panic!("ConvexHull hull failure: a segment must not be affinely dependent.");
211        }
212
213        SegmentFacet {
214            valid: true,
215            normal: normal,
216            prev: prev,
217            next: next,
218            pts: [p1, p2],
219            visible_points: Vec::new(),
220            pt_type: PhantomData,
221        }
222    }
223
224    pub fn can_be_seen_by(&self, point: usize, points: &[Point2<N>]) -> bool {
225        let p0 = &points[self.pts[0]];
226        let pt = &points[point];
227
228        let _eps = N::default_epsilon();
229
230        (*pt - *p0).dot(&self.normal) > _eps * na::convert(100.0f64)
231    }
232}