theta_chart/utils/
delaunator.rs

1use crate::{
2    coord::{Line, Point, Vector},
3    series::{SNumber, Series},
4    TAU,
5};
6use approx::AbsDiffEq;
7use core::cmp::Ordering;
8pub const EMPTY: usize = usize::MAX;
9
10pub fn triangle(sx: Series, sy: Series) -> Triangulation {
11    let mut ax: SNumber = SNumber::new([].to_vec());
12    let mut ay: SNumber = SNumber::new([].to_vec());
13    let mut points: Vec<Point> = [].to_vec();
14    if let Series::Number(x) = sx {
15        ax = x;
16    }
17    if let Series::Number(y) = sy {
18        ay = y;
19    }
20
21    for (index, data) in ax.series().iter().enumerate() {
22        points.push(Point::new(*data, ay.series()[index]));
23    }
24    let slice = &points[..];
25
26    triangulate(slice)
27}
28
29/// Next halfedge in a triangle.
30pub fn next_halfedge(i: usize) -> usize {
31    if i % 3 == 2 {
32        i - 2
33    } else {
34        i + 1
35    }
36}
37
38/// Previous halfedge in a triangle.
39pub fn prev_halfedge(i: usize) -> usize {
40    if i % 3 == 0 {
41        i + 2
42    } else {
43        i - 1
44    }
45}
46
47/// Result of the Delaunay triangulation.
48#[derive(Debug, Clone)]
49pub struct Triangulation {
50    /// A vector of point indices where each triple represents a Delaunay triangle.
51    /// All triangles are directed counter-clockwise.
52    pub triangles: Vec<usize>,
53
54    /// A vector of adjacent halfedge indices that allows traversing the triangulation graph.
55    ///
56    /// `i`-th half-edge in the array corresponds to vertex `triangles[i]`
57    /// the half-edge is coming from. `halfedges[i]` is the index of a twin half-edge
58    /// in an adjacent triangle (or `EMPTY` for outer half-edges on the convex hull).
59    pub halfedges: Vec<usize>,
60
61    /// A vector of indices that reference points on the convex hull of the triangulation,
62    /// counter-clockwise.
63    pub hull: Vec<usize>,
64
65    /// List vertex of Voronol
66    pub vertices: Vec<Point>,
67
68    pub voronois: Vec<Vec<usize>>,
69    pub voronoi_edges: Vec<Line>,
70
71    pub tuple_triangles: Vec<Vec<usize>>,
72    pub tuple_halfedges: Vec<Vec<usize>>,
73}
74
75impl Triangulation {
76    fn new(n: usize) -> Self {
77        let max_triangles = if n > 2 { 2 * n - 5 } else { 0 };
78
79        Self {
80            triangles: Vec::with_capacity(max_triangles * 3),
81            halfedges: Vec::with_capacity(max_triangles * 3),
82            hull: Vec::new(),
83            vertices: Vec::new(),
84            tuple_triangles: Vec::new(),
85            tuple_halfedges: Vec::new(),
86            voronois: Vec::new(),
87            voronoi_edges: Vec::new(),
88        }
89    }
90
91    /// The number of triangles in the triangulation.
92    pub fn len(&self) -> usize {
93        self.triangles.len() / 3
94    }
95
96    pub fn is_empty(&self) -> bool {
97        self.triangles.is_empty()
98    }
99
100    fn add_triangle(
101        &mut self,
102        i0: usize,
103        i1: usize,
104        i2: usize,
105        a: usize,
106        b: usize,
107        c: usize,
108    ) -> usize {
109        let t = self.triangles.len();
110
111        self.triangles.push(i0);
112        self.triangles.push(i1);
113        self.triangles.push(i2);
114
115        self.halfedges.push(a);
116        self.halfedges.push(b);
117        self.halfedges.push(c);
118
119        if a != EMPTY {
120            self.halfedges[a] = t;
121        }
122        if b != EMPTY {
123            self.halfedges[b] = t + 1;
124        }
125        if c != EMPTY {
126            self.halfedges[c] = t + 2;
127        }
128
129        t
130    }
131
132    fn legalize(&mut self, a: usize, points: &[Point], hull: &mut Hull) -> usize {
133        let b = self.halfedges[a];
134
135        // if the pair of triangles doesn't satisfy the Delaunay condition
136        // (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
137        // then do the same check/flip recursively for the new pair of triangles
138        //
139        //           pl                    pl
140        //          /||\                  /  \
141        //       al/ || \bl            al/    \a
142        //        /  ||  \              /      \
143        //       /  a||b  \    flip    /___ar___\
144        //     p0\   ||   /p1   =>   p0\---bl---/p1
145        //        \  ||  /              \      /
146        //       ar\ || /br             b\    /br
147        //          \||/                  \  /
148        //           pr                    pr
149        //
150        let ar = prev_halfedge(a);
151
152        if b == EMPTY {
153            return ar;
154        }
155
156        let al = next_halfedge(a);
157        let bl = prev_halfedge(b);
158
159        let p0 = self.triangles[ar];
160        let pr = self.triangles[a];
161        let pl = self.triangles[al];
162        let p1 = self.triangles[bl];
163
164        let illegal = points[p0].in_circle(&points[pr], &points[pl], &points[p1]);
165        if illegal {
166            self.triangles[a] = p1;
167            self.triangles[b] = p0;
168
169            let hbl = self.halfedges[bl];
170            let har = self.halfedges[ar];
171
172            // edge swapped on the other side of the hull (rare); fix the halfedge reference
173            if hbl == EMPTY {
174                let mut e = hull.start;
175                loop {
176                    if hull.tri[e] == bl {
177                        hull.tri[e] = a;
178                        break;
179                    }
180                    e = hull.prev[e];
181                    if e == hull.start {
182                        break;
183                    }
184                }
185            }
186
187            self.halfedges[a] = hbl;
188            self.halfedges[b] = har;
189            self.halfedges[ar] = bl;
190
191            if hbl != EMPTY {
192                self.halfedges[hbl] = a;
193            }
194            if har != EMPTY {
195                self.halfedges[har] = b;
196            }
197            if bl != EMPTY {
198                self.halfedges[bl] = ar;
199            }
200
201            let br = next_halfedge(b);
202
203            self.legalize(a, points, hull);
204            return self.legalize(br, points, hull);
205        }
206        ar
207    }
208}
209
210// data structure for tracking the edges of the advancing convex hull
211#[derive(Debug)]
212struct Hull {
213    prev: Vec<usize>,
214    next: Vec<usize>,
215    tri: Vec<usize>,
216    hash: Vec<usize>,
217    start: usize,
218    center: Point,
219}
220
221impl Hull {
222    fn new(n: usize, center: Point, i0: usize, i1: usize, i2: usize, points: &[Point]) -> Self {
223        let hash_len = (n as f64).sqrt() as usize;
224
225        let mut hull = Self {
226            prev: vec![0; n],            // edge to prev edge
227            next: vec![0; n],            // edge to next edge
228            tri: vec![0; n],             // edge to adjacent halfedge
229            hash: vec![EMPTY; hash_len], // angular edge hash
230            start: i0,
231            center,
232        };
233
234        hull.next[i0] = i1;
235        hull.prev[i2] = i1;
236        hull.next[i1] = i2;
237        hull.prev[i0] = i2;
238        hull.next[i2] = i0;
239        hull.prev[i1] = i0;
240
241        hull.tri[i0] = 0;
242        hull.tri[i1] = 1;
243        hull.tri[i2] = 2;
244
245        hull.hash_edge(&points[i0], i0);
246        hull.hash_edge(&points[i1], i1);
247        hull.hash_edge(&points[i2], i2);
248
249        hull
250    }
251
252    fn hash_key(&self, p: &Point) -> usize {
253        let dx = p.get_x() - self.center.get_x();
254        let dy = p.get_y() - self.center.get_y();
255
256        let p = dx / (dx.abs() + dy.abs());
257
258        let a = (if dy > 0.0 { 3.0 - p } else { 1.0 + p }) / 4.0; // [0..1]
259
260        let len = self.hash.len();
261
262        (((len as f64) * a).floor() as usize) % len
263    }
264
265    fn hash_edge(&mut self, p: &Point, i: usize) {
266        let key = self.hash_key(p);
267
268        self.hash[key] = i;
269    }
270
271    fn find_visible_edge(&self, p: &Point, points: &[Point]) -> (usize, bool) {
272        let mut start: usize = 0;
273        let key = self.hash_key(p);
274        let len = self.hash.len();
275
276        for j in 0..len {
277            start = self.hash[(key + j) % len];
278            if start != EMPTY && self.next[start] != EMPTY {
279                break;
280            }
281        }
282
283        start = self.prev[start];
284
285        let mut e = start;
286
287        while p.orient(&points[e], &points[self.next[e]]) <= 0. {
288            e = self.next[e];
289            if e == start {
290                return (EMPTY, false);
291            }
292        }
293
294        (e, e == start)
295    }
296}
297
298// Find center of box(ALL POINTS)
299fn calc_bbox_center(points: &[Point]) -> Point {
300    let mut min_x = f64::INFINITY;
301    let mut min_y = f64::INFINITY;
302    let mut max_x = f64::NEG_INFINITY;
303    let mut max_y = f64::NEG_INFINITY;
304    for p in points.iter() {
305        min_x = min_x.min(p.get_x());
306        min_y = min_y.min(p.get_y());
307        max_x = max_x.max(p.get_x());
308        max_y = max_y.max(p.get_y());
309    }
310    Point::new((min_x + max_x) / 2.0, (min_y + max_y) / 2.0)
311}
312
313fn find_closest_point(points: &[Point], p0: &Point) -> Option<usize> {
314    let mut min_dist = f64::INFINITY;
315    let mut k: usize = 0;
316    for (i, p) in points.iter().enumerate() {
317        let d = p0.dist2(p);
318        if d > 0.0 && d < min_dist {
319            k = i;
320            min_dist = d;
321        }
322    }
323    if min_dist == f64::INFINITY {
324        None
325    } else {
326        Some(k)
327    }
328}
329
330fn find_seed_triangle(points: &[Point]) -> Option<(usize, usize, usize)> {
331    // pick a seed point close to the center
332    let bbox_center = calc_bbox_center(points);
333
334    let i0 = find_closest_point(points, &bbox_center)?;
335    let p0 = &points[i0];
336
337    // find the point closest to the seed
338    let i1 = find_closest_point(points, p0)?;
339
340    let p1 = &points[i1];
341    // find the third point which forms the smallest circumcircle with the first two
342    let mut min_radius = f64::INFINITY;
343    let mut i2: usize = 0;
344    for (i, p) in points.iter().enumerate() {
345        if i == i0 || i == i1 {
346            continue;
347        }
348        let r = p0.circumradius2(p1, p);
349        if r < min_radius {
350            i2 = i;
351            min_radius = r;
352        }
353    }
354
355    if min_radius == f64::INFINITY {
356        None
357    } else {
358        // swap the order of the seed points for counter-clockwise orientation
359        Some(if p0.orient(p1, &points[i2]) > 0. {
360            (i0, i2, i1)
361        } else {
362            (i0, i1, i2)
363        })
364    }
365}
366
367fn sortf(f: &mut [(usize, f64)]) {
368    f.sort_unstable_by(|&(_, da), &(_, db)| da.partial_cmp(&db).unwrap_or(Ordering::Equal));
369}
370
371/// Order collinear points by dx (or dy if all x are identical) and return the list as a hull
372fn handle_collinear_points(points: &[Point]) -> Triangulation {
373    let pf = points.first().cloned().unwrap_or_default();
374
375    let mut dist: Vec<_> = points
376        .iter()
377        .enumerate()
378        .map(|(i, p)| {
379            let mut d = p.get_x() - pf.get_x();
380            if d == 0.0 {
381                d = p.get_y() - pf.get_y();
382            }
383            (i, d)
384        })
385        .collect();
386    sortf(&mut dist);
387
388    let mut triangulation = Triangulation::new(0);
389    let mut d0 = f64::NEG_INFINITY;
390    for (i, distance) in dist {
391        if distance > d0 {
392            triangulation.hull.push(i);
393            d0 = distance;
394        }
395    }
396
397    triangulation
398}
399
400/// Triangulate a set of 2D points.
401/// Returns the triangulation for the input points.
402/// For the degenerated case when all points are collinear, returns an empty triangulation where all points are in the hull.
403pub fn triangulate(points: &[Point]) -> Triangulation {
404    let seed_triangle = find_seed_triangle(points);
405
406    if seed_triangle.is_none() {
407        return handle_collinear_points(points);
408    }
409
410    let n = points.len();
411    let (i0, i1, i2) =
412        seed_triangle.expect("At this stage, points are guaranteed to yeild a seed triangle");
413    let center = points[i0].circumcenter(&points[i1], &points[i2]);
414
415    let mut triangulation = Triangulation::new(n);
416    triangulation.add_triangle(i0, i1, i2, EMPTY, EMPTY, EMPTY);
417
418    // sort the points by distance from the seed triangle circumcenter
419    let mut dists: Vec<_> = points
420        .iter()
421        .enumerate()
422        .map(|(i, point)| (i, center.dist2(point)))
423        .collect();
424
425    sortf(&mut dists);
426
427    let mut hull = Hull::new(n, center, i0, i1, i2, points);
428
429    for (k, &(i, _)) in dists.iter().enumerate() {
430        let p = &points[i];
431
432        // skip near-duplicates
433        // if k > 0 && p.nearly_equals(&points[dists[k - 1].0]) {
434        if k > 0 && p.abs_diff_eq(&points[dists[k - 1].0], Point::default_epsilon()) {
435            continue;
436        }
437        // skip seed triangle points
438        if i == i0 || i == i1 || i == i2 {
439            continue;
440        }
441
442        // find a visible edge on the convex hull using edge hash
443        let (mut e, walk_back) = hull.find_visible_edge(p, points);
444        if e == EMPTY {
445            continue; // likely a near-duplicate point; skip it
446        }
447
448        // add the first triangle from the point
449        let t = triangulation.add_triangle(e, i, hull.next[e], EMPTY, EMPTY, hull.tri[e]);
450
451        // recursively flip triangles from the point until they satisfy the Delaunay condition
452        hull.tri[i] = triangulation.legalize(t + 2, points, &mut hull);
453        hull.tri[e] = t; // keep track of boundary triangles on the hull
454
455        // walk forward through the hull, adding more triangles and flipping recursively
456        let mut n = hull.next[e];
457        loop {
458            let q = hull.next[n];
459            if p.orient(&points[n], &points[q]) <= 0. {
460                break;
461            }
462            let t = triangulation.add_triangle(n, i, q, hull.tri[i], EMPTY, hull.tri[n]);
463            hull.tri[i] = triangulation.legalize(t + 2, points, &mut hull);
464            hull.next[n] = EMPTY; // mark as removed
465            n = q;
466        }
467
468        // walk backward from the other side, adding more triangles and flipping
469        if walk_back {
470            loop {
471                let q = hull.prev[e];
472                if p.orient(&points[q], &points[e]) <= 0. {
473                    break;
474                }
475                let t = triangulation.add_triangle(q, i, e, EMPTY, hull.tri[e], hull.tri[q]);
476                triangulation.legalize(t + 2, points, &mut hull);
477                hull.tri[q] = t;
478                hull.next[e] = EMPTY; // mark as removed
479                e = q;
480            }
481        }
482
483        // update the hull indices
484        hull.prev[i] = e;
485        hull.next[i] = n;
486        hull.prev[n] = i;
487        hull.next[e] = i;
488        hull.start = e;
489
490        // save the two new edges in the hash table
491        hull.hash_edge(p, i);
492        hull.hash_edge(&points[e], e);
493    }
494
495    // expose hull as a vector of point indices
496    let mut e = hull.start;
497    loop {
498        triangulation.hull.push(e);
499        e = hull.next[e];
500        if e == hull.start {
501            break;
502        }
503    }
504
505    triangulation.triangles.shrink_to_fit();
506    triangulation.halfedges.shrink_to_fit();
507
508    // Custom for Voronol
509    for index in (0..triangulation.triangles.len()).step_by(3) {
510        let p0 = triangulation.triangles[index];
511        let p1 = triangulation.triangles[index + 1];
512        let p2 = triangulation.triangles[index + 2];
513        triangulation.tuple_triangles.push([p0, p1, p2].to_vec());
514
515        let center = &points[p0].circumcenter(&points[p1], &points[p2]);
516        triangulation.vertices.push(center.clone());
517
518        let e1_i = triangulation.halfedges[index];
519        let e2_i = triangulation.halfedges[index + 1];
520        let e3_i = triangulation.halfedges[index + 2];
521        triangulation
522            .tuple_halfedges
523            .push([e1_i, e2_i, e3_i].to_vec());
524    }
525
526    for index in 0..points.len() {
527        if !triangulation.hull.contains(&index) {
528            let mut voronoi_all: Vec<(usize, Vec<usize>)> = Vec::new();
529            for (pos, tri) in triangulation.tuple_triangles.iter().enumerate() {
530                
531                if tri.contains(&index) {
532                    let mut new = tri.clone();
533                    if tri[1] == index {
534                        new.reverse();
535                    }
536                    new.retain(|&x| x != index);
537
538                    voronoi_all.push((pos, new));
539                }
540            }
541
542            let voronoi = sortv(voronoi_all);
543            triangulation.voronois.push(voronoi);
544        }
545    }
546
547    let mut temp = triangulation.hull.clone();
548    let f = triangulation.hull.first().unwrap();
549    temp.push(*f);
550
551    for pos in 1..temp.len() {
552        let pi0 = temp[pos - 1];
553        let pi1 = temp[pos];
554        let p0 = &points[pi0];
555        let p1 = &points[pi1];
556
557        let square = Vector::from((p0.clone(), p1.clone())).az_rotate_tau(0.25 * TAU);
558
559        for (pos, tri) in triangulation.tuple_triangles.iter().enumerate() {
560            if tri.contains(&pi0) && tri.contains(&pi1) {
561                triangulation.voronoi_edges.push(Line::new(
562                    triangulation.vertices[pos].clone(),
563                    square.clone(),
564                ))
565            }
566        }
567    }
568
569    triangulation
570}
571
572fn sortv(vec_voronoi: Vec<(usize, Vec<usize>)>) -> Vec<usize> {
573    let mut sort: Vec<(usize, Vec<usize>)> = Vec::new();
574    if vec_voronoi.len() <= 3 {
575        sort = vec_voronoi;
576    } else {
577        let mut slice = vec_voronoi.clone();
578        sort.push(slice[0].clone());
579        slice.remove(0);
580
581        while slice.len() != 0 {
582            let value = sort.last().unwrap();
583            let mut index = 0;
584            for (pos, data) in slice.iter().enumerate() {
585                if data.1[0] == value.1[1] {
586                    sort.push(data.clone());
587                    index = pos;
588                    break;
589                }
590            }
591            slice.remove(index);
592        }
593    }
594    let mut result: Vec<usize> = Vec::new();
595    for data in sort.iter() {
596        result.push(data.0);
597    }
598    result
599}