cdt/triangulate.rs
1use crate::{
2 contour::{Contour, ContourData},
3 Error, Point,
4 half::Half, hull::Hull,
5 indexes::{PointIndex, PointVec, EdgeIndex, HullIndex, EMPTY_EDGE},
6 predicates::{acute, orient2d, in_circle, centroid, distance2, pseudo_angle},
7};
8
9#[derive(Debug)]
10enum Walk {
11 Inside(EdgeIndex),
12 Done(EdgeIndex),
13}
14
15/// This `struct` contains all of the data needed to generate a (constrained)
16/// Delaunay triangulation of a set of input points and edges. It is a
17/// **low-level** API; consider using the module-level functions if you don't
18/// need total control.
19pub struct Triangulation {
20 pub(crate) points: PointVec<Point>, // Sorted in the constructor
21 angles: PointVec<f64>, // pseudo-angles for each point
22 remap: PointVec<usize>, // self.points[i] = input[self.remap[i]]
23 next: PointIndex, // Progress of the triangulation
24 constrained: bool,
25
26 // If a point p terminates fixed edges, then endings[p] will be a tuple
27 // range into ending_data containing the starting points of those edges.
28 endings: PointVec<(usize, usize)>,
29 ending_data: Vec<PointIndex>,
30
31 // This stores the start of an edge (as a pseudoangle) as an index into
32 // the edges array
33 pub(crate) hull: Hull,
34 pub(crate) half: Half,
35}
36
37impl Triangulation {
38 /// Builds a complete triangulation from the given points
39 ///
40 /// # Errors
41 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`], or
42 /// [`Error::CannotInitialize`] if the input is invalid.
43 pub fn build(points: & [Point]) -> Result<Triangulation, Error> {
44 let mut t = Self::new(points)?;
45 t.run()?;
46 Ok(t)
47 }
48
49 /// Builds a complete triangulation from the given points and edges.
50 /// The points are a flat array of positions in 2D spaces; edges are
51 /// undirected and expressed as indexes into the points list.
52 ///
53 /// # Errors
54 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`],
55 /// [`Error::InvalidEdge`], or [`Error::CannotInitialize`] if the input is
56 /// invalid.
57 pub fn build_with_edges<'a, E>(points: &[Point], edges: E)
58 -> Result<Triangulation, Error>
59 where E: IntoIterator<Item=&'a (usize, usize)> + Copy
60 {
61 let mut t = Self::new_with_edges(points, edges)?;
62 t.run()?;
63 Ok(t)
64 }
65
66 /// Builds a complete triangulation from the given points and contours
67 /// (which are represented as indexes into the points array).
68 ///
69 /// # Errors
70 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`],
71 /// [`Error::InvalidEdge`], [`Error::OpenContour`] or
72 /// [`Error::CannotInitialize`] if the input is invalid.
73 pub fn build_from_contours<V>(points: &[Point], contours: &[V])
74 -> Result<Triangulation, Error>
75 where for<'b> &'b V: IntoIterator<Item=&'b usize>
76 {
77 let mut t = Self::new_from_contours(points, contours)?;
78 t.run()?;
79 Ok(t)
80 }
81
82 fn validate_input<'a, E>(points: &[Point], edges: E)
83 -> Result<(), Error>
84 where E: IntoIterator<Item=&'a (usize, usize)> + Copy
85 {
86 if points.is_empty() {
87 Err(Error::EmptyInput)
88 } else if points.iter().any(|p| p.0.is_nan() || p.0.is_infinite() ||
89 p.1.is_nan() || p.1.is_infinite()) {
90 Err(Error::InvalidInput)
91 } else if edges.into_iter().any(|e| e.0 >= points.len() ||
92 e.1 >= points.len() ||
93 e.0 == e.1) {
94 Err(Error::InvalidEdge)
95 } else if points.len() < 3 {
96 Err(Error::TooFewPoints)
97 } else {
98 Ok(())
99 }
100 }
101
102 /// Constructs a new triangulation of the given points. The points are a
103 /// flat array of positions in 2D spaces; edges are undirected and expressed
104 /// as indexes into the `points` list.
105 ///
106 /// The triangulation is not actually run in this constructor; use
107 /// [`Triangulation::step`] or [`Triangulation::run`] to triangulate,
108 /// or [`Triangulation::build_with_edges`] to get a complete triangulation
109 /// right away.
110 ///
111 /// # Errors
112 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`],
113 /// [`Error::InvalidEdge`], or [`Error::CannotInitialize`] if the input is
114 /// invalid.
115 pub fn new_with_edges<'a, E>(points: &[Point], edges: E)
116 -> Result<Triangulation, Error>
117 where E: IntoIterator<Item=&'a (usize, usize)> + Copy
118 {
119 Self::validate_input(points, edges)?;
120
121 // Picking the seed triangle and center point is tricky!
122 //
123 // We want a center which is contained within the seed triangle,
124 // and with the property that the seed triangle is the closest
125 // three points when sorted by distance to the center.
126 //
127 // The paper suggests using the center of the bounding box, but in
128 // that case, you can end up with cases where the center is _outside_
129 // of the initial seed triangle, which is awkward.
130 //
131 // delaunator and its ports instead pick the circumcenter of a
132 // triangle near the bbox center, which has the same issue.
133 //
134 // Picking the centroid of the seed triangle instead of the
135 // circumcenter can also lead to issues, as another point could be
136 // closer, which will violate the condition that points are always
137 // outside the hull when they are added to the triangulation.
138 //
139 // We iterate, repeatedly picking a center and checking to see if the
140 // conditions hold; otherwise, we pick a new center and try again.
141
142 // Start by picking a center which is at the center of the bbox
143 let (x_bounds, y_bounds) = Self::bbox(points);
144 let mut center = ((x_bounds.0 + x_bounds.1) / 2.0,
145 (y_bounds.0 + y_bounds.1) / 2.0);
146
147 // The scratch buffer contains our points, their indexes, and a distance
148 // relative to the current center. We leave distance unpopulated
149 // because it's calculated at the beginning of the loop below.
150 let mut scratch: Vec<(usize, f64)> = (0..points.len())
151 .map(|j| (j, distance2(center, points[j])))
152 .collect();
153
154 // Find the three closest points
155 let arr = min3(&scratch, &points);
156
157 // Pick out the triangle points, ensuring that they're clockwise
158 let pa = arr[0];
159 let mut pb = arr[1];
160 let mut pc = arr[2];
161 if orient2d(points[pa], points[pb], points[pc]) < 0.0 {
162 std::mem::swap(&mut pb, &mut pc);
163 }
164
165 // Pick this triangle's centroid as our starting point
166 center = centroid(points[pa], points[pb], points[pc]);
167
168 // Sort with a special comparison function that puts the first
169 // three keys at the start of the list, and uses partial_cmp
170 // otherwise. The order of the first three keys is not
171 // guaranteed, which we fix up below.
172 scratch.sort_unstable_by(|k, r|
173 if k.0 == pa || k.0 == pb || k.0 == pc {
174 std::cmp::Ordering::Less
175 } else if r.0 == pa || r.0 == pb || r.0 == pc {
176 std::cmp::Ordering::Greater
177 } else {
178 // Compare by radius first, then break ties with pseudoangle
179 // This should be reproducible, i.e. two identical points should
180 // end up next to each other in the list, although with
181 // floating-point values, you _never know_.
182 match k.1.partial_cmp(&r.1).unwrap() {
183 std::cmp::Ordering::Equal => {
184 let pk = points[k.0];
185 let pr = points[r.0];
186 let ak = pseudo_angle((pk.0 - center.0, pk.1 - center.1));
187 let ar = pseudo_angle((pr.0 - center.0, pr.1 - center.1));
188 ak.partial_cmp(&ar).unwrap()
189 },
190 e => e,
191 }
192 });
193
194 // Sanity-check that our three target points are at the head of the
195 // list, as expected.
196 assert!((scratch[0].0 == pa) as u8 +
197 (scratch[1].0 == pa) as u8 +
198 (scratch[2].0 == pa) as u8 == 1);
199 assert!((scratch[0].0 == pb) as u8 +
200 (scratch[1].0 == pb) as u8 +
201 (scratch[2].0 == pb) as u8 == 1);
202 assert!((scratch[0].0 == pc) as u8 +
203 (scratch[1].0 == pc) as u8 +
204 (scratch[2].0 == pc) as u8 == 1);
205
206 // Apply sorting to initial three points, ignoring distance
207 // values at this point because they're unused.
208 scratch[0].0 = pa;
209 scratch[1].0 = pb;
210 scratch[2].0 = pc;
211
212 // These are the points used in the Triangulation struct
213 let mut sorted_points = PointVec::with_capacity(points.len());
214
215 // usize in original array -> PointIndex in sorted array
216 let mut map_forward = vec![PointIndex::empty(); points.len()];
217
218 // PointIndex in sorted array -> usize in original array
219 let mut map_reverse = PointVec::with_capacity(points.len());
220
221 for i in 0..scratch.len() {
222 // The first three points are guaranteed to be unique by the
223 // min3 selection function, so they have no dupe
224 let mut dupe = None;
225 let p = scratch[i];
226 if i >= 3 {
227 // Check each point against its nearest neighbor and the
228 // three original points, since they could be duplicates
229 // and may not be adjacent
230 for j in &[i - 1, 0, 1, 2] {
231 let pa = points[scratch[*j].0];
232 let pb = points[p.0];
233 if (pa.0 - pb.0).abs() < f64::EPSILON &&
234 (pa.1 - pb.1).abs() < f64::EPSILON
235 {
236 dupe = Some(scratch[*j].0);
237 break;
238 }
239 }
240 };
241 map_forward[p.0] = match dupe {
242 None => {
243 sorted_points.push(points[p.0]);
244 map_reverse.push(p.0)
245 },
246 Some(d) => {
247 assert!(map_forward[d] != PointIndex::empty());
248 map_forward[d]
249 },
250 };
251 }
252
253 ////////////////////////////////////////////////////////////////////////
254 let has_edges = edges.into_iter().count() > 0;
255 let mut out = Triangulation {
256 hull: Hull::new(sorted_points.len(), has_edges),
257 half: Half::new(sorted_points.len()),
258 constrained: has_edges,
259
260 remap: map_reverse,
261 next: PointIndex::new(0),
262 angles: PointVec::of(sorted_points.iter()
263 .map(|p| pseudo_angle((p.0 - center.0, p.1 - center.1)))
264 .collect()),
265
266 // Endings are assigned later
267 endings: PointVec::of(vec![(0,0); sorted_points.len()]),
268 ending_data: vec![],
269
270 points: sorted_points, // moved out here
271 };
272
273 let pa = out.next;
274 let pb = out.next + 1;
275 let pc = out.next + 2;
276 out.next += 3;
277 let e_ab = out.half.insert(pa, pb, pc,
278 EMPTY_EDGE, EMPTY_EDGE, EMPTY_EDGE);
279 assert!(e_ab == EdgeIndex::new(0));
280 let e_bc = out.half.next(e_ab);
281 let e_ca = out.half.prev(e_ab);
282
283 /*
284 * a
285 * / ^
286 * / \
287 * V f \
288 * b-------> c
289 */
290 out.hull.initialize(pa, out.angles[pa], e_ca);
291 out.hull.insert_bare(out.angles[pb], pb, e_ab);
292 out.hull.insert_bare(out.angles[pc], pc, e_bc);
293
294 ////////////////////////////////////////////////////////////////////////
295 // Iterate over edges, counting which points have a termination
296 let mut termination_count = PointVec::of(vec![0; out.points.len()]);
297 let edge_iter = || edges
298 .into_iter()
299 .map(|&(src, dst)| {
300 let src = map_forward[src];
301 let dst = map_forward[dst];
302 assert!(src != PointIndex::empty());
303 assert!(dst != PointIndex::empty());
304
305 if src > dst { (dst, src) } else { (src, dst) }
306 });
307 for (src, dst) in edge_iter() {
308 // Lock any edges that appear in the seed triangle. Because the
309 // (src, dst) tuple is sorted, there are only three possible
310 // matches here.
311 if (src, dst) == (pa, pb) {
312 out.half.toggle_lock_sign(e_ab);
313 } else if (src, dst) == (pa, pc) {
314 out.half.toggle_lock_sign(e_ca);
315 } else if (src, dst) == (pb, pc) {
316 out.half.toggle_lock_sign(e_bc);
317 }
318 termination_count[dst] += 1;
319 }
320 // Ending data will be tightly packed into the ending_data array; each
321 // point stores its range into that array in self.endings[pt]. If the
322 // point has no endings, then the range is (n,n) for some value n.
323 let mut cumsum = 0;
324 for (dst, t) in termination_count.iter().enumerate() {
325 out.endings[PointIndex::new(dst)] = (cumsum, cumsum);
326 cumsum += t;
327 }
328 out.ending_data.resize(cumsum, PointIndex::new(0));
329 for (src, dst) in edge_iter() {
330 let t = &mut out.endings[dst].1;
331 out.ending_data[*t] = src;
332 *t += 1;
333 }
334
335 // ...and we're done!
336 Ok(out)
337 }
338
339 /// Constructs a new unconstrained triangulation
340 ///
341 /// The triangulation is not actually run in this constructor; use
342 /// [`Triangulation::step`] or [`Triangulation::run`] to triangulate,
343 /// or [`Triangulation::build`] to get a complete triangulation right away.
344 ///
345 /// # Errors
346 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`], or
347 /// [`Error::CannotInitialize`] if the input is invalid.
348 pub fn new(points: &[Point]) -> Result<Triangulation, Error> {
349 let edges: [(usize, usize); 0] = [];
350 Self::new_with_edges(points, &edges)
351 }
352
353 /// Triangulates a set of contours, given as indexed paths into the point
354 /// list. Each contour must be closed (i.e. the last point in the contour
355 /// must equal the first point), otherwise [`Error::OpenContour`] will be
356 /// returned.
357 ///
358 /// The triangulation is not actually run in this constructor; use
359 /// [`Triangulation::step`] or [`Triangulation::run`] to triangulate,
360 /// or [`Triangulation::build_from_contours`] to get a complete
361 /// triangulation right away.
362 ///
363 /// # Errors
364 /// This may return [`Error::EmptyInput`], [`Error::InvalidInput`],
365 /// [`Error::InvalidEdge`], [`Error::OpenContour`] or
366 /// [`Error::CannotInitialize`] if the input is invalid.
367 pub fn new_from_contours<'a, V>(pts: &[Point], contours: &[V])
368 -> Result<Triangulation, Error>
369 where for<'b> &'b V: IntoIterator<Item=&'b usize>
370 {
371 let mut edges = Vec::new();
372 for c in contours {
373 let next = edges.len();
374 for (a, b) in c.into_iter().zip(c.into_iter().skip(1)) {
375 edges.push((*a, *b));
376 }
377 if let Some(start) = edges.get(next) {
378 if start.0 != edges.last().unwrap().1 {
379 return Err(Error::OpenContour);
380 }
381 }
382 }
383 Self::new_with_edges(&pts, &edges)
384 }
385
386 /// Runs the triangulation algorithm until completion
387 ///
388 /// # Errors
389 /// This may return [`Error::PointOnFixedEdge`], [`Error::NoMorePoints`],
390 /// or [`Error::CrossingFixedEdge`] if those error conditions are met.
391 pub fn run(&mut self) -> Result<(), Error> {
392 while !self.done() {
393 self.step()?;
394 }
395 Ok(())
396 }
397
398 pub(crate) fn orient2d(&self, pa: PointIndex, pb: PointIndex, pc: PointIndex) -> f64 {
399 orient2d(self.points[pa], self.points[pb], self.points[pc])
400 }
401
402 fn acute(&self, pa: PointIndex, pb: PointIndex, pc: PointIndex) -> f64 {
403 acute(self.points[pa], self.points[pb], self.points[pc])
404 }
405
406 /// Checks whether the triangulation is done
407 pub fn done(&self) -> bool {
408 self.next == self.points.len() + 1
409 }
410
411 /// Walks the upper hull, making it convex.
412 /// This should only be called once from `finalize()`.
413 fn make_outer_hull_convex(&mut self) {
414 // Walk the hull from left to right, flattening any convex regions
415 assert!(self.next == self.points.len());
416 let mut start = self.hull.start();
417 let mut hl = start;
418 let mut hr = self.hull.right_hull(hl);
419 loop {
420 /*
421 ^
422 \
423 \el/hl
424 \
425 <-------------
426 er/hr
427 */
428 let el = self.hull.edge(hl);
429 let er = self.hull.edge(hr);
430
431 let edge_l = self.half.edge(el);
432 let edge_r = self.half.edge(er);
433 assert!(edge_r.dst == edge_l.src);
434
435 // If this triangle on the hull is strictly convex, fill it
436 if self.orient2d(edge_l.dst, edge_l.src, edge_r.src) > 0.0 {
437 self.hull.erase(hr);
438 let new_edge = self.half.insert(
439 edge_r.src, edge_l.dst, edge_l.src,
440 el, er, EMPTY_EDGE);
441 self.hull.update(hl, new_edge);
442 self.legalize(self.half.next(new_edge));
443 self.legalize(self.half.prev(new_edge));
444
445 // Try stepping back in case this reveals another convex tri
446 hr = hl;
447 hl = self.hull.left_hull(hl);
448
449 // Record this as the start of the convex region
450 start = hl;
451 } else {
452 // Continue walking along the hull
453 let next = self.hull.right_hull(hr);
454 hl = hr;
455 hr = next;
456 if hl == start {
457 break;
458 }
459 }
460 }
461 }
462
463 /// Finalizes the triangulation by making the outer hull convex (in the case
464 /// of unconstrained triangulation), or removing unattached triangles (for
465 /// CDT).
466 fn finalize(&mut self) {
467 assert!(self.next == self.points.len());
468
469 if self.constrained {
470 // For a constrained triangulation, flood fill and erase triangles
471 // that are outside the shape boundaries.
472 let h = self.hull.start();
473 let e = self.hull.edge(h);
474 self.half.flood_erase_from(e);
475 } else {
476 // For an unconstrained triangulation, make the outer hull convex
477 self.make_outer_hull_convex();
478 }
479
480 self.next += 1usize;
481 }
482
483 /// Checks that invariants of the algorithm are maintained. This is a slow
484 /// operation and should only be used for debugging.
485 ///
486 /// # Panics
487 /// Panics if invariants are not correct
488 pub fn check(&self) {
489 self.hull.check();
490 self.half.check();
491 }
492
493 /// Advances the triangulation by one step.
494 ///
495 /// # Errors
496 /// This may return [`Error::PointOnFixedEdge`], [`Error::NoMorePoints`],
497 /// or [`Error::CrossingFixedEdge`] if those error conditions are met.
498 pub fn step(&mut self) -> Result<(), Error> {
499 if self.done() {
500 return Err(Error::NoMorePoints);
501 } else if self.next == self.points.len() {
502 self.finalize();
503 return Ok(());
504 }
505
506 // Pick the next point in our pre-sorted array
507 let p = self.next;
508 self.next += 1usize;
509
510 // Find the hull edge which will be split by this point
511 let h_ab = self.hull.get(self.angles[p]);
512 let e_ab = self.hull.edge(h_ab);
513
514 /*
515 * p [new point]
516 * / ^
517 * / \
518 * V f \
519 * --------> [new edge]
520 * b<------a [previous hull edge]
521 * e
522 */
523 let edge = self.half.edge(e_ab);
524 let a = edge.src;
525 let b = edge.dst;
526 assert!(edge.next != EMPTY_EDGE);
527 assert!(edge.prev != EMPTY_EDGE);
528 assert!(edge.buddy == EMPTY_EDGE);
529
530 assert!(a != b);
531 assert!(a != p);
532 assert!(b != p);
533
534 let o = self.orient2d(b, a, p);
535 let h_p = if o <= 0.0 {
536 /*
537 b<-------p<------a
538 \ ^| ^
539 \ | /
540 next \ | / prev
541 \ | /
542 \ | /
543 \ |v/
544 V |/
545 c
546
547 Special case: if p is exactly on the line (or inside), then we
548 split the line instead of inserting a new triangle.
549 */
550 if edge.fixed() {
551 // TODO: this should only be checked if o == 0.0; otherwise,
552 // we should re-insert a-b with a-b-p being a third triangle
553 return Err(Error::PointOnFixedEdge(self.remap[p]));
554 }
555 assert!(edge.buddy == EMPTY_EDGE);
556 let edge_bc = self.half.edge(edge.next);
557 let edge_ca = self.half.edge(edge.prev);
558 let c = edge_bc.dst;
559 assert!(c == edge_ca.src);
560
561 let hull_right = self.hull.right_hull(h_ab);
562 let hull_left = self.hull.left_hull(h_ab);
563
564 self.half.erase(e_ab);
565
566 let e_pc = self.half.insert(p, c, a, edge_ca.buddy, EMPTY_EDGE, EMPTY_EDGE);
567 let e_cp = self.half.insert(c, p, b, EMPTY_EDGE, edge_bc.buddy, e_pc);
568
569 // Update the hull point at b to point to the new split edge
570 self.hull.update(h_ab, self.half.next(e_cp));
571
572 // Split the edge in the hull
573 let h_ap = self.hull.insert(
574 h_ab, self.angles[p], p, self.half.prev(e_pc));
575
576 // If either of the other triangle edges (in the now-deleted
577 // triangle) were attached to the hull, then patch them up.
578 if self.hull.edge(hull_right) == edge.prev {
579 self.hull.update(hull_right, self.half.next(e_pc));
580 }
581 if self.hull.edge(hull_left) == edge.next {
582 self.hull.update(hull_left, self.half.prev(e_cp));
583 }
584
585 self.legalize(self.half.prev(e_cp));
586 self.legalize(self.half.next(e_pc));
587 h_ap
588 } else {
589 let f = self.half.insert(b, a, p, EMPTY_EDGE, EMPTY_EDGE, e_ab);
590 assert!(o != 0.0);
591 assert!(o > 0.0);
592
593 // Replaces the previous item in the hull
594 self.hull.update(h_ab, self.half.prev(f));
595
596 let h_p = if self.angles[a] != self.angles[p] {
597 // Insert the new edge into the hull, using the previous
598 // HullIndex as a hint to avoid searching for its position.
599 let h_ap = self.hull.insert(
600 h_ab, self.angles[p], p, self.half.next(f));
601 self.legalize(f);
602 h_ap
603 } else {
604 /* Rare case when p and a are in a perfect vertical line:
605 *
606 * We already inserted the left triangle and attached p-b to
607 * the hull index. We insert a bonus right triangle here and
608 * attach c-p to to p's hull index, rather than splitting a-b
609 * in the hull.
610 *
611 * /p [new point]
612 * / | ^
613 * / | \
614 * V f | g \
615 * -------->------>\
616 * b<------a<------c [previous hull edge]
617 * e
618 */
619 let h_ca = self.hull.right_hull(h_ab);
620 let e_ca = self.hull.edge(h_ca);
621 let edge_ca = self.half.edge(e_ca);
622 assert!(a == edge_ca.dst);
623 let c = edge_ca.src;
624 let g = self.half.insert(a, c, p,
625 EMPTY_EDGE, self.half.next(f), e_ca);
626
627 // h_ca has the same X position as c-p, so we update the same
628 // slot in the hull, then move the point in the look-up table.
629 self.hull.update(h_ca, self.half.next(g));
630 self.hull.move_point(a, p);
631
632 // Legalize the two new triangle edges
633 self.legalize(f);
634 self.legalize(g);
635 h_ca
636 };
637
638 // Check and fill acute angles
639 self.check_acute_left(p, h_p);
640 self.check_acute_right(p, h_p);
641 h_p
642 };
643
644 // Finally, we check whether this point terminates any edges that are
645 // locked in the triangulation (the "constrainted" part of Constrained
646 // Delaunay Triangulation).
647 let (start, end) = self.endings[p];
648 for i in start..end {
649 self.handle_fixed_edge(h_p, p, self.ending_data[i])?;
650 }
651
652 Ok(())
653 }
654
655 fn check_acute_left(&mut self, p: PointIndex, h_p: HullIndex) {
656 /* Search for sharp angles on the left side.
657 *
658 * q p [new point]
659 * / ^ e/ ^
660 * / \ / \
661 * / \ V \
662 * b------->
663 */
664 let mut h_b = h_p;
665 loop {
666 // Move one edge to the left. In the first iteration of the loop,
667 // h_b will be pointing at the b->p edge.
668 h_b = self.hull.left_hull(h_b);
669 let e_pb = self.hull.edge(h_b);
670 let edge_pb = self.half.edge(e_pb);
671 let b = edge_pb.dst;
672
673 // Pick out the next item in the list
674 let h_q = self.hull.left_hull(h_b);
675 let e_bq = self.hull.edge(h_q);
676 let edge_bq = self.half.edge(e_bq);
677 let q = edge_bq.dst;
678
679 // If we're building a constrained triangulation, then we force the
680 // outer hull to be convex, so each point-to-point connection
681 // is guaranteed to stay within the triangulation. This is slightly
682 // less efficient than the acute check, but dramatically simplifies
683 // the code for fixing edges.
684 //
685 // For unconstrained triangulations, we check that the inner angle
686 // is less that pi/2, per Zalik '05.
687 if (!self.constrained && self.acute(p, b, q) <= 0.0) ||
688 self.orient2d(p, b, q) >= 0.0
689 {
690 break;
691 }
692
693 // Friendship ended with q-b-p
694 self.hull.erase(h_b);
695
696 // Now p-q is my new friend
697 let e_pq = self.half.insert(p, q, b, e_bq, e_pb, EMPTY_EDGE);
698 self.hull.update(h_q, e_pq);
699 h_b = h_p;
700
701 // Then legalize from the two new triangle edges (bp and qb)
702 self.legalize(self.half.next(e_pq));
703 self.legalize(self.half.prev(e_pq));
704 }
705 }
706
707 fn check_acute_right(&mut self, p: PointIndex, h_p: HullIndex) {
708 /* Rightward equivalent of check_acute_left
709 * p q
710 * / ^ / \
711 * / \e / \
712 * V \ v \
713 * -------->a \
714 */
715 let mut h_a = h_p;
716 loop {
717 // Move one edge to the left. In the first iteration of the loop,
718 // h_a will be pointing at the p->a edge.
719 let e_ap = self.hull.edge(h_a);
720 let edge_ap = self.half.edge(e_ap);
721 let a = edge_ap.src;
722 assert!(a != p);
723
724 // Scoot over by one to look at the a-q edge
725 h_a = self.hull.right_hull(h_a);
726 let e_qa = self.hull.edge(h_a);
727 let edge_qa = self.half.edge(e_qa);
728 let q = edge_qa.src;
729
730 // Same check as above
731 if (!self.constrained && self.acute(p, a, q) <= 0.0) ||
732 self.orient2d(p, a, q) <= 0.0
733 {
734 break;
735 }
736
737 self.hull.erase(h_a);
738 let edge_qp = self.half.insert(q, p, a, e_ap, e_qa, EMPTY_EDGE);
739 self.hull.update(h_p, edge_qp);
740 h_a = h_p;
741
742 // Then legalize from the two new triangle edges (bp and qb)
743 self.legalize(self.half.next(edge_qp));
744 self.legalize(self.half.prev(edge_qp));
745 }
746 }
747
748 /// Finds which mode to begin walking through the triangulation when
749 /// inserting a fixed edge. h is a [`HullIndex`] equivalent to the `src`
750 /// point, and `dst` is the destination of the new fixed edge.
751 fn find_hull_walk_mode(&self, h: HullIndex, src: PointIndex, dst: PointIndex)
752 -> Result<Walk, Error> {
753 /* We've just built a triangle that contains a fixed edge, and need
754 to walk through the triangulation and implement that edge.
755
756 The only thing we know going in is that point src is on the hull of
757 the triangulation with HullIndex h.
758
759 We start by finding the triangle a->src->b which contains the edge
760 src->dst, e.g.
761
762 src
763 / :^
764 / : \
765 / : \h
766 / : \
767 V : \
768 b---:------->a
769 :
770 dst
771
772 Because the triangulation is convex, we know that this triangle
773 exists; we can't escape the triangulation.
774 */
775 let e_right = self.hull.edge(h);
776 let h_left = self.hull.left_hull(h);
777 let e_left = self.hull.edge(h_left);
778
779 // Note that e_right-e_left may be a wedge that contains multiple
780 // triangles (for example, this would be the case if there was an edge
781 // flip of b->a)
782 let wedge_left = self.half.edge(e_left).dst;
783 let wedge_right = self.half.edge(e_right).src;
784
785 // If the fixed edge is directly attached to src, then we can declare
786 // that we're done right away (and the caller will lock the edge)
787 if dst == wedge_left {
788 return Ok(Walk::Done(e_left));
789 } else if dst == wedge_right {
790 return Ok(Walk::Done(e_right));
791 }
792
793 // Otherwise, check the winding to see which side we're on.
794 let o_left = self.orient2d(src, wedge_left, dst);
795 let o_right = self.orient2d(src, dst, wedge_right);
796
797 // For now, we don't handle cases where fixed edges have coincident
798 // points that are not the start/end of the fixed edge.
799 if o_left == 0.0 {
800 return Err(Error::PointOnFixedEdge(self.remap[wedge_left]));
801 } else if o_right == 0.0 {
802 return Err(Error::PointOnFixedEdge(self.remap[wedge_right]));
803 }
804
805 // Walk the inside of the wedge until we find the
806 // subtriangle which captures the p-src line.
807 let mut index_a_src = self.half.edge(e_left).prev;
808
809 loop {
810 let edge_a_src = self.half.edge(index_a_src);
811 let a = edge_a_src.src;
812 if a == dst {
813 /* Lucky break: the src point is one of the edges directly
814 within the wedge, e.g.:
815 src
816 / ^\
817 / | \
818 / | \
819 / | \
820 V | \
821 ------>a------ (a == dst)
822 */
823 return Ok(Walk::Done(index_a_src));
824 }
825
826 // Keep walking through the fan
827 let intersected_index = edge_a_src.prev;
828
829 let o = self.orient2d(src, dst, a);
830 // If we've found the intersection point, then we return the new
831 // (inner) edge. The walking loop will transition to Left or Right
832 // if this edge doesn't have a buddy.
833 if o > 0.0 {
834 /*
835 src
836 /:^\
837 / :| \
838 / :| \
839 / : | \
840 V : | \
841 ----:->a------
842 :
843 dst
844 */
845 // We may exit either into another interior triangle or
846 // leave the triangulation and walk the hull, but we don't
847 // need to decide that right now.
848 return Ok(Walk::Inside(intersected_index));
849 } else if o < 0.0 {
850 /* Sorry, Mario; your src-dst line is in another triangle
851
852 src
853 / ^:\
854 / |: \
855 / | : \
856 / | : \
857 V | : \
858 ------>a--:---
859 :
860 dst
861
862 (so keep going through the triangle)
863 */
864 let buddy = edge_a_src.buddy;
865
866 // We can't have walked out of the wedge, because otherwise
867 // o_right would be < 0.0 and we wouldn't be in this branch
868 if buddy == EMPTY_EDGE {
869 return Err(Error::WedgeEscape);
870 }
871 index_a_src = self.half.edge(buddy).prev;
872 } else {
873 // If we hit a vertex, exactly, then return an error
874 return Err(Error::PointOnFixedEdge(self.remap[a]));
875 }
876 }
877 }
878
879 fn walk_fill(&mut self, src: PointIndex, dst: PointIndex, mut e: EdgeIndex) -> Result<(), Error> {
880 let mut steps_left = Contour::new_pos(src, ContourData::None);
881 let mut steps_right = Contour::new_neg(src, ContourData::None);
882
883 /*
884 * We start inside a triangle, then escape it right away:
885 src
886 / :^
887 / : \
888 hl/ : \hr
889 / : \
890 V : e \
891 b---:------->a
892 :
893 dst
894 */
895 let edge_ba = self.half.edge(e);
896 let e_ac = edge_ba.next;
897 let e_cb = edge_ba.prev;
898 let edge_ac = self.half.edge(e_ac);
899 let edge_cb = self.half.edge(e_cb);
900
901 // Delete this triangle from the triangulation; it will be
902 // reconstructed later in a more perfect form.
903 self.half.erase(e);
904
905 steps_left.push(self, edge_ba.src,
906 if edge_cb.buddy != EMPTY_EDGE {
907 ContourData::Buddy(edge_cb.buddy)
908 } else {
909 let hl = self.hull.index_of(edge_cb.dst);
910 assert!(self.hull.edge(hl) == e_cb);
911 ContourData::Hull(hl, edge_cb.sign)
912 });
913 steps_right.push(self, edge_ba.dst,
914 if edge_ac.buddy != EMPTY_EDGE {
915 ContourData::Buddy(edge_ac.buddy)
916 } else {
917 let hr = self.hull.index_of(edge_ac.dst);
918 assert!(self.hull.edge(hr) == e_ac);
919 ContourData::Hull(hr, edge_ac.sign)
920 });
921
922 // Exit this triangle, either onto the hull or continuing inside
923 // the triangulation.
924 if edge_ba.fixed() {
925 return Err(Error::CrossingFixedEdge);
926 }
927 assert!(edge_ba.buddy != EMPTY_EDGE);
928 e = edge_ba.buddy;
929
930 loop {
931 /* src
932 :
933 b<--:-------a
934 \ : e ^
935 :\ /
936 : v /
937 : c
938 dst
939 */
940 let edge_ab = self.half.edge(e);
941 let e_bc = edge_ab.next;
942 let e_ca = edge_ab.prev;
943 let edge_bc = self.half.edge(e_bc);
944 let edge_ca = self.half.edge(e_ca);
945 let c = edge_bc.dst;
946
947 // Erase this triangle from the triangulation before
948 // pushing vertices to the contours, which could create
949 // new triangles. At this point, you're not allowed to use
950 // self.half for any of the triangle edges, which is why
951 // we stored them all above.
952 self.half.erase(e);
953
954 // Handle the termination case, if c is the destination
955 if c == dst {
956 // The left (above) contour is either on the hull
957 // (if no buddy is present) or inside the triangulation
958 let e_dst_src = steps_left.push(self, c,
959 if edge_bc.buddy == EMPTY_EDGE {
960 let h = self.hull.index_of(edge_bc.dst);
961 assert!(self.hull.edge(h) == e_bc);
962 ContourData::Hull(h, edge_bc.sign)
963 } else {
964 ContourData::Buddy(edge_bc.buddy)
965 }).expect("Failed to create fixed edge");
966
967 // This better have terminated the triangulation of
968 // the upper contour with a dst-src edge
969 assert!(self.half.edge(e_dst_src).dst == src);
970 assert!(self.half.edge(e_dst_src).src == dst);
971
972 // The other contour will finish up with the other
973 // half of the fixed edge as its buddy. This edge
974 // could also be on the hull, so we do the same check
975 // as above.
976 let e_src_dst = steps_right.push(self, c,
977 if edge_ca.buddy == EMPTY_EDGE {
978 let h = self.hull.index_of(edge_ca.dst);
979 assert!(self.hull.edge(h) == e_ca);
980 ContourData::Hull(h, edge_ca.sign)
981 } else {
982 ContourData::Buddy(edge_ca.buddy)
983 })
984 .expect("Failed to create second fixed edge");
985
986 // Similarly, this better have terminated the
987 // triangulation of the lower contour.
988 assert!(self.half.edge(e_src_dst).src == src);
989 assert!(self.half.edge(e_src_dst).dst == dst);
990
991 self.half.link(e_src_dst, e_dst_src);
992 self.half.toggle_lock_sign(e_src_dst); // locks both sides
993
994 break;
995 }
996
997 let o_psc = self.orient2d(src, dst, c);
998 e = if o_psc > 0.0 {
999 // Store the c-a edge as our buddy, and exit via b-c
1000 // (unless c-a is the 0th edge, which has no buddy)
1001 steps_right.push(self, c,
1002 if edge_ca.buddy == EMPTY_EDGE {
1003 let h = self.hull.index_of(edge_ca.dst);
1004 assert!(self.hull.edge(h) == e_ca);
1005 ContourData::Hull(h, edge_ca.sign)
1006 } else {
1007 ContourData::Buddy(edge_ca.buddy)
1008 });
1009
1010 // Exit the triangle, either onto the hull or staying
1011 // in the triangulation
1012 if edge_bc.fixed() {
1013 return Err(Error::CrossingFixedEdge);
1014 }
1015 assert!(edge_bc.buddy != EMPTY_EDGE);
1016 edge_bc.buddy
1017 } else if o_psc < 0.0 {
1018 /* src
1019 :
1020 b<-- :-a
1021 | : ^
1022 | :/
1023 | :/
1024 | :
1025 V/:
1026 c dst
1027 */
1028 // Store the b-c edge as our buddy and exit via c-a,
1029 //
1030 // (c-b may be a hull edge, so we check for that)
1031 steps_left.push(self, c,
1032 if edge_bc.buddy == EMPTY_EDGE {
1033 let h = self.hull.index_of(edge_bc.dst);
1034 assert!(self.hull.edge(h) == e_bc);
1035 ContourData::Hull(h, edge_bc.sign)
1036 } else {
1037 ContourData::Buddy(edge_bc.buddy)
1038 });
1039
1040 if edge_ca.fixed() {
1041 return Err(Error::CrossingFixedEdge);
1042 }
1043 assert!(edge_ca.buddy != EMPTY_EDGE);
1044 edge_ca.buddy
1045 } else {
1046 return Err(Error::PointOnFixedEdge(self.remap[c]));
1047 }
1048 }
1049 Ok(())
1050 }
1051
1052 fn handle_fixed_edge(&mut self, h: HullIndex, src: PointIndex, dst: PointIndex) -> Result<(), Error> {
1053 match self.find_hull_walk_mode(h, src, dst)? {
1054 // Easy mode: the fixed edge is directly connected to the new
1055 // point, so we lock it and return immediately.
1056 Walk::Done(e) => { self.half.toggle_lock_sign(e); Ok(()) },
1057
1058 // Otherwise, we're guaranteed to be inside the triangulation,
1059 // because the hull is convex by construction.
1060 Walk::Inside(e) => self.walk_fill(src, dst, e),
1061 }
1062 }
1063
1064 pub(crate) fn legalize(&mut self, e_ab: EdgeIndex) {
1065 /* We're given this
1066 * c
1067 * / ^
1068 * / \
1069 * / \
1070 * / \
1071 * V e \
1072 * a----------->\
1073 * \<-----------b
1074 * \ f ^
1075 * \ /
1076 * \ /
1077 * \ /
1078 * V /
1079 * d
1080 * We check whether d is within the circumcircle of abc.
1081 * If so, then we flip the edge and recurse based on the triangles
1082 * across from edges ad and db.
1083 *
1084 * This function may be called with a half-empty edge, e.g. while
1085 * recursing; in that case, then return immediately.
1086 */
1087 let edge = self.half.edge(e_ab);
1088 if edge.fixed() || edge.buddy == EMPTY_EDGE {
1089 return;
1090 }
1091 let a = edge.src;
1092 let b = edge.dst;
1093 let c = self.half.edge(self.half.next(e_ab)).dst;
1094
1095 let e_ba = edge.buddy;
1096 let e_ad = self.half.next(e_ba);
1097 let d = self.half.edge(e_ad).dst;
1098
1099 if in_circle(self.points[a], self.points[b], self.points[c],
1100 self.points[d]) > 0.0
1101 {
1102 let e_db = self.half.prev(e_ba);
1103
1104 self.half.swap(e_ab);
1105 self.legalize(e_ad);
1106 self.legalize(e_db);
1107 }
1108 }
1109
1110 /// Calculates a bounding box, returning `((xmin, xmax), (ymin, ymax))`
1111 pub(crate) fn bbox(points: &[Point]) -> ((f64, f64), (f64, f64)) {
1112 let (mut xmin, mut xmax) = (std::f64::INFINITY, -std::f64::INFINITY);
1113 let (mut ymin, mut ymax) = (std::f64::INFINITY, -std::f64::INFINITY);
1114 for (px, py) in points.iter() {
1115 xmin = px.min(xmin);
1116 ymin = py.min(ymin);
1117 xmax = px.max(xmax);
1118 ymax = py.max(ymax);
1119 }
1120 ((xmin, xmax), (ymin, ymax))
1121 }
1122
1123 /// Returns all of the resulting triangles, as indexes into the original
1124 /// `points` array from the constructor.
1125 pub fn triangles(&self) -> impl Iterator<Item=(usize, usize, usize)> + '_ {
1126 self.half.iter_triangles()
1127 .map(move |(a, b, c)|
1128 (self.remap[a], self.remap[b], self.remap[c]))
1129 }
1130
1131 /// Checks whether the given point is inside or outside the triangulation.
1132 /// This is extremely inefficient, and should only be used for debugging
1133 /// or unit tests.
1134 pub fn inside(&self, p: Point) -> bool {
1135 self.half.iter_triangles()
1136 .any(|(a, b, c)| {
1137 orient2d(self.points[a], self.points[b], p) >= 0.0 &&
1138 orient2d(self.points[b], self.points[c], p) >= 0.0 &&
1139 orient2d(self.points[c], self.points[a], p) >= 0.0
1140 })
1141 }
1142
1143 /// Writes the current state of the triangulation to an SVG file,
1144 /// without debug visualizations.
1145 pub fn save_svg(&self, filename: &str) -> std::io::Result<()> {
1146 std::fs::write(filename, self.to_svg(false))
1147 }
1148
1149 /// Writes the current state of the triangulation to an SVG file,
1150 /// including the upper hull as a debugging visualization.
1151 pub fn save_debug_svg(&self, filename: &str) -> std::io::Result<()> {
1152 std::fs::write(filename, self.to_svg(true))
1153 }
1154
1155 /// Converts the current state of the triangulation to an SVG. When `debug`
1156 /// is true, includes the upper hull and to-be-fixed edges; otherwise, just
1157 /// shows points, triangles, and fixed edges from the half-edge graph.
1158 pub fn to_svg(&self, debug: bool) -> String {
1159 let (x_bounds, y_bounds) = Self::bbox(&self.points);
1160 let scale = 800.0 /
1161 (x_bounds.1 - x_bounds.0).max(y_bounds.1 - y_bounds.0);
1162 let line_width = 2.0;
1163 let dx = |x| { scale * (x - x_bounds.0) + line_width};
1164 let dy = |y| { scale * (y_bounds.1 - y) + line_width};
1165
1166 let mut out = String::new();
1167 // Put a dummy rectangle in the SVG so that rsvg-convert doesn't clip
1168 out.push_str(&format!(
1169 r#"<svg viewbox="auto" xmlns="http://www.w3.org/2000/svg" width="{}" height="{}">
1170 <rect x="0" y="0" width="{}" height="{}"
1171 style="fill:rgb(0,0,0)" />"#,
1172 scale * (x_bounds.1 - x_bounds.0) + line_width*2.0,
1173 scale * (y_bounds.1 - y_bounds.0) + line_width*2.0,
1174 dx(x_bounds.1) + line_width,
1175 dy(y_bounds.0) + line_width));
1176
1177 // Draw endings in green (they will be overdrawn in white if they're
1178 // included in the triangulation).
1179 if debug {
1180 for (p, (start, end)) in self.endings.iter().enumerate() {
1181 for i in *start..*end {
1182 let dst = PointIndex::new(p);
1183 let src = self.ending_data[i];
1184 out.push_str(&format!(
1185 r#"
1186 <line x1="{}" y1="{}" x2="{}" y2="{}"
1187 style="stroke:rgb(0,255,0)"
1188 stroke-width="{}" stroke-linecap="round" />"#,
1189 dx(self.points[src].0),
1190 dy(self.points[src].1),
1191 dx(self.points[dst].0),
1192 dy(self.points[dst].1),
1193 line_width));
1194 }
1195 }
1196 }
1197
1198 // Push every edge into the SVG
1199 for (pa, pb, fixed) in self.half.iter_edges() {
1200 out.push_str(&format!(
1201 r#"
1202 <line x1="{}" y1="{}" x2="{}" y2="{}"
1203 style="{}"
1204 stroke-width="{}"
1205 stroke-linecap="round" />"#,
1206 dx(self.points[pa].0),
1207 dy(self.points[pa].1),
1208 dx(self.points[pb].0),
1209 dy(self.points[pb].1),
1210 if fixed { "stroke:rgb(255,255,255)" }
1211 else { "stroke:rgb(255,0,0)" },
1212 line_width))
1213 }
1214
1215 if debug {
1216 for e in self.hull.values() {
1217 let edge = self.half.edge(e);
1218 let (pa, pb) = (edge.src, edge.dst);
1219 out.push_str(&format!(
1220 r#"
1221 <line x1="{}" y1="{}" x2="{}" y2="{}"
1222 style="stroke:rgb(255,255,0)"
1223 stroke-width="{}" stroke-dasharray="{}"
1224 stroke-linecap="round" />"#,
1225 dx(self.points[pa].0),
1226 dy(self.points[pa].1),
1227 dx(self.points[pb].0),
1228 dy(self.points[pb].1),
1229 line_width, line_width * 2.0))
1230 }
1231 }
1232
1233 for p in self.points.iter() {
1234 out.push_str(&format!(
1235 r#"
1236 <circle cx="{}" cy="{}" r="{}" style="fill:rgb(255,128,128)" />"#,
1237 dx(p.0), dy(p.1), line_width));
1238 }
1239
1240 out.push_str("\n</svg>");
1241 out
1242 }
1243}
1244
1245// Finds the three points in the given buffer with the lowest score, returning
1246// then in order (so that out[0] is closest)
1247//
1248// This is faster than sorting an entire array each time.
1249fn min3(buf: &[(usize, f64)], points: &[(f64, f64)]) -> [usize; 3] {
1250 let mut array = [(0, std::f64::INFINITY); 3];
1251 for &(p, score) in buf.iter() {
1252 if score < array[0].1 {
1253 array[0] = (p, score);
1254 }
1255 }
1256 for &(p, score) in buf.iter() {
1257 if score < array[1].1 {
1258 // If there is one point picked already, then don't
1259 // pick it again, since that will be doomed to be colinear.
1260 let p0 = points[array[0].0];
1261 if (p0.0 - points[p].0).abs() >= std::f64::EPSILON ||
1262 (p0.1 - points[p].1).abs() >= std::f64::EPSILON
1263 {
1264 array[1] = (p, score);
1265 }
1266 }
1267 }
1268 for &(p, score) in buf.iter() {
1269 if score < array[2].1 {
1270 let p0 = points[array[0].0];
1271 let p1 = points[array[1].0];
1272 if orient2d(p0, p1, points[p]).abs() > std::f64::EPSILON {
1273 array[2] = (p, score);
1274 }
1275 }
1276 }
1277
1278 let mut out = [0usize; 3];
1279 for (i, a) in array.iter().enumerate() {
1280 out[i] = a.0;
1281 }
1282 // TODO: return a reasonable error if all inputs are colinear or duplicates
1283 out
1284}
1285
1286#[cfg(test)]
1287mod tests {
1288 use super::*;
1289
1290 #[test]
1291 fn simple_triangle() {
1292 let pts = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)];
1293 let t = Triangulation::build(&pts[0..]).expect("Could not construct");
1294 assert!(t.inside((0.5, 0.5)));
1295 }
1296
1297 #[test]
1298 fn duplicate_point() {
1299 let points = vec![
1300 (0.0, 0.0),
1301 (1.0, 0.0),
1302 (1.1, 1.1),
1303 (1.1, 1.1),
1304 (0.0, 1.0),
1305 ];
1306 let edges = vec![
1307 (0, 1),
1308 (1, 2),
1309 (3, 4),
1310 (4, 0),
1311 ];
1312 let t = Triangulation::build_with_edges(&points, &edges);
1313 assert!(!t.is_err());
1314 assert!(t.unwrap().inside((0.5, 0.5)));
1315 }
1316
1317 #[test]
1318 fn simple_circle() {
1319 let mut edges = Vec::new();
1320 let mut points = Vec::new();
1321 const N: usize = 22;
1322 for i in 0..N {
1323 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1324 let x = a.cos();
1325 let y = a.sin();
1326 points.push((x, y));
1327 edges.push((i, (i + 1) % N));
1328 }
1329 let t = Triangulation::build_with_edges(&points, &edges)
1330 .expect("Could not build triangulation");
1331 assert!(t.inside((0.0, 0.0)));
1332 assert!(!t.inside((1.01, 0.0)));
1333 }
1334
1335 #[test]
1336 fn dupe_start() {
1337 let points = vec![
1338 // Duplicate nearest points
1339 (0.5, 0.5),
1340 (0.5, 0.5),
1341 (0.5, 0.6),
1342 (0.6, 0.5),
1343 (0.5, 0.4),
1344
1345 // Force the center to be at 0.5, 0.5
1346 (0.0, 0.0),
1347 (1.0, 0.0),
1348 (1.0, 1.0),
1349 (0.0, 1.0),
1350 ];
1351 let edges = vec![
1352 (1, 2),
1353 (2, 3),
1354 (3, 4),
1355 (4, 0),
1356 ];
1357 let t = Triangulation::build_with_edges(&points, &edges)
1358 .expect("Could not build triangulation");
1359 assert!(t.inside((0.55, 0.5)));
1360 assert!(!t.inside((0.45, 0.5)));
1361 }
1362
1363 #[test]
1364 fn colinear_start() {
1365 let points = vec![
1366 // Force the center to be at 0.5, 0.5
1367 (0.0, 0.0),
1368 (1.0, 0.0),
1369 (1.0, 1.0),
1370 (0.0, 1.0),
1371
1372 // Threee colinear points
1373 (0.5, 0.4),
1374 (0.5, 0.5),
1375 (0.5, 0.6),
1376 (0.6, 0.5),
1377 ];
1378 let edges = vec![
1379 (4, 5),
1380 (5, 6),
1381 (6, 7),
1382 (7, 4),
1383 ];
1384 let t = Triangulation::build_with_edges(&points, &edges)
1385 .expect("Could not build triangulation");
1386 assert!(t.inside((0.55, 0.5)));
1387 assert!(!t.inside((0.45, 0.5)));
1388 }
1389
1390 #[test]
1391 fn fuzzy_circle() {
1392 let mut edges = Vec::new();
1393 let mut points = Vec::new();
1394 const N: usize = 32;
1395 for i in 0..N {
1396 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1397 let x = a.cos();
1398 let y = a.sin();
1399 points.push((x, y));
1400 edges.push((i, (i + 1) % N));
1401 }
1402 const M: usize = 32;
1403
1404 use std::iter::repeat_with;
1405 use rand::{Rng, SeedableRng};
1406 use itertools::Itertools;
1407
1408 // Use a ChaCha RNG to be reproducible across platforms
1409 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(12345);
1410 points.extend(repeat_with(|| rng.gen_range(-1.0..1.0))
1411 .tuple_windows()
1412 .filter(|(x, y): &(f64, f64)| (x*x + y*y).sqrt() < 0.95)
1413 .take(M));
1414
1415 let t = Triangulation::build_with_edges(&points, &edges)
1416 .expect("Could not build triangulation");
1417 assert!(t.inside((0.0, 0.0)));
1418 assert!(!t.inside((1.01, 0.0)));
1419 }
1420
1421 #[test]
1422 fn spiral_circle() {
1423 let mut edges = Vec::new();
1424 let mut points = Vec::new();
1425 const N: usize = 16;
1426 for i in 0..N {
1427 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1428 let x = a.cos();
1429 let y = a.sin();
1430 points.push((x, y));
1431 edges.push((i, (i + 1) % N));
1432 }
1433 const M: usize = 32;
1434 for i in 0..(2*M) {
1435 let a = (i as f64) / (M as f64) * core::f64::consts::PI * 2.0;
1436 let scale = (i as f64 + 1.1).powf(0.2);
1437 let x = a.cos() / scale;
1438 let y = a.sin() / scale;
1439 points.push((x, y));
1440 }
1441
1442 let t = Triangulation::build_with_edges(&points, &edges)
1443 .expect("Could not build triangulation");
1444 assert!(t.inside((0.0, 0.0)));
1445 assert!(!t.inside((1.01, 0.0)));
1446 }
1447
1448 #[test]
1449 fn nested_circles() {
1450 let mut edges = Vec::new();
1451 let mut points = Vec::new();
1452 const N: usize = 32;
1453 for i in 0..N {
1454 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1455 let x = a.cos();
1456 let y = a.sin();
1457 points.push((x, y));
1458 edges.push((i, (i + 1) % N));
1459 }
1460 for i in 0..N {
1461 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1462 let x = a.cos() / 2.0;
1463 let y = a.sin() / 2.0;
1464 points.push((x, y));
1465 edges.push((N + i, N + (i + 1) % N));
1466 }
1467
1468 let t = Triangulation::build_with_edges(&points, &edges)
1469 .expect("Could not build triangulation");
1470 assert!(!t.inside((0.0, 0.0)));
1471 assert!(!t.inside((1.01, 0.0)));
1472 assert!(t.inside((0.75, 0.0)));
1473 assert!(t.inside((0.0, 0.8)));
1474 }
1475
1476 #[test]
1477 fn grid() {
1478 let mut points = Vec::new();
1479 const N: usize = 32;
1480 for i in 0..N {
1481 for j in 0..N {
1482 points.push((i as f64, j as f64));
1483 }
1484 }
1485 let t = Triangulation::build(&points)
1486 .expect("Could not build triangulation");
1487 t.check();
1488 }
1489
1490 #[test]
1491 fn grid_with_fixed_circle() {
1492 let mut edges = Vec::new();
1493 let mut points = Vec::new();
1494 const N: usize = 32;
1495 for i in 0..N {
1496 let a = (i as f64) / (N as f64) * core::f64::consts::PI * 2.0;
1497 let x = a.cos() * 0.9;
1498 let y = a.sin() * 0.9;
1499 points.push((x, y));
1500 edges.push((i, (i + 1) % N));
1501 }
1502 const M: usize = 32;
1503 for i in 0..M {
1504 for j in 0..M {
1505 points.push((i as f64 / M as f64 * 2.0 - 1.0,
1506 j as f64 / M as f64 * 2.0 - 1.0));
1507 }
1508 }
1509 let t = Triangulation::build_with_edges(&points, &edges)
1510 .expect("Could not build triangulation");
1511 t.check();
1512 }
1513
1514 #[test]
1515 fn new_from_contours() {
1516 let t = Triangulation::build_from_contours::<Vec<usize>>(
1517 &[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)], &vec![]);
1518 assert!(t.is_ok());
1519
1520 let t = Triangulation::build_from_contours(
1521 &[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)], &[vec![]]);
1522 assert!(t.is_ok());
1523
1524 let t = Triangulation::build_from_contours(
1525 &[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)], &[vec![0]]);
1526 assert!(t.is_ok());
1527
1528 let t = Triangulation::build_from_contours(
1529 &[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)], &[vec![0, 1]]);
1530 assert!(t.is_err());
1531 if let Err(e) = t {
1532 assert!(e == Error::OpenContour);
1533 }
1534 }
1535}