triangulation/
lib.rs

1#[cfg(feature = "rayon")]
2use rayon::prelude::*;
3
4pub mod dcel;
5pub mod geom;
6
7pub use dcel::TrianglesDCEL;
8pub use geom::{Point, Triangle};
9
10const STACK_CAPACITY: usize = 512;
11
12/// Option<usize>, where None is represented by -1
13///
14/// Takes 8 bytes instead of 16.
15#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub struct OptionIndex(usize);
17
18impl OptionIndex {
19    /// Returns `Some(idx)` value
20    #[inline]
21    pub fn some(idx: usize) -> OptionIndex {
22        debug_assert!(idx < std::usize::MAX);
23        OptionIndex(idx)
24    }
25
26    /// Returns None value
27    #[inline]
28    pub fn none() -> OptionIndex {
29        OptionIndex(std::usize::MAX)
30    }
31
32    /// Returns true if it is a `Some` value
33    #[inline]
34    pub fn is_some(self) -> bool {
35        self != OptionIndex::none()
36    }
37
38    /// Returns true if it is a `None` value
39    #[inline]
40    pub fn is_none(self) -> bool {
41        self == OptionIndex::none()
42    }
43
44    /// Returns the associated `Option` value
45    #[inline]
46    pub fn get(self) -> Option<usize> {
47        if self.is_some() {
48            Some(self.0)
49        } else {
50            None
51        }
52    }
53}
54
55/// Maps angle between `point` and `center` to index in the hash table
56fn angular_hash(point: Point, center: Point, size: usize) -> usize {
57    let angle = geom::pseudo_angle(point.x - center.x, point.y - center.y);
58    (angle * size as f32) as usize % size
59}
60
61/// Counter-clockwise convex hull
62struct Hull {
63    /// Maps point index to next point index
64    next: Vec<usize>,
65
66    /// Maps point index to previous point index
67    prev: Vec<usize>,
68
69    /// Radial hash table
70    hash_table: Vec<OptionIndex>,
71
72    /// Boundary triangles
73    triangles: Vec<OptionIndex>,
74
75    /// Center point for calculating radial hash
76    center: Point,
77
78    /// Starting point index
79    start: usize,
80}
81
82impl Hull {
83    fn new(seed: [usize; 3], points: &[Point]) -> Hull {
84        let capacity = points.len();
85        let table_size = (capacity as f32).sqrt().ceil() as usize;
86
87        let center = Triangle(points[seed[0]], points[seed[1]], points[seed[2]]).circumcenter();
88
89        let mut hull = Hull {
90            next: vec![0; capacity],
91            prev: vec![0; capacity],
92            hash_table: vec![OptionIndex::none(); table_size],
93            triangles: vec![OptionIndex::none(); capacity],
94            start: seed[0],
95            center,
96        };
97
98        hull.next[seed[0]] = seed[1];
99        hull.next[seed[1]] = seed[2];
100        hull.next[seed[2]] = seed[0];
101
102        hull.prev[seed[0]] = seed[2];
103        hull.prev[seed[1]] = seed[0];
104        hull.prev[seed[2]] = seed[1];
105
106        hull.triangles[seed[0]] = OptionIndex::some(0);
107        hull.triangles[seed[1]] = OptionIndex::some(1);
108        hull.triangles[seed[2]] = OptionIndex::some(2);
109
110        hull.add_hash(seed[0], points[seed[0]]);
111        hull.add_hash(seed[1], points[seed[1]]);
112        hull.add_hash(seed[2], points[seed[2]]);
113
114        hull
115    }
116
117    /// Adds a new point in the hash table
118    fn add_hash(&mut self, index: usize, point: Point) {
119        let table_size = self.hash_table.len();
120        self.hash_table[angular_hash(point, self.center, table_size)] = OptionIndex::some(index);
121    }
122
123    /// Returns the first convex hull edge visible from the point and a boolean
124    /// indicating whether the previous edge may be visible too
125    fn find_visible_edge(&self, point: Point, points: &[Point]) -> Option<(usize, bool)> {
126        let table_size = self.hash_table.len();
127        let hash = angular_hash(point, self.center, table_size);
128
129        let mut start = OptionIndex::none();
130
131        // basically linear probing hash table
132        for i in 0..table_size {
133            start = self.hash_table[(hash + i) % table_size];
134
135            // if e == self.next[e] then it is an empty hash table entry; skip it
136            if start.get().filter(|&e| e != self.next[e]).is_some() {
137                break;
138            }
139        }
140
141        // now `start` is a point near enough to the target
142        // let's go forward to find a visible edge
143
144        let start = self.prev[start.get()?];
145        let mut edge = start;
146
147        loop {
148            let next = self.next[edge];
149            let tri = Triangle(point, points[edge], points[next]);
150
151            if tri.is_left_handed() {
152                // edge is visible, breakin' outta hell
153                break;
154            }
155
156            edge = next;
157            if edge == start {
158                // avoiding the endless loop
159                return None;
160            }
161        }
162
163        // if edge == start then we made 0 iterations, so we can't say for sure
164        // that there are no visible edges preceding the start one
165
166        Some((edge, edge == start))
167    }
168}
169
170/// Calculates the median point (arithmetic mean of the coordinates)
171fn find_center(points: &[Point]) -> Point {
172    let (x_sum, y_sum) = points
173        .iter()
174        .fold((0.0, 0.0), |(x, y), point| (x + point.x, y + point.y));
175
176    Point::new(x_sum / points.len() as f32, y_sum / points.len() as f32)
177}
178
179fn find_seed_triangle(points: &[Point]) -> Option<(Triangle, [usize; 3])> {
180    let center = find_center(&points);
181
182    #[cfg(feature = "rayon")]
183    let iter = points.par_iter();
184
185    #[cfg(not(feature = "rayon"))]
186    let iter = points.iter();
187
188    let (seed_idx, seed) = iter.clone().cloned().enumerate().min_by(|(_, a), (_, b)| {
189        a.distance_sq(center)
190            .partial_cmp(&b.distance_sq(center))
191            .unwrap()
192    })?;
193
194    let (nearest_idx, nearest, _) = iter
195        .clone()
196        .cloned()
197        .enumerate()
198        .filter(|&(i, _)| i != seed_idx)
199        .map(|(i, p)| (i, p, p.distance_sq(seed)))
200        .filter(|(_, _, d)| d.abs() > std::f32::EPSILON)
201        .min_by(|(_, _, a), (_, _, b)| a.partial_cmp(&b).unwrap())?;
202
203    let (third_idx, third) = iter
204        .cloned()
205        .enumerate()
206        .filter(|&(i, _)| i != seed_idx && i != nearest_idx)
207        .min_by(|&(_, a), &(_, b)| {
208            let t0 = Triangle(seed, nearest, a);
209            let t1 = Triangle(seed, nearest, b);
210
211            t0.circumradius_sq()
212                .partial_cmp(&t1.circumradius_sq())
213                .unwrap()
214        })?;
215
216    let tri = Triangle(seed, nearest, third);
217
218    if tri.is_right_handed() {
219        Some((tri, [seed_idx, nearest_idx, third_idx]))
220    } else {
221        let tri = Triangle(seed, third, nearest);
222        Some((tri, [seed_idx, third_idx, nearest_idx]))
223    }
224}
225
226/// Delaunay triangulation
227pub struct Delaunay {
228    pub dcel: TrianglesDCEL,
229    hull: Hull,
230    stack: Vec<usize>,
231}
232
233impl Delaunay {
234    /// Triangulates a set of given points, if it is possible.
235    pub fn new(points: &[Point]) -> Option<Delaunay> {
236        let (seed, seed_indices) = find_seed_triangle(points)?;
237        let seed_circumcenter = seed.circumcenter();
238
239        let mut indices = (0..points.len())
240            .filter(|&i| i != seed_indices[0] && i != seed_indices[1] && i != seed_indices[2])
241            .collect::<Vec<_>>();
242
243        let cmp = |&a: &usize, &b: &usize| {
244            points[a]
245                .distance_sq(seed_circumcenter)
246                .partial_cmp(&points[b].distance_sq(seed_circumcenter))
247                .unwrap()
248        };
249
250        #[cfg(feature = "rayon")]
251        indices.par_sort_by(cmp);
252
253        #[cfg(not(feature = "rayon"))]
254        indices.sort_by(cmp);
255
256        let max_triangles = 2 * points.len() - 3 - 2;
257
258        let mut delaunay = Delaunay {
259            dcel: TrianglesDCEL::with_capacity(max_triangles),
260            hull: Hull::new(seed_indices, points),
261            stack: Vec::with_capacity(STACK_CAPACITY),
262        };
263
264        delaunay.dcel.add_triangle(seed_indices);
265
266        let mut prev_point: Option<Point> = None;
267
268        for &i in &indices {
269            let point = points[i];
270
271            if let Some(p) = prev_point {
272                if p.approx_eq(point) {
273                    continue;
274                }
275            }
276
277            delaunay.add_point(i, points);
278            prev_point = Some(point);
279        }
280
281        Some(delaunay)
282    }
283
284    fn add_point(&mut self, index: usize, points: &[Point]) {
285        let point = points[index];
286
287        let (mut start, should_walk_back) = match self.hull.find_visible_edge(point, points) {
288            Some(v) => v,
289            None => return,
290        };
291
292        let mut end = self.hull.next[start];
293
294        let t = self.add_triangle(
295            [start, index, end],
296            [
297                OptionIndex::none(),
298                OptionIndex::none(),
299                self.hull.triangles[start],
300            ],
301        );
302
303        self.hull.triangles[index] = OptionIndex::some(self.legalize(t + 2, points));
304        self.hull.triangles[start] = OptionIndex::some(t);
305
306        loop {
307            let next = self.hull.next[end];
308            let tri = Triangle(point, points[next], points[end]);
309            if !tri.is_right_handed() {
310                break;
311            }
312
313            let t = self.add_triangle(
314                [end, index, next],
315                [
316                    self.hull.triangles[index],
317                    OptionIndex::none(),
318                    self.hull.triangles[end],
319                ],
320            );
321
322            self.hull.triangles[index] = OptionIndex::some(self.legalize(t + 2, points));
323            self.hull.next[end] = end;
324            end = next;
325        }
326
327        if should_walk_back {
328            loop {
329                let prev = self.hull.prev[start];
330                let tri = Triangle(point, points[start], points[prev]);
331                if !tri.is_right_handed() {
332                    break;
333                }
334
335                let t = self.add_triangle(
336                    [prev, index, start],
337                    [
338                        OptionIndex::none(),
339                        self.hull.triangles[start],
340                        self.hull.triangles[prev],
341                    ],
342                );
343
344                self.legalize(t + 2, points);
345
346                self.hull.triangles[prev] = OptionIndex::some(t);
347                self.hull.next[start] = start;
348                start = prev;
349            }
350        }
351
352        self.hull.start = start;
353        self.hull.next[start] = index;
354        self.hull.next[index] = end;
355
356        self.hull.prev[end] = index;
357        self.hull.prev[index] = start;
358
359        self.hull.add_hash(index, point);
360        self.hull.add_hash(start, points[start]);
361    }
362
363    fn add_triangle(&mut self, vertices: [usize; 3], halfedges: [OptionIndex; 3]) -> usize {
364        let t = self.dcel.add_triangle(vertices);
365
366        for (i, &halfedge) in halfedges.iter().enumerate() {
367            if let Some(e) = halfedge.get() {
368                self.dcel.link(t + i, e);
369            }
370        }
371
372        t
373    }
374
375    fn legalize(&mut self, index: usize, points: &[Point]) -> usize {
376        self.stack.push(index);
377
378        let mut output = 0;
379
380        while let Some(a) = self.stack.pop() {
381            let ar = self.dcel.prev_edge(a);
382            output = ar;
383
384            let b = match self.dcel.twin(a) {
385                Some(v) => v,
386                None => continue,
387            };
388
389            let br = self.dcel.next_edge(b);
390            let bl = self.dcel.prev_edge(b);
391
392            /* if the pair of triangles doesn't satisfy the Delaunay condition
393             * (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
394             * then do the same check/flip recursively for the new pair of triangles
395             *
396             *           pl                    pl
397             *          /||\                  /  \
398             *       al/ || \bl            al/    \a
399             *        /  ||  \              /      \
400             *       /  a||b  \    flip    /___ar___\
401             *     p0\   ||   /p1   =>   p0\---bl---/p1
402             *        \  ||  /              \      /
403             *       ar\ || /br             b\    /br
404             *          \||/                  \  /
405             *           pr                    pr
406             */
407
408            let [p0, pr, pl] = self.dcel.triangle_points(ar);
409            let p1 = self.dcel.triangle_points(bl)[0];
410
411            let illegal = Triangle(points[p0], points[pr], points[pl]).in_circumcircle(points[p1]);
412
413            if !illegal {
414                continue;
415            }
416
417            self.dcel.vertices[a] = p1;
418            self.dcel.vertices[b] = p0;
419
420            let hbl = self.dcel.twin(bl);
421
422            self.dcel.link_option(a, hbl);
423            self.dcel.link_option(b, self.dcel.twin(ar));
424            self.dcel.link(ar, bl);
425
426            if hbl.is_none() {
427                let mut edge = self.hull.start;
428
429                loop {
430                    if self.hull.triangles[edge] == OptionIndex::some(bl) {
431                        self.hull.triangles[edge] = OptionIndex::some(a);
432                        break;
433                    }
434
435                    edge = self.hull.next[edge];
436
437                    if edge == self.hull.start || edge == self.hull.next[edge] {
438                        break;
439                    }
440                }
441            }
442
443            if self.stack.len() >= STACK_CAPACITY - 1 {
444                continue;
445            }
446
447            self.stack.push(br);
448            self.stack.push(a);
449        }
450
451        output
452    }
453}