1use crate::algorithms::zhash::{ZHashBox, ZHashable};
2use crate::data::{Point, PointId, PointLocation, Polygon, TriangleView};
3use crate::Orientation;
4use crate::PolygonScalar;
5
6use rand::rngs::SmallRng;
8use rand::Rng;
9use rand::SeedableRng;
10
11pub fn earclip<T>(poly: &Polygon<T>) -> impl Iterator<Item = (PointId, PointId, PointId)> + '_
13where
14 T: PolygonScalar,
15{
16 assert!(poly.rings.len() == 1);
18 let rng = SmallRng::seed_from_u64(0xDEADBEEF);
20 triangulate_list(&poly.points, &poly.rings[0], rng)
21}
22
23pub fn earclip_hashed<T>(
27 poly: &Polygon<T>,
28) -> impl Iterator<Item = (PointId, PointId, PointId)> + '_
29where
30 T: PolygonScalar + ZHashable,
31{
32 assert!(poly.rings.len() == 1);
34 let rng = SmallRng::seed_from_u64(0xDEADBEEF);
36 triangulate_list_hashed(&poly.points, &poly.rings[0], rng)
37}
38
39pub fn triangulate_list<'a, T, R>(
53 points: &'a [Point<T>],
54 order: &'a [PointId],
55 mut rng: R,
56) -> impl Iterator<Item = (PointId, PointId, PointId)> + 'a
57where
58 T: PolygonScalar,
59 R: Rng + 'static,
60{
61 let mut len = order.len();
62 let mut vertices = List::new(points, order);
63 let mut possible_ears = EarStore::new(order.len());
64 std::iter::from_fn(move || match len {
65 0..=2 => None,
66 _ => loop {
67 let focus = vertices.cursor(possible_ears.pop(&mut rng).unwrap());
68 let prev = focus.prev();
69 let next = focus.next();
70 if is_ear(prev, focus, next) {
71 possible_ears.new_possible_ear(prev.position);
72 possible_ears.new_possible_ear(next.position);
73 let out = (prev.point_id(), focus.point_id(), next.point_id());
74 let focus = focus.position;
75 vertices.delete(focus);
76 len -= 1;
77 return Some(out);
78 }
79 },
80 })
81}
82
83fn is_ear<T>(a: Cursor<'_, T>, b: Cursor<'_, T>, c: Cursor<'_, T>) -> bool
84where
85 T: PolygonScalar,
86{
87 let trig = TriangleView::new_unchecked([a.point(), b.point(), c.point()]);
88 if trig.orientation() == Orientation::CounterClockWise {
89 let mut focus = c.next();
90 while focus != a {
91 if trig.locate(focus.point()) != PointLocation::Outside {
92 return false;
93 }
94 focus = focus.next();
95 }
96 true
97 } else {
98 false
99 }
100}
101
102pub fn triangulate_list_hashed<'a, T, R>(
106 points: &'a [Point<T>],
107 order: &'a [PointId],
108 mut rng: R,
109) -> impl Iterator<Item = (PointId, PointId, PointId)> + 'a
110where
111 T: PolygonScalar + ZHashable,
112 R: Rng + 'static,
113{
114 let mut len = order.len();
115 let mut vertices = List::new(points, order);
116 let mut possible_ears = EarStore::new(order.len());
117
118 let zbox = zbox_slice(points, order);
119 let key = ZHashable::zhash_key(zbox);
120 let zhashes: Vec<u64> = order
121 .iter()
122 .map(|&pid| ZHashable::zhash_fn(key, &points[pid.usize()]))
123 .collect();
124 let mut zorder = List::new_sorted(points, order, zhashes);
125
126 std::iter::from_fn(move || match len {
127 0..=2 => None,
128 _ => loop {
129 let focus = possible_ears.pop(&mut rng).unwrap();
130 let prev = vertices.prev(focus);
131 let next = vertices.next(focus);
132 if is_ear_hashed(
133 key,
134 zorder.cursor(prev),
135 zorder.cursor(focus),
136 zorder.cursor(next),
137 ) {
138 possible_ears.new_possible_ear(prev);
139 possible_ears.new_possible_ear(next);
140 vertices.delete(focus);
141 zorder.delete(focus);
142 len -= 1;
143 let out = (order[prev], order[focus], order[next]);
144 return Some(out);
145 }
146 },
147 })
148}
149
150fn is_ear_hashed<T: PolygonScalar + ZHashable>(
151 key: <T as ZHashable>::ZHashKey,
152 a: Cursor<'_, T>,
153 b: Cursor<'_, T>,
154 c: Cursor<'_, T>,
155) -> bool {
156 let trig = TriangleView::new_unchecked([a.point(), b.point(), c.point()]);
157 if trig.orientation() == Orientation::CounterClockWise {
158 let (min, max) = trig.bounding_box();
161 let min_hash = ZHashable::zhash_fn(key, &min);
162 let max_hash = ZHashable::zhash_fn(key, &max);
163
164 let mut up_focus = b.next();
167 let mut down_focus = b.prev();
168
169 let cond = |cursor: Cursor<'_, T>| cursor.valid() && cursor.between(min_hash, max_hash);
170 let check = |cursor: Cursor<'_, T>| {
171 cursor != a
172 && cursor != b
173 && cursor != c
174 && trig.locate(cursor.point()) != PointLocation::Outside
175 };
176
177 while cond(up_focus) && cond(down_focus) {
178 if check(up_focus) || check(down_focus) {
179 return false;
180 }
181 up_focus = up_focus.next();
182 down_focus = down_focus.prev();
183 }
184
185 while cond(up_focus) {
187 if check(up_focus) {
188 return false;
189 }
190 up_focus = up_focus.next();
191 }
192
193 while cond(down_focus) {
195 if check(down_focus) {
196 return false;
197 }
198 down_focus = down_focus.prev();
199 }
200
201 true
202 } else {
203 false
204 }
205}
206
207fn zbox_slice<'a, T>(slice: &'a [Point<T>], order: &'a [PointId]) -> ZHashBox<'a, T>
208where
209 T: PolygonScalar + ZHashable,
210{
211 if slice.is_empty() {
212 panic!("Cannot zhash an empty slice");
213 }
220 let mut zbox = ZHashBox {
221 min_x: slice[0].x_coord(),
222 max_x: slice[0].x_coord(),
223 min_y: slice[0].y_coord(),
224 max_y: slice[0].y_coord(),
225 };
226 for &pid in order {
227 let pt = &slice[pid.usize()];
228 if zbox.min_x > pt.x_coord() {
229 zbox.min_x = pt.x_coord();
230 }
231 if zbox.max_x < pt.x_coord() {
232 zbox.max_x = pt.x_coord();
233 }
234 if zbox.min_y > pt.y_coord() {
235 zbox.min_y = pt.y_coord();
236 }
237 if zbox.max_y < pt.y_coord() {
238 zbox.max_y = pt.y_coord();
239 }
240 }
241 zbox
242}
243
244struct Cursor<'a, T> {
248 list: &'a List<'a, T>,
249 position: usize,
250}
251
252impl<T> Eq for Cursor<'_, T> {}
253
254impl<'a, T> PartialEq for Cursor<'a, T> {
255 fn eq(&self, other: &Cursor<'a, T>) -> bool {
256 self.position == other.position
257 }
258}
259
260impl<T> Copy for Cursor<'_, T> {}
261
262impl<'a, T> Clone for Cursor<'a, T> {
263 fn clone(&self) -> Cursor<'a, T> {
264 *self
265 }
266}
267
268impl<'a, T> Cursor<'a, T> {
269 fn valid(&self) -> bool {
270 self.position != usize::MAX
271 }
272
273 fn next(mut self) -> Cursor<'a, T> {
274 self.position = self.list.next(self.position);
275 self
276 }
277
278 fn prev(mut self) -> Cursor<'a, T> {
279 self.position = self.list.prev(self.position);
280 self
281 }
282
283 fn point(&self) -> &Point<T> {
284 self.list.point(self.position)
285 }
286
287 fn point_id(&self) -> PointId {
288 self.list.point_id(self.position)
289 }
290
291 fn hash(&self) -> u64 {
292 self.list.hash(self.position)
293 }
294
295 fn between(&self, min: u64, max: u64) -> bool {
296 self.hash() >= min && self.hash() <= max
297 }
298}
299
300struct List<'a, T> {
301 points: &'a [Point<T>],
302 order: &'a [PointId],
303 hashes: Vec<u64>,
304 prev: Vec<usize>,
305 next: Vec<usize>,
306}
307
308impl<'a, T> List<'a, T> {
309 fn new(points: &'a [Point<T>], order: &'a [PointId]) -> List<'a, T> {
310 let size = order.len();
311 let mut prev = Vec::with_capacity(size);
312 let mut next = Vec::with_capacity(size);
313 prev.resize(size, 0);
314 next.resize(size, 0);
315 for i in 0..size {
316 prev[(i + 1) % size] = i;
317 next[i] = (i + 1) % size;
318 }
319 List {
320 points,
321 order,
322 hashes: vec![],
323 prev,
324 next,
325 }
326 }
327
328 fn prev(&self, vertex: usize) -> usize {
329 self.prev[vertex]
330 }
331
332 fn next(&self, vertex: usize) -> usize {
333 self.next[vertex]
334 }
335
336 fn delete(&mut self, vertex: usize) {
337 let prev = self.prev[vertex];
338 let next = self.next[vertex];
339 if prev != usize::MAX {
340 self.next[prev] = next;
341 }
342 if next != usize::MAX {
343 self.prev[next] = prev;
344 }
345 }
346
347 fn cursor(&self, vertex: usize) -> Cursor<'_, T> {
348 Cursor {
349 list: self,
350 position: vertex,
351 }
352 }
353
354 fn point(&self, vertex: usize) -> &Point<T> {
355 &self.points[self.point_id(vertex).usize()]
356 }
357
358 fn point_id(&self, vertex: usize) -> PointId {
359 self.order[vertex]
360 }
361
362 fn hash(&self, vertex: usize) -> u64 {
363 self.hashes[vertex]
364 }
365}
366
367impl<'a, T> List<'a, T> {
368 fn new_sorted(points: &'a [Point<T>], order: &'a [PointId], keys: Vec<u64>) -> List<'a, T> {
369 let size = keys.len();
370 let mut v: Vec<usize> = (0..size).collect();
371 v.sort_unstable_by_key(|&idx| keys[idx]);
372 let mut prev = Vec::with_capacity(size);
373 let mut next = Vec::with_capacity(size);
374 prev.resize(size, 0);
375 next.resize(size, 0);
376 for i in 0..size {
377 prev[v[(i + 1) % size]] = v[i];
378 next[v[i]] = v[(i + 1) % size];
379 }
380 next[v[size - 1]] = usize::MAX;
381 prev[v[0]] = usize::MAX;
382 List {
383 points,
384 order,
385 hashes: keys,
386 prev,
387 next,
388 }
389 }
390}
391
392struct EarStore {
396 possible_ears_vec: Vec<usize>,
397 possible_ears_set: IntSet,
398}
399
400impl EarStore {
401 fn new(size: usize) -> EarStore {
402 EarStore {
403 possible_ears_vec: (0..size).collect(),
404 possible_ears_set: IntSet::with_capacity(size),
405 }
406 }
407
408 fn new_possible_ear(&mut self, possible_ear: usize) {
409 if !self.possible_ears_set.contains(possible_ear) {
410 self.possible_ears_set.insert(possible_ear);
411 self.possible_ears_vec.push(possible_ear);
412 }
413 }
414
415 fn pop<R>(&mut self, rng: &mut R) -> Option<usize>
416 where
417 R: Rng + ?Sized,
418 {
419 let n = rng.gen_range(0..self.possible_ears_vec.len());
420 let next = self.possible_ears_vec.swap_remove(n);
421 self.possible_ears_set.delete(next);
422 Some(next)
423 }
424}
425
426struct IntSet {
431 set: Vec<bool>,
432}
433
434impl IntSet {
435 fn with_capacity(capacity: usize) -> IntSet {
436 let mut set = Vec::with_capacity(capacity);
437 set.resize(capacity, true);
438 IntSet { set }
439 }
440
441 fn contains(&self, value: usize) -> bool {
442 self.set[value]
443 }
444
445 fn insert(&mut self, value: usize) {
446 self.set[value] = true
447 }
448
449 fn delete(&mut self, value: usize) {
450 self.set[value] = false
451 }
452}
453
454#[cfg(test)]
458#[cfg(not(tarpaulin_include))]
459mod tests {
460 use super::*;
461 use crate::data::*;
462 use num_bigint::BigInt;
463 use rand::rngs::SmallRng;
464 use rand::SeedableRng;
465
466 fn trig_area_2x<F: PolygonScalar + Into<BigInt>>(p: &Polygon<F>) -> BigInt {
467 let mut trig_area_2x = BigInt::from(0);
468 let rng = SmallRng::seed_from_u64(0);
470 for (a, b, c) in triangulate_list(&p.points, &p.rings[0], rng) {
471 let trig = TriangleView::new_unchecked([p.point(a), p.point(b), p.point(c)]);
472 trig_area_2x += trig.signed_area_2x::<BigInt>();
473 }
474 trig_area_2x
475 }
476
477 #[test]
478 fn basic_1() {
479 let p = Polygon::new(vec![
480 Point::new([0, 0]),
481 Point::new([1, 0]),
482 Point::new([1, 1]),
483 ])
484 .unwrap();
485
486 assert_eq!(p.signed_area_2x::<BigInt>(), trig_area_2x(&p));
487 }
488
489 #[test]
490 fn basic_2() {
491 let p = Polygon::new(vec![
492 Point::new([0, 0]),
493 Point::new([1, 0]),
494 Point::new([2, 0]),
495 Point::new([3, 0]),
496 Point::new([4, 0]),
497 Point::new([1, 1]),
498 ])
499 .unwrap();
500
501 assert_eq!(p.signed_area_2x::<BigInt>(), trig_area_2x(&p));
502 }
503
504 #[test]
505 fn basic_3() {
506 let rng = SmallRng::seed_from_u64(0);
507 let p: Polygon<i8> = Polygon::new(vec![
508 Point { array: [-44, -11] },
509 Point { array: [-43, 23] },
510 Point { array: [-64, 44] },
511 Point { array: [-52, 114] },
512 Point { array: [-82, 69] },
513 ])
514 .unwrap();
515
516 triangulate_list_hashed(&p.points, &p.rings[0], rng).count();
517 }
518
519 #[test]
520 fn basic_4() {
521 let rng = SmallRng::seed_from_u64(0);
522 let p: Polygon<i8> = Polygon::new(vec![
523 Point { array: [12, 5] }, Point { array: [0, 8] }, Point { array: [-10, -6] }, Point { array: [-3, 3] }, Point { array: [-2, 4] }, ])
529 .unwrap();
530
531 triangulate_list_hashed(&p.points, &p.rings[0], rng).count();
532 }
533
534 use proptest::prelude::*;
535 use test_strategy::proptest;
536
537 #[proptest]
538 fn equal_area_prop(poly: Polygon<i64>) {
539 prop_assert_eq!(poly.signed_area_2x::<BigInt>(), trig_area_2x(&poly));
540 }
541
542 #[proptest]
543 fn hashed_identity_prop(poly: Polygon<i8>) {
544 let rng = SmallRng::seed_from_u64(0);
545 let not_hashed: Vec<(PointId, PointId, PointId)> =
546 triangulate_list(&poly.points, &poly.rings[0], rng.clone()).collect();
547 let hashed: Vec<(PointId, PointId, PointId)> =
548 triangulate_list_hashed(&poly.points, &poly.rings[0], rng).collect();
549 prop_assert_eq!(not_hashed, hashed);
550 }
551}