poly2tri_rs/
sweeper.rs

1use crate::advancing_front::{AdvancingFront, NodeId, NodeRef};
2use crate::points::{Points, PointsBuilder};
3use crate::triangles::TriangleId;
4use crate::triangles::TriangleStore;
5use crate::utils::{in_circle, in_scan_area, orient_2d, Angle, Orientation};
6use crate::{shape::*, Context, PointId, Triangle};
7
8/// Observer for sweeper, used to monitor how sweeper works, quite useful
9/// for visual debugging when things goes wrong. Check example's draw.
10#[allow(unused_variables)]
11pub trait Observer {
12    /// A point_event processed
13    fn enter_point_event(&mut self, point_id: PointId, context: &Context) {}
14    fn exit_point_event(&mut self, point_id: PointId, context: &Context) {}
15
16    /// An edge event processed
17    fn edge_event(&mut self, edge: Edge, context: &Context) {}
18
19    /// Sweep process done
20    fn sweep_done(&mut self, context: &Context) {}
21
22    /// The result finalized, holes, fake points etc cleaned.
23    fn finalized(&mut self, context: &Context) {}
24
25    /// About to legalize for triangle
26    #[inline]
27    fn will_legalize(&mut self, triangle_id: TriangleId, context: &Context) {}
28
29    /// A single step inside one legalization process
30    #[inline]
31    fn legalize_step(&mut self, triangle_id: TriangleId, context: &Context) {}
32
33    /// A rotate happened
34    #[inline]
35    fn triangle_rotated(
36        &mut self,
37        triangle_id: TriangleId,
38        opposite_triangle_id: TriangleId,
39        context: &Context,
40    ) {
41    }
42
43    /// The triangle legalized
44    #[inline]
45    fn legalized(&mut self, triangel_id: TriangleId, context: &Context) {}
46}
47
48/// Default dummy observer, blank impl, so all calls should be optimized out by compiler.
49impl Observer for () {}
50
51/// Sweeper Builder
52///
53/// # Example
54/// ```rust
55///    use poly2tri_rs::{SweeperBuilder, Point};
56///
57///    let builder = SweeperBuilder::new(vec![
58///        Point::new(-10., -10.),
59///        Point::new(810., -10.),
60///        Point::new(810., 810.),
61///        Point::new(-10., 810.),
62///    ]).add_steiner_points(vec![
63///        Point::new(50., 50.),
64///    ]).add_hole(vec![
65///        Point::new(400., 400.),
66///        Point::new(600., 400.),
67///        Point::new(600., 600.),
68///        Point::new(400., 600.),
69///    ]);
70///    let sweeper = builder.build();
71/// ```
72
73#[derive(Clone)]
74pub struct SweeperBuilder {
75    points_builder: PointsBuilder,
76}
77
78impl SweeperBuilder {
79    /// Create a new Builder with polyline
80    /// There should be only one polyline, and multiple holes and steiner points supported
81    pub fn new(polyline: Vec<Point>) -> Self {
82        let mut points_builder = PointsBuilder::with_capacity(polyline.len());
83        parse_polyline(polyline, &mut points_builder);
84
85        Self { points_builder }
86    }
87
88    /// Add a single sparse `Point`, there is no edge attached to it
89    /// NOTE: if the point locates outside of polyline, then it has no
90    /// effect on the final result
91    pub fn add_steiner_point(mut self, point: Point) -> Self {
92        self.points_builder.add_steiner_point(point);
93        self
94    }
95
96    /// Add multiple [`Point`], batch version for `Self::add_point`
97    pub fn add_steiner_points(mut self, points: impl IntoIterator<Item = Point>) -> Self {
98        let _ = self.points_builder.add_steiner_points(points);
99        self
100    }
101
102    /// Add a hole defined by polyline.
103    pub fn add_hole(mut self, polyline: Vec<Point>) -> Self {
104        parse_polyline(polyline, &mut self.points_builder);
105        self
106    }
107
108    /// Add holes
109    pub fn add_holes(mut self, holes: impl IntoIterator<Item = Vec<Point>>) -> Self {
110        for polyline in holes.into_iter() {
111            self = self.add_hole(polyline);
112        }
113        self
114    }
115
116    /// build the sweeper
117    pub fn build(self) -> Sweeper {
118        let points = self.points_builder.build();
119        Sweeper { points }
120    }
121}
122
123/// Main interface, user should grab a new Sweeper by [`SweeperBuilder::build`]
124#[derive(Clone)]
125pub struct Sweeper {
126    points: Points,
127}
128
129/// The result of triangulate
130pub struct Triangles {
131    /// points store, it includes all points, including ones in hole
132    points: Points,
133    /// including all triangles, including ones in hole
134    triangles: TriangleStore,
135    /// final result `TriangleId`s
136    result: Vec<TriangleId>,
137
138    /// iterator next cursor
139    next: usize,
140}
141
142impl Iterator for Triangles {
143    type Item = Triangle;
144
145    fn next(&mut self) -> Option<Self::Item> {
146        if self.next < self.result.len() {
147            let index = self.next;
148            self.next += 1;
149
150            // safety: just checked index less than len
151            let tri_id = unsafe { self.result.get_unchecked(index) };
152            let triangle = tri_id.get(&self.triangles);
153
154            return Some(Triangle {
155                points: [
156                    triangle.points[0].get(&self.points),
157                    triangle.points[1].get(&self.points),
158                    triangle.points[2].get(&self.points),
159                ],
160            });
161        } else {
162            None
163        }
164    }
165}
166
167impl Sweeper {
168    /// Run trianglate with dummy observer
169    pub fn triangulate(self) -> Triangles {
170        self.triangulate_with_observer(&mut ())
171    }
172
173    /// Run triangulate with observer
174    pub fn triangulate_with_observer(self, observer: &mut impl Observer) -> Triangles {
175        let mut triangles = TriangleStore::with_capacity(self.points.len() * 3);
176
177        let initial_triangle = triangles.insert(InnerTriangle::new(
178            self.points.get_id_by_y(0).unwrap(),
179            self.points.head,
180            self.points.tail,
181        ));
182
183        // create the advancing front with initial triangle
184        let mut advancing_front = AdvancingFront::new(
185            triangles.get(initial_triangle).unwrap(),
186            initial_triangle,
187            &self.points,
188        );
189
190        let mut context = Context::new(&self.points, &mut triangles, &mut advancing_front);
191
192        Self::sweep_points(&mut context, observer);
193        observer.sweep_done(&context);
194
195        Self::finalize_polygon(&mut context);
196        observer.finalized(&context);
197
198        // take result out of context
199        let result = context.result;
200
201        Triangles {
202            points: self.points,
203            triangles,
204            result,
205
206            next: 0,
207        }
208    }
209}
210
211impl Sweeper {
212    fn sweep_points(context: &mut Context, observer: &mut impl Observer) {
213        for (point_id, point, edges) in context.points.iter_point_by_y(1) {
214            observer.enter_point_event(point_id, context);
215            Self::point_event(point_id, point, context, observer);
216            observer.exit_point_event(point_id, context);
217
218            for p in edges {
219                let edge = Edge { p, q: point_id };
220                Self::edge_event(edge, point, context, observer);
221
222                observer.edge_event(edge, context);
223            }
224
225            debug_assert!(Self::verify_triangles(context));
226        }
227    }
228
229    fn finalize_polygon(context: &mut Context) -> Option<()> {
230        // get an internal triangle to start with
231        // the first node is head, artificial point, so skip
232        let node = context.advancing_front.nth(1)?;
233
234        let mut t = node.triangle?;
235
236        loop {
237            if let Some(tri) = context.triangles.get(t) {
238                if !tri.constrained_edge_cw(node.point_id()) {
239                    t = tri.neighbor_ccw(node.point_id());
240                } else {
241                    break;
242                }
243            } else {
244                // no valid triangle found, just break
245                break;
246            }
247        }
248
249        if !t.invalid() {
250            Self::clean_mesh(t, context);
251        }
252
253        Some(())
254    }
255
256    fn clean_mesh(triangle_id: TriangleId, context: &mut Context) -> Option<()> {
257        // id and from, it should not trigger from again
258        let mut triangles = Vec::<(TriangleId, TriangleId)>::with_capacity(context.points.len());
259        triangles.push((triangle_id, TriangleId::INVALID));
260
261        while let Some((t, from)) = triangles.pop() {
262            if t.invalid() {
263                continue;
264            }
265
266            let tri = context.triangles.get_mut(t).unwrap();
267
268            if !tri.interior {
269                tri.interior = true;
270                context.result.push(t);
271
272                for i in 0..3 {
273                    if !tri.is_constrained(i) {
274                        let new_t = tri.neighbors[i];
275                        if new_t != from {
276                            triangles.push((new_t, t));
277                        }
278                    }
279                }
280            }
281        }
282
283        Some(())
284    }
285}
286
287// first need to visualize each step, then trouble shoot
288// print detailed steps, like what changes this going to address.
289
290/// Point event related methods
291impl Sweeper {
292    fn point_event(
293        point_id: PointId,
294        point: Point,
295        context: &mut Context,
296        observer: &mut impl Observer,
297    ) {
298        let node = context.advancing_front.locate_node(point).unwrap();
299        let node_id = node.get_node_id();
300        let next_node = node.next().unwrap();
301        let node_point = node.point();
302
303        let triangle = context.triangles.insert(InnerTriangle::new(
304            point_id,
305            node.point_id(),
306            next_node.point_id(),
307        ));
308        let node_triangle = node.triangle.unwrap();
309        context.triangles.mark_neighbor(node_triangle, triangle);
310        context.advancing_front.insert(point_id, point, triangle);
311
312        Self::legalize(triangle, context, observer);
313
314        // in middle case, the node's x should be less than point'x
315        // in left case, they are same.
316        if point.x <= node_point.x + f64::EPSILON {
317            Self::fill_one(node_id, context, observer);
318        }
319
320        Self::fill_advancing_front(point, context, observer);
321    }
322
323    /// helper function to check wether triangle is legal
324    fn is_legalize(triangle_id: TriangleId, context: &Context) -> [TriangleId; 3] {
325        let mut result = [TriangleId::INVALID; 3];
326        for point_idx in 0..3 {
327            let triangle = context.triangles.get_unchecked(triangle_id);
328            let opposite_triangle_id = triangle.neighbors[point_idx];
329            let Some(opposite_triangle) = context.triangles.get(opposite_triangle_id) else {
330                continue;
331            };
332
333            let p = triangle.points[point_idx];
334            let op = opposite_triangle.opposite_point(&triangle, p);
335            let oi = opposite_triangle.point_index(op).unwrap();
336
337            if opposite_triangle.is_constrained(oi) {
338                continue;
339            }
340
341            let inside = unsafe {
342                in_circle(
343                    context.points.get_point_uncheck(p),
344                    context.points.get_point_uncheck(triangle.point_ccw(p)),
345                    context.points.get_point_uncheck(triangle.point_cw(p)),
346                    context.points.get_point_uncheck(op),
347                )
348            };
349
350            if inside {
351                result[point_idx] = opposite_triangle_id;
352            }
353        }
354
355        result
356    }
357
358    /// legalize the triangle
359    fn legalize(triangle_id: TriangleId, context: &mut Context, observer: &mut impl Observer) {
360        observer.will_legalize(triangle_id, context);
361
362        // keeps record of all touched triangles, after legalize finished
363        // need to remap all to the advancing front
364        let mut legalized_triangles = std::mem::take(&mut context.legalize_remap_tids);
365
366        // record the task and who triggered it
367        let mut task_queue = std::mem::take(&mut context.legalize_task_queue);
368        task_queue.push(triangle_id);
369        legalized_triangles.push(triangle_id);
370
371        while let Some(triangle_id) = task_queue.pop() {
372            for point_idx in 0..3 {
373                let triangle = triangle_id.get(&context.triangles);
374                // skip legalize for constrained_edge
375                if triangle.is_constrained(point_idx) || triangle.is_delaunay(point_idx) {
376                    continue;
377                }
378
379                let opposite_triangle_id = triangle.neighbors[point_idx];
380                if opposite_triangle_id.invalid() {
381                    continue;
382                };
383                let opposite_triangle = opposite_triangle_id.get(&context.triangles);
384
385                let p = triangle.points[point_idx];
386                let op = opposite_triangle.opposite_point(&triangle, p);
387
388                let illegal = in_circle(
389                    p.get(&context.points),
390                    triangle.point_ccw(p).get(&context.points),
391                    triangle.point_cw(p).get(&context.points),
392                    op.get(&context.points),
393                );
394                if illegal {
395                    observer.triangle_rotated(triangle_id, opposite_triangle_id, context);
396                    // rotate shared edge one vertex cw to legalize it
397                    let need_remap = Self::rotate_triangle_pair(
398                        triangle_id,
399                        p,
400                        opposite_triangle_id,
401                        op,
402                        context.triangles,
403                    );
404
405                    // set the delaunay flag for the edge we just fixed
406                    {
407                        let (t, ot) = unsafe {
408                            context
409                                .triangles
410                                .get_mut_two(triangle_id, opposite_triangle_id)
411                        };
412
413                        let (t_idx, ot_idx) = t.common_edge_index(ot).unwrap();
414                        t.set_delaunay(t_idx, true);
415                        ot.set_delaunay(ot_idx, true);
416                    }
417
418                    task_queue.push(triangle_id);
419                    task_queue.push(opposite_triangle_id);
420
421                    if need_remap {
422                        legalized_triangles.push(triangle_id);
423                        legalized_triangles.push(opposite_triangle_id);
424                    }
425                    break;
426                } else {
427                    // though we can set delaunay edge to prevent future recalulate
428                    // it turns out slower, it means the recalculation is not many
429                }
430            }
431
432            observer.legalize_step(triangle_id, context);
433        }
434
435        for triangle_id in legalized_triangles.drain(..) {
436            Self::map_triangle_to_nodes(triangle_id, context);
437        }
438
439        {
440            // give back the task queue
441            context.legalize_task_queue = task_queue;
442            context.legalize_remap_tids = legalized_triangles;
443        }
444
445        observer.legalized(triangle_id, context);
446    }
447
448    /// Rotate the triangle pair, returns two flag indicate (t, ot) whether candidate for af remap
449    fn rotate_triangle_pair(
450        t_id: TriangleId,
451        p: PointId,
452        ot_id: TriangleId,
453        op: PointId,
454        triangles: &mut TriangleStore,
455    ) -> bool {
456        let (t, ot) = unsafe { triangles.get_mut_two(t_id, ot_id) };
457
458        let n1 = t.neighbor_ccw(p);
459        let n2 = t.neighbor_cw(p);
460        let n3 = ot.neighbor_ccw(op);
461        let n4 = ot.neighbor_cw(op);
462
463        let ea1 = t.edge_attr_ccw(p);
464        let ea2 = t.edge_attr_cw(p);
465        let ea3 = ot.edge_attr_ccw(op);
466        let ea4 = ot.edge_attr_cw(op);
467
468        // rotate shared edge one vertex cw to legalize it
469        t.rotate_cw(p, op);
470        ot.rotate_cw(op, p);
471
472        t.set_edge_attr_cw(p, ea2);
473        t.set_edge_attr_ccw(op, ea3);
474        ot.set_edge_attr_ccw(p, ea1);
475        ot.set_edge_attr_cw(op, ea4);
476
477        t.clear_neighbors();
478        ot.clear_neighbors();
479
480        TriangleStore::mark_neighbor_for_two_mut(t_id, ot_id, t, ot);
481
482        let (t, ot, t_n1, t_n2, t_n3, t_n4) =
483            unsafe { triangles.get_mut_six(t_id, ot_id, n1, n2, n3, n4) };
484
485        if let Some(t_n2) = t_n2 {
486            TriangleStore::mark_neighbor_for_two_mut(t_id, n2, t, t_n2);
487        }
488        if let Some(t_n3) = t_n3 {
489            TriangleStore::mark_neighbor_for_two_mut(t_id, n3, t, t_n3);
490        }
491        if let Some(t_n1) = t_n1 {
492            TriangleStore::mark_neighbor_for_two_mut(ot_id, n1, ot, t_n1);
493        }
494        if let Some(t_n4) = t_n4 {
495            TriangleStore::mark_neighbor_for_two_mut(ot_id, n4, ot, t_n4);
496        }
497
498        n1.invalid() || n2.invalid() || n3.invalid() || n4.invalid()
499    }
500
501    /// update advancing front node's triangle
502    fn map_triangle_to_nodes(triangle_id: TriangleId, context: &mut Context) {
503        let triangle = triangle_id.get(&context.triangles);
504        for i in 0..3 {
505            if triangle.neighbors[i].invalid() {
506                let point = unsafe {
507                    context
508                        .points
509                        .get_point_uncheck(triangle.point_cw(triangle.points[i]))
510                };
511                context.advancing_front.update_triangle(point, triangle_id);
512            }
513        }
514    }
515
516    /// fill the node with one triangle.
517    /// Note: The moment it filled, advancing_front is modified.
518    /// if the node is covered by another triangle, then it is deleted from advancing_front.
519    /// all following advancing front lookup is affected.
520    /// Returns the new next's point info.
521    fn fill_one(
522        node: NodeId,
523        context: &mut Context,
524        observer: &mut impl Observer,
525    ) -> Option<FillOne> {
526        let node = context.advancing_front.get_node_with_id(node).unwrap();
527        let prev_node = node.prev()?;
528        let next_node = node.next()?;
529
530        // After fill and legalize, next's node won't change. So we save a version here
531        // why: Most of time, after fill, external code needs to query the new
532        //      next/prev and check whether more work needs to do. The checking logic
533        //      only requires point info.
534        let fill_one_result = FillOne {
535            prev: prev_node.get_node_id(),
536            next: next_node.get_node_id(),
537        };
538
539        let new_triangle = context.triangles.insert(InnerTriangle::new(
540            prev_node.point_id(),
541            node.point_id(),
542            next_node.point_id(),
543        ));
544
545        context
546            .triangles
547            .mark_neighbor(new_triangle, prev_node.triangle.unwrap());
548        context
549            .triangles
550            .mark_neighbor(new_triangle, node.triangle.unwrap());
551
552        // update prev_node's triangle to newly created and delete the node.
553        // node is covered by new triangle.
554        // safety: prev_node and node is valid till this point, advanceing_front can not changed
555        //       under the hood, so the index is still valid
556        unsafe {
557            context.advancing_front.update_and_delete_by_index(
558                prev_node.index(),
559                prev_node.point_id(),
560                new_triangle,
561                node.index(),
562            )
563        };
564
565        // legalize works on existing triangles, no new triangle will be created
566        // that ganrentees next point won't change
567        Self::legalize(new_triangle, context, observer);
568
569        Some(fill_one_result)
570    }
571
572    fn fill_advancing_front(
573        node_point: Point,
574        context: &mut Context,
575        observer: &mut impl Observer,
576    ) {
577        let node_id = context
578            .advancing_front
579            .get_node(node_point)
580            .unwrap()
581            .get_node_id();
582
583        {
584            // fill right holes
585            let mut node_id = node_id.clone();
586            while let Some(next_node) = context.advancing_front.locate_next_node(node_id) {
587                if next_node.next().is_some() {
588                    // if HoleAngle exceeds 90 degrees then break
589                    if Self::should_fill(&next_node) {
590                        break;
591                    }
592                    let next_node_id = next_node.get_node_id();
593
594                    node_id = match Self::fill_one(next_node_id, context, observer) {
595                        Some(fill_one) => fill_one.next,
596                        None => next_node_id,
597                    };
598                } else {
599                    break;
600                }
601            }
602        }
603
604        {
605            // fill left holes
606            let mut node_id = node_id.clone();
607
608            while let Some(prev_node) = context.advancing_front.locate_prev_node(node_id) {
609                if prev_node.prev().is_some() {
610                    // if HoleAngle exceeds 90 degrees then break
611                    if Self::should_fill(&prev_node) {
612                        break;
613                    }
614
615                    node_id = prev_node.get_node_id();
616                    Self::fill_one(node_id, context, observer);
617                } else {
618                    break;
619                }
620            }
621        }
622
623        // fill right basins
624        if Self::basin_angle_satisfy(node_id, context) {
625            Self::fill_basin(node_id, context, observer);
626        }
627    }
628
629    fn should_fill(node: &NodeRef) -> bool {
630        let next = node.next().unwrap();
631        let prev_node = node.prev().unwrap();
632
633        let angle = crate::utils::Angle::new(node.point(), next.point(), prev_node.point());
634
635        if angle.is_negative() {
636            // negative means next -> node -> prev is cw, then it is not a hole
637            return true;
638        } else if !angle.exceeds_90_degree() {
639            // we only fill holes with angle less than PI/2
640            // otherwise we generate many bad shaped triangles
641            // that needs rotated later
642            return false;
643        }
644
645        if let Some(next_next) = next.next() {
646            if Angle::new(node.point(), next_next.point(), prev_node.point())
647                .between_0_to_90_degree()
648            {
649                return false;
650            }
651        }
652
653        if let Some(prev_prev) = prev_node.prev() {
654            if Angle::new(node.point(), next.point(), prev_prev.point()).between_0_to_90_degree() {
655                return false;
656            }
657        }
658
659        true
660    }
661}
662
663struct FillOne {
664    prev: NodeId,
665    next: NodeId,
666}
667
668#[derive(Debug)]
669struct ConstrainedEdge {
670    constrained_edge: Edge,
671    p: Point,
672    q: Point,
673    /// Whether the constrained edge is "right" edge, p.x larger than q.x
674    right: bool,
675}
676
677impl ConstrainedEdge {
678    fn p_id(&self) -> PointId {
679        self.constrained_edge.p
680    }
681
682    fn q_id(&self) -> PointId {
683        self.constrained_edge.q
684    }
685
686    fn with_q(&self, q: PointId, context: &Context) -> Self {
687        let q_point = q.get(&context.points);
688        Self {
689            constrained_edge: Edge {
690                p: self.constrained_edge.p,
691                q,
692            },
693            p: self.p,
694            q: q_point,
695            right: self.p.x > q_point.x,
696        }
697    }
698}
699
700/// EdgeEvent related methods
701impl Sweeper {
702    fn edge_event(edge: Edge, q: Point, context: &mut Context, observer: &mut impl Observer) {
703        let p = edge.p.get(&context.points);
704
705        let constrain_edge = ConstrainedEdge {
706            constrained_edge: edge,
707            p,
708            q,
709            right: p.x > q.x,
710        };
711
712        {
713            // check and fill
714            let node = context.advancing_front.get_node_with_cache(q).unwrap();
715
716            let triangle_id = node.triangle.unwrap();
717            let node_id = node.get_node_id();
718            if Self::try_mark_edge_for_triangle(edge.p, edge.q, triangle_id, context) {
719                // the edge is already an edge of the triangle, return
720                return;
721            }
722
723            // for now we will do all needed filling
724            Self::fill_edge_event(&constrain_edge, node_id, context, observer);
725        }
726
727        // node's triangle may changed, get the latest
728        let triangle = context
729            .advancing_front
730            .get_node_with_cache(q)
731            .unwrap()
732            .triangle
733            .unwrap();
734
735        // this triangle crosses constraint so let's flippin start!
736        let mut triangle_ids = std::mem::take(&mut context.triangle_id_queue);
737        Self::edge_event_process(
738            edge.p,
739            edge.q,
740            &constrain_edge,
741            triangle,
742            edge.q,
743            &mut triangle_ids,
744            context,
745        );
746
747        for triangle_id in triangle_ids.drain(..) {
748            Self::legalize(triangle_id, context, observer);
749        }
750        context.triangle_id_queue = triangle_ids;
751    }
752
753    /// try mark edge for triangle if the constrained edge already is a edge
754    /// returns `true` if yes, otherwise `false`
755    fn try_mark_edge_for_triangle(
756        p: PointId,
757        q: PointId,
758        t_id: TriangleId,
759        context: &mut Context,
760    ) -> bool {
761        let triangle = context.triangles.get_mut_unchecked(t_id);
762        let Some(index) = triangle.edge_index(p, q) else { return false; };
763
764        triangle.set_constrained(index, true);
765        let neighbor_t_id = triangle.neighbors[index];
766        if !neighbor_t_id.invalid() {
767            let ot = context.triangles.get_mut_unchecked(neighbor_t_id);
768            let index = ot.neighbor_index(t_id);
769            ot.set_constrained(index, true);
770        }
771
772        true
773    }
774
775    fn fill_edge_event(
776        edge: &ConstrainedEdge,
777        node_id: NodeId,
778        context: &mut Context,
779        observer: &mut impl Observer,
780    ) {
781        if edge.right {
782            Self::fill_right_above_edge_event(edge, node_id, context, observer);
783        } else {
784            Self::fill_left_above_edge_event(edge, node_id, context, observer);
785        }
786    }
787
788    fn fill_right_above_edge_event(
789        edge: &ConstrainedEdge,
790        node_id: NodeId,
791        context: &mut Context,
792        observer: &mut impl Observer,
793    ) {
794        let mut node_id = node_id.clone();
795        while let Some(next_node) = context.advancing_front.locate_next_node(node_id) {
796            if next_node.point().x >= edge.p.x {
797                break;
798            }
799
800            // check if next node is below the edge
801            if orient_2d(edge.q, next_node.point(), edge.p).is_ccw() {
802                Self::fill_right_below_edge_event(edge, node_id, context, observer);
803            } else {
804                // try next node
805                node_id = next_node.get_node_id();
806            }
807        }
808    }
809
810    fn fill_right_below_edge_event(
811        edge: &ConstrainedEdge,
812        node_id: NodeId,
813        context: &mut Context,
814        observer: &mut impl Observer,
815    ) -> Option<()> {
816        if node_id.point().x >= edge.p.x {
817            return None;
818        }
819
820        let node = context.advancing_front.get_node_with_id(node_id).unwrap();
821
822        let next_node = node.next().unwrap();
823        let next_next_node = next_node.next().unwrap();
824
825        if orient_2d(node.point(), next_node.point(), next_next_node.point()).is_ccw() {
826            // concave
827            Self::fill_right_concave_edge_event(edge, node_id, context, observer);
828        } else {
829            // convex
830            Self::fill_right_convex_edge_event(edge, node_id, context, observer)?;
831
832            // retry this one
833            Self::fill_right_below_edge_event(edge, node_id, context, observer);
834        }
835
836        Some(())
837    }
838
839    /// recursively fill concave nodes
840    fn fill_right_concave_edge_event(
841        edge: &ConstrainedEdge,
842        node_id: NodeId,
843        context: &mut Context,
844        observer: &mut impl Observer,
845    ) {
846        let next_id = {
847            let next_node = context.advancing_front.locate_next_node(node_id).unwrap();
848            let next_id = next_node.get_node_id();
849            match Self::fill_one(next_id, context, observer) {
850                None => {
851                    // nothing changed
852                    next_id
853                }
854                Some(fill_one) => fill_one.next,
855            }
856        };
857
858        if next_id.point_id() != edge.p_id() {
859            // next above or below edge?
860            if orient_2d(edge.q, next_id.point(), edge.p).is_ccw() {
861                let next_next_node = context.advancing_front.locate_next_node(next_id).unwrap();
862
863                //  below
864                if orient_2d(node_id.point(), next_id.point(), next_next_node.point()).is_ccw() {
865                    // next is concave
866                    Self::fill_right_concave_edge_event(edge, node_id, context, observer);
867                } else {
868                    // next is convex
869                }
870            }
871        }
872    }
873
874    // if nothing changed, returns None
875    #[must_use]
876    fn fill_right_convex_edge_event(
877        edge: &ConstrainedEdge,
878        node_id: NodeId,
879        context: &mut Context,
880        observer: &mut impl Observer,
881    ) -> Option<()> {
882        let next_node = context.advancing_front.locate_next_node(node_id).unwrap();
883        let next_next_node = next_node.next().unwrap();
884        let next_next_next_node = next_next_node.next()?;
885        // next concave or convex?
886        if orient_2d(
887            next_node.point(),
888            next_next_node.point(),
889            next_next_next_node.point(),
890        )
891        .is_ccw()
892        {
893            // concave
894            Self::fill_right_concave_edge_event(edge, node_id, context, observer);
895        } else {
896            // convex
897            // next above or below edge?
898            if orient_2d(edge.q, next_next_node.point(), edge.p).is_ccw() {
899                // Below
900                Self::fill_right_convex_edge_event(
901                    edge,
902                    next_node.get_node_id(),
903                    context,
904                    observer,
905                )?;
906            } else {
907                // Above
908            }
909        }
910
911        Some(())
912    }
913
914    fn fill_left_above_edge_event(
915        edge: &ConstrainedEdge,
916        node_id: NodeId,
917        context: &mut Context,
918        observer: &mut impl Observer,
919    ) {
920        let mut node_id = node_id.clone();
921        while let Some(prev_node) = context.advancing_front.locate_prev_node(node_id) {
922            // check if next node is below the edge
923            if prev_node.point().x <= edge.p.x {
924                break;
925            }
926
927            if orient_2d(edge.q, prev_node.point(), edge.p).is_cw() {
928                Self::fill_left_below_edge_event(edge, node_id, context, observer);
929            } else {
930                node_id = prev_node.get_node_id();
931            }
932        }
933    }
934
935    fn fill_left_below_edge_event(
936        edge: &ConstrainedEdge,
937        node_id: NodeId,
938        context: &mut Context,
939        observer: &mut impl Observer,
940    ) -> Option<()> {
941        if node_id.point().x > edge.p.x {
942            let prev_node = context.advancing_front.locate_prev_node(node_id).unwrap();
943            let prev_prev_node = prev_node.prev().unwrap();
944            if orient_2d(node_id.point(), prev_node.point(), prev_prev_node.point()).is_cw() {
945                Self::fill_left_concave_edge_event(edge, node_id, context, observer);
946            } else {
947                // convex
948                Self::fill_left_convex_edge_event(edge, node_id, context, observer)?;
949
950                // retry this one
951                Self::fill_left_below_edge_event(edge, node_id, context, observer);
952            }
953            Some(())
954        } else {
955            None
956        }
957    }
958
959    // if nothing changed, returns None
960    #[must_use]
961    fn fill_left_convex_edge_event(
962        edge: &ConstrainedEdge,
963        node_id: NodeId,
964        context: &mut Context,
965        observer: &mut impl Observer,
966    ) -> Option<()> {
967        // next concave or convex?
968        let prev_node = context.advancing_front.locate_prev_node(node_id).unwrap();
969        let prev_prev_node = prev_node.prev().unwrap();
970        let prev_prev_prev_node = prev_prev_node.prev()?;
971
972        if orient_2d(
973            prev_node.point(),
974            prev_prev_node.point(),
975            prev_prev_prev_node.point(),
976        )
977        .is_cw()
978        {
979            // concave
980            Self::fill_left_concave_edge_event(edge, prev_node.get_node_id(), context, observer);
981        } else {
982            // convex
983            // next above or below edge?
984            if orient_2d(edge.q, prev_prev_node.point(), edge.p).is_cw() {
985                // below
986                Self::fill_left_convex_edge_event(
987                    edge,
988                    prev_node.get_node_id(),
989                    context,
990                    observer,
991                )?;
992            } else {
993                // above
994            }
995        }
996        Some(())
997    }
998
999    fn fill_left_concave_edge_event(
1000        edge: &ConstrainedEdge,
1001        node_id: NodeId,
1002        context: &mut Context,
1003        observer: &mut impl Observer,
1004    ) {
1005        let prev_node = context.advancing_front.locate_prev_node(node_id).unwrap();
1006
1007        let prev_node_id = prev_node.get_node_id();
1008
1009        let prev_node_id = match Self::fill_one(prev_node_id, context, observer) {
1010            Some(fill_one) => fill_one.prev,
1011            None => prev_node_id,
1012        };
1013
1014        if prev_node_id.point_id() != edge.p_id() {
1015            // next above or below edge?
1016            if orient_2d(edge.q, prev_node_id.point(), edge.p).is_cw() {
1017                let prev_node = context
1018                    .advancing_front
1019                    .get_node_with_id(prev_node_id)
1020                    .unwrap();
1021                // below
1022                let prev_prev_node = prev_node.prev().unwrap();
1023                if orient_2d(node_id.point(), prev_node.point(), prev_prev_node.point()).is_cw() {
1024                    // next is concave
1025                    Self::fill_left_concave_edge_event(edge, node_id, context, observer);
1026                } else {
1027                    // next is convex
1028                }
1029            }
1030        }
1031    }
1032
1033    fn edge_event_process(
1034        ep: PointId,
1035        eq: PointId,
1036        constrain_edge: &ConstrainedEdge,
1037        triangle_id: TriangleId,
1038        p: PointId,
1039        triangle_ids: &mut Vec<TriangleId>,
1040        context: &mut Context,
1041    ) {
1042        assert!(!triangle_id.invalid());
1043
1044        if Self::try_mark_edge_for_triangle(ep, eq, triangle_id, context) {
1045            return;
1046        }
1047
1048        let triangle = context.triangles.get_mut_unchecked(triangle_id);
1049        let p1 = triangle.point_ccw(p);
1050        let o1 = orient_2d(
1051            eq.get(&context.points),
1052            p1.get(&context.points),
1053            ep.get(&context.points),
1054        );
1055
1056        if o1.is_collinear() {
1057            if let Some(edge_index) = triangle.edge_index(eq, p1) {
1058                triangle.set_constrained(edge_index, true);
1059
1060                let neighbor_across_t = triangle.neighbor_across(p);
1061                Self::edge_event_process(
1062                    ep,
1063                    p1,
1064                    &constrain_edge.with_q(p1, context),
1065                    neighbor_across_t,
1066                    p1,
1067                    triangle_ids,
1068                    context,
1069                );
1070                return;
1071            } else {
1072                panic!("EdgeEvent - collinear points not supported")
1073            }
1074        }
1075
1076        let p2 = triangle.point_cw(p);
1077        let o2 = orient_2d(
1078            eq.get(&context.points),
1079            p2.get(&context.points),
1080            ep.get(&context.points),
1081        );
1082        if o2.is_collinear() {
1083            if let Some(edge_index) = triangle.edge_index(eq, p2) {
1084                triangle.set_constrained(edge_index, true);
1085
1086                let neighbor_across_t = triangle.neighbor_across(p);
1087                Self::edge_event_process(
1088                    ep,
1089                    p2,
1090                    &constrain_edge.with_q(p2, context),
1091                    neighbor_across_t,
1092                    p2,
1093                    triangle_ids,
1094                    context,
1095                );
1096
1097                return;
1098            } else {
1099                panic!("collinear points not supported");
1100            }
1101        }
1102
1103        if o1 == o2 {
1104            // need to decide if we are rotating cw or ccw to get to a triangle
1105            // that will cross edge
1106            let triangle_id = if o1.is_cw() {
1107                triangle.neighbor_ccw(p)
1108            } else {
1109                triangle.neighbor_cw(p)
1110            };
1111
1112            Self::edge_event_process(
1113                ep,
1114                eq,
1115                constrain_edge,
1116                triangle_id,
1117                p,
1118                triangle_ids,
1119                context,
1120            );
1121        } else {
1122            Self::flip_edge_event(
1123                ep,
1124                eq,
1125                constrain_edge,
1126                triangle_id,
1127                p,
1128                triangle_ids,
1129                context,
1130            );
1131        }
1132    }
1133}
1134
1135/// flip edge related methods
1136impl Sweeper {
1137    fn flip_edge_event(
1138        ep: PointId,
1139        eq: PointId,
1140        edge: &ConstrainedEdge,
1141        triangle_id: TriangleId,
1142        p: PointId,
1143        legalize_queue: &mut Vec<TriangleId>,
1144        context: &mut Context,
1145    ) {
1146        let t = triangle_id.get(&context.triangles);
1147        let ot_id = t.neighbor_across(p);
1148        let ot = ot_id.get(&context.triangles);
1149        let op = ot.opposite_point(t, p);
1150
1151        if in_scan_area(
1152            p.get(&context.points),
1153            t.point_ccw(p).get(&context.points),
1154            t.point_cw(p).get(&context.points),
1155            op.get(&context.points),
1156        ) {
1157            // lets rotate shared edge one vertex cw
1158            if Self::rotate_triangle_pair(triangle_id, p, ot_id, op, &mut context.triangles) {
1159                Self::map_triangle_to_nodes(triangle_id, context);
1160                Self::map_triangle_to_nodes(ot_id, context);
1161            }
1162            // legalize later
1163            legalize_queue.extend([triangle_id, ot_id]);
1164
1165            if p == eq && op == ep {
1166                if eq == edge.q_id() && ep == edge.p_id() {
1167                    context
1168                        .triangles
1169                        .get_mut_unchecked(triangle_id)
1170                        .set_constrained_for_edge(ep, eq);
1171
1172                    context
1173                        .triangles
1174                        .get_mut_unchecked(ot_id)
1175                        .set_constrained_for_edge(ep, eq);
1176                }
1177            } else {
1178                let o = orient_2d(
1179                    eq.get(&context.points),
1180                    op.get(&context.points),
1181                    ep.get(&context.points),
1182                );
1183
1184                let t = Self::next_flip_triangle(o, triangle_id, ot_id, legalize_queue);
1185                Self::flip_edge_event(ep, eq, edge, t, p, legalize_queue, context);
1186            }
1187        } else {
1188            let new_p = Self::next_flip_point(ep, eq, ot_id, op, context);
1189            Self::flip_scan_edge_event(
1190                ep,
1191                eq,
1192                edge,
1193                triangle_id,
1194                ot_id,
1195                new_p,
1196                legalize_queue,
1197                context,
1198            );
1199            Self::edge_event_process(ep, eq, edge, triangle_id, p, legalize_queue, context);
1200        }
1201    }
1202
1203    fn next_flip_triangle(
1204        o: Orientation,
1205        t: TriangleId,
1206        ot: TriangleId,
1207        triangle_ids: &mut Vec<TriangleId>,
1208    ) -> TriangleId {
1209        if o.is_ccw() {
1210            // ot is not crossing edge after flip
1211            triangle_ids.push(ot);
1212            t
1213        } else {
1214            // t is not crossing edge after flip
1215            triangle_ids.push(t);
1216            ot
1217        }
1218    }
1219
1220    fn next_flip_point(
1221        ep: PointId,
1222        eq: PointId,
1223        ot: TriangleId,
1224        op: PointId,
1225        context: &mut Context,
1226    ) -> PointId {
1227        let o2d = orient_2d(
1228            eq.get(&context.points),
1229            op.get(&context.points),
1230            ep.get(&context.points),
1231        );
1232
1233        let ot = context.triangles.get_unchecked(ot);
1234        match o2d {
1235            Orientation::CW => {
1236                // right
1237                ot.point_ccw(op)
1238            }
1239            Orientation::CCW => {
1240                // left
1241                ot.point_cw(op)
1242            }
1243            Orientation::Collinear => {
1244                panic!("Opposing point on constrained edge");
1245            }
1246        }
1247    }
1248
1249    fn flip_scan_edge_event(
1250        ep: PointId,
1251        eq: PointId,
1252        edge: &ConstrainedEdge,
1253        flip_triangle_id: TriangleId,
1254        t_id: TriangleId,
1255        p: PointId,
1256        triangle_ids: &mut Vec<TriangleId>,
1257        context: &mut Context,
1258    ) {
1259        let t = t_id.get(&context.triangles);
1260        let ot = t.neighbor_across(p);
1261        if ot.invalid() {
1262            panic!("flip_scan_edge_event - null neighbor across");
1263        }
1264
1265        let op = ot.get(&context.triangles).opposite_point(t, p);
1266        let flip_triangle = flip_triangle_id.get(&context.triangles);
1267        let p1 = flip_triangle.point_ccw(eq);
1268        let p2 = flip_triangle.point_cw(eq);
1269
1270        if in_scan_area(
1271            eq.get(&context.points),
1272            p1.get(&context.points),
1273            p2.get(&context.points),
1274            op.get(&context.points),
1275        ) {
1276            // flip with new edge op -> eq
1277            Self::flip_edge_event(eq, op, edge, ot, op, triangle_ids, context);
1278
1279            // original comment:
1280            // TODO: Actually I just figured out that it should be possible to
1281            //       improve this by getting the next ot and op before the the above
1282            //       flip and continue the flipScanEdgeEvent here
1283            // set new ot and op here and loop back to inScanArea test
1284            // also need to set a new flip_triangle first
1285            // Turns out at first glance that this is somewhat complicated
1286            // so it will have to wait.
1287        } else {
1288            let new_p = Self::next_flip_point(ep, eq, ot, op, context);
1289            Self::flip_scan_edge_event(
1290                ep,
1291                eq,
1292                edge,
1293                flip_triangle_id,
1294                ot,
1295                new_p,
1296                triangle_ids,
1297                context,
1298            );
1299        }
1300    }
1301}
1302
1303#[derive(Debug)]
1304struct Basin {
1305    left: Point,
1306    right: Point,
1307    width: f64,
1308    left_higher: bool,
1309}
1310
1311impl Basin {
1312    pub fn is_shallow(&self, point: Point) -> bool {
1313        let height = if self.left_higher {
1314            self.left.y - point.y
1315        } else {
1316            self.right.y - point.y
1317        };
1318
1319        self.width > height
1320    }
1321
1322    pub fn completed(&self, point: Point) -> bool {
1323        if point.x >= self.right.x || point.x <= self.left.x {
1324            return true;
1325        }
1326
1327        self.is_shallow(point)
1328    }
1329}
1330
1331/// Basin related methods
1332impl Sweeper {
1333    fn basin_angle_satisfy(node_id: NodeId, context: &Context) -> bool {
1334        const TAN_3_4_PI: f64 = -1.;
1335        let Some(next) = context.advancing_front.locate_next_node(node_id) else { return false };
1336        let Some(next_next) = next.next() else { return false };
1337
1338        let ax = node_id.point().x - next_next.point().x;
1339        let ay = node_id.point().y - next_next.point().y;
1340        // the basin angle is (1/2pi, pi), so as long as tan value is less than 3/4 pi's, then its angle is less than 3/4 pi
1341
1342        // ay / ax < tan(3/4 * PI)
1343        if ax > 0. {
1344            ay < TAN_3_4_PI * ax
1345        } else {
1346            ay > TAN_3_4_PI * ax
1347        }
1348    }
1349
1350    /// basin is like a bowl, we first identify it's left, bottom, right node.
1351    /// then fill it
1352    fn fill_basin(
1353        node_point: NodeId,
1354        context: &mut Context,
1355        observer: &mut impl Observer,
1356    ) -> Option<()> {
1357        let next_node = context.advancing_front.locate_next_node(node_point)?;
1358        let next_next_node = next_node.next()?;
1359
1360        // find the left
1361        let left: NodeRef<'_>;
1362        if orient_2d(
1363            node_point.point(),
1364            next_node.point(),
1365            next_next_node.point(),
1366        )
1367        .is_ccw()
1368        {
1369            left = next_next_node;
1370        } else {
1371            left = next_node;
1372        }
1373
1374        // find the bottom
1375        let mut bottom = left.clone();
1376        while let Some(next_node) = bottom.next() {
1377            if bottom.point().y >= next_node.point().y {
1378                bottom = next_node;
1379            } else {
1380                break;
1381            }
1382        }
1383
1384        // no valid basin
1385        if bottom.point_id().eq(&left.point_id()) {
1386            return None;
1387        }
1388
1389        // find the right
1390        let mut right: NodeRef = bottom.clone();
1391        while let Some(next_node) = right.next() {
1392            if right.point().y < next_node.point().y {
1393                right = next_node;
1394            } else {
1395                break;
1396            }
1397        }
1398        if right.point_id() == bottom.point_id() {
1399            // no valid basin
1400            return None;
1401        }
1402
1403        let width = right.point().x - left.point().x;
1404        let left_higher: bool = left.point().y > right.point().y;
1405
1406        Self::fill_basin_req(
1407            bottom.get_node_id(),
1408            &Basin {
1409                left: left.point(),
1410                right: right.point(),
1411                width,
1412                left_higher,
1413            },
1414            context,
1415            observer,
1416        );
1417
1418        Some(())
1419    }
1420
1421    fn fill_basin_req(
1422        node: NodeId,
1423        basin: &Basin,
1424        context: &mut Context,
1425        observer: &mut impl Observer,
1426    ) -> Option<()> {
1427        if basin.completed(node.point()) {
1428            return None;
1429        }
1430
1431        let fill_one = Self::fill_one(node, context, observer).expect("already in basin");
1432        let prev = fill_one.prev;
1433        let next = fill_one.next;
1434
1435        if prev.point().eq(&basin.left) && next.point().eq(&basin.right) {
1436            return Some(());
1437        }
1438
1439        let new_node = if prev.point().eq(&basin.left) {
1440            let next = context.advancing_front.get_node_with_id(next).unwrap();
1441            let next_next = next.next().unwrap();
1442            if orient_2d(node.point(), next.point(), next_next.point()).is_cw() {
1443                return None;
1444            }
1445
1446            next.get_node_id()
1447        } else if next.point().eq(&basin.right) {
1448            let prev = context.advancing_front.get_node_with_id(prev).unwrap();
1449            let prev_prev = prev.prev()?;
1450            if orient_2d(node.point(), prev.point(), prev_prev.point()).is_ccw() {
1451                return None;
1452            }
1453
1454            prev.get_node_id()
1455        } else {
1456            // continue with the neighbor node with lowest Y value
1457            if prev.point().y < next.point().y {
1458                prev
1459            } else {
1460                next
1461            }
1462        };
1463
1464        Self::fill_basin_req(new_node, basin, context, observer)
1465    }
1466}
1467
1468impl Sweeper {
1469    pub fn verify_triangles(context: &Context) -> bool {
1470        Self::illegal_triangles(context).is_empty()
1471    }
1472
1473    /// verify all triangles stored in context are legal
1474    #[allow(unused)]
1475    pub fn illegal_triangles(context: &Context) -> Vec<(TriangleId, TriangleId)> {
1476        let triangle_ids = context
1477            .triangles
1478            .iter()
1479            .map(|(t_id, _)| t_id)
1480            .collect::<Vec<_>>();
1481
1482        let mut result = Vec::<(TriangleId, TriangleId)>::new();
1483
1484        for t_id in triangle_ids {
1485            for illegal_neighbor in &Self::is_legalize(t_id, context) {
1486                if !illegal_neighbor.invalid() {
1487                    result.push((t_id, *illegal_neighbor));
1488                }
1489            }
1490        }
1491
1492        result
1493    }
1494}
1495
1496fn parse_polyline(polyline: Vec<Point>, points: &mut PointsBuilder) {
1497    // here we need to set points' edges
1498    let mut point_iter = polyline
1499        .iter()
1500        .map(|p| (points.add_steiner_point(*p), p))
1501        .collect::<Vec<_>>()
1502        .into_iter();
1503
1504    if let Some(first_point) = point_iter.next() {
1505        let mut last_point = first_point;
1506        loop {
1507            match point_iter.next() {
1508                Some(p2) => {
1509                    let edge = Edge::new(last_point, p2);
1510                    points.get_point_mut(edge.q).unwrap().edges.push(edge.p);
1511                    last_point = p2;
1512                }
1513                None => {
1514                    let edge = Edge::new(last_point, first_point);
1515                    points.get_point_mut(edge.q).unwrap().edges.push(edge.p);
1516                    break;
1517                }
1518            }
1519        }
1520    }
1521}
1522
1523#[cfg(test)]
1524mod tests {
1525    use std::io::{Read, Write};
1526
1527    use rand::Rng;
1528
1529    use super::*;
1530
1531    #[derive(Default)]
1532    struct CacheHitOb {
1533        hit_count: u64,
1534        mis_count: u64,
1535        rotate_count: u64,
1536        legalize_step_count: u64,
1537        legalize_count: u64,
1538    }
1539
1540    impl CacheHitOb {
1541        pub fn hit_rate(&self) -> f64 {
1542            self.hit_count as f64 / (self.hit_count + self.mis_count) as f64
1543        }
1544    }
1545
1546    impl Observer for CacheHitOb {
1547        fn legalized(&mut self, _triangel_id: TriangleId, _context: &Context) {
1548            self.legalize_count += 1;
1549        }
1550
1551        fn legalize_step(&mut self, _triangle_id: TriangleId, _context: &Context) {
1552            self.legalize_step_count += 1;
1553        }
1554
1555        fn triangle_rotated(
1556            &mut self,
1557            _triangle_id: TriangleId,
1558            _opposite_triangle_id: TriangleId,
1559            _context: &Context,
1560        ) {
1561            self.rotate_count += 1;
1562        }
1563
1564        fn finalized(&mut self, context: &Context) {
1565            let hit = context
1566                .advancing_front
1567                .hit_count
1568                .load(std::sync::atomic::Ordering::Relaxed);
1569            let miss = context
1570                .advancing_front
1571                .miss_count
1572                .load(std::sync::atomic::Ordering::Relaxed);
1573            println!(
1574                "af cache hit: {}/{} rate: {:.2}%",
1575                hit,
1576                hit + miss,
1577                hit as f64 / (hit + miss) as f64 * 100.
1578            );
1579
1580            println!(
1581                "points: {} triangle: {}",
1582                context.points.len(),
1583                context.triangles.len()
1584            );
1585            println!(
1586                "legalize: {} steps: {} rotate: {}",
1587                self.legalize_count, self.legalize_step_count, self.rotate_count
1588            );
1589            self.hit_count = hit;
1590            self.mis_count = miss;
1591        }
1592    }
1593
1594    #[test]
1595    fn test_bird() {
1596        let file_path = "test_data/bird.dat";
1597        let points = try_load_from_file(file_path).unwrap();
1598
1599        let mut cache_hit = CacheHitOb::default();
1600        let sweeper = SweeperBuilder::new(points).build();
1601        let triangles = sweeper
1602            .triangulate_with_observer(&mut cache_hit)
1603            .collect::<Vec<_>>();
1604        assert_eq!(triangles.len(), 273);
1605        assert!(cache_hit.hit_rate() > 0.63);
1606        assert!(cache_hit.rotate_count == 272);
1607    }
1608
1609    #[test]
1610    fn test_nazca_heron() {
1611        let file_path = "test_data/nazca_heron.dat";
1612        let points = try_load_from_file(file_path).unwrap();
1613
1614        let sweeper = SweeperBuilder::new(points).build();
1615        let mut cache_hit = CacheHitOb::default();
1616        let triangles = sweeper
1617            .triangulate_with_observer(&mut cache_hit)
1618            .collect::<Vec<_>>();
1619        assert_eq!(triangles.len(), 1034);
1620        assert!(cache_hit.hit_rate() > 0.71);
1621        assert!(cache_hit.rotate_count == 671);
1622    }
1623
1624    #[test]
1625    fn test_rand() {
1626        let test_path = "test_data/latest_test_data";
1627        let points = match try_load_from_file(test_path) {
1628            None => {
1629                let mut points = Vec::<Point>::new();
1630                for _ in 0..100 {
1631                    let x: f64 = rand::thread_rng().gen_range(0.0..800.);
1632                    let y: f64 = rand::thread_rng().gen_range(0.0..800.);
1633                    points.push(Point::new(x, y));
1634                }
1635                save_to_file(&points, test_path);
1636                points
1637            }
1638            Some(points) => points,
1639        };
1640
1641        let sweeper = SweeperBuilder::new(vec![
1642            Point::new(-10., -10.),
1643            Point::new(810., -10.),
1644            Point::new(810., 810.),
1645            Point::new(-10., 810.),
1646        ])
1647        .add_steiner_points(points)
1648        .add_hole(vec![
1649            Point::new(400., 400.),
1650            Point::new(600., 400.),
1651            Point::new(600., 600.),
1652            Point::new(400., 600.),
1653        ])
1654        .build();
1655        let _ = sweeper.triangulate();
1656
1657        delete_file(test_path);
1658    }
1659
1660    fn try_load_from_file(path: &str) -> Option<Vec<Point>> {
1661        let mut f = std::fs::File::options().read(true).open(path).ok()?;
1662        let mut value = "".to_string();
1663        f.read_to_string(&mut value).unwrap();
1664        let mut points = vec![];
1665        for line in value.lines() {
1666            let mut iter = line.split_whitespace();
1667            let x = iter.next().unwrap();
1668            let y = iter.next().unwrap();
1669
1670            let x = x.parse::<f64>().unwrap();
1671            let y = y.parse::<f64>().unwrap();
1672            points.push(Point::new(x, y));
1673        }
1674
1675        Some(points)
1676    }
1677
1678    fn save_to_file(points: &[Point], path: &str) {
1679        use std::fmt::Write;
1680
1681        let mut f = std::fs::File::options()
1682            .write(true)
1683            .create_new(true)
1684            .open(path)
1685            .unwrap();
1686
1687        let mut value = "".to_string();
1688        for p in points {
1689            writeln!(value, "{} {}", p.x, p.y).unwrap();
1690        }
1691
1692        f.write_all(value.as_bytes()).unwrap();
1693    }
1694
1695    fn delete_file(path: &str) {
1696        std::fs::remove_file(path).unwrap();
1697    }
1698}