rgeometry/algorithms/polygonization/
two_opt.rs

1use crate::data::Cursor;
2use crate::data::EndPoint;
3use crate::data::IndexEdge;
4use crate::data::LineSegmentView;
5use crate::data::Point;
6use crate::data::PointId;
7use crate::data::Polygon;
8use crate::data::{IndexIntersection, IndexIntersectionSet};
9use crate::Intersects;
10use crate::{Error, PolygonScalar, TotalOrd};
11
12use rand::Rng;
13use std::collections::BTreeSet;
14use std::ops::Bound::*;
15
16use crate::Orientation;
17
18pub fn resolve_self_intersections<T, R>(poly: &mut Polygon<T>, rng: &mut R) -> Result<(), Error>
19where
20  T: PolygonScalar,
21  R: Rng + ?Sized,
22{
23  assert_eq!(poly.rings.len(), 1);
24  if poly.iter_boundary().all(|pt| pt.is_colinear()) {
25    return Err(Error::InsufficientVertices);
26  }
27  // if all points are colinear, return error.
28  // dbg!(&pts);
29  let mut isects = IndexIntersectionSet::new(poly.iter_boundary().len());
30  // dbg!(&poly.rings[0]);
31  for e1 in edges(poly) {
32    for e2 in edges(poly) {
33      if e1 < e2 {
34        if let Some(isect) = intersects(poly, e1, e2) {
35          // eprintln!("Inserting new intersection: {:?} {:?}", e1, e2);
36          isects.push(isect)
37        }
38      }
39    }
40  }
41  // sanity_check(&poly, &isects);
42  // dbg!(isects.to_vec());
43  while let Some(isect) = isects.random(rng) {
44    untangle(poly, &mut isects, isect)
45  }
46  poly.ensure_ccw()?;
47  // poly.validate()?;
48  Ok(())
49}
50
51// Create list of edges
52// Find all intersections
53/// Generate a valid polygon by connecting a set of points in such a way
54/// that there are no self-intersections.
55/// # Time complexity
56/// $O(n^4)$
57/// # Space complexity
58/// $O(n^2)$
59pub fn two_opt_moves<T, R>(pts: Vec<Point<T>>, rng: &mut R) -> Result<Polygon<T>, Error>
60where
61  T: PolygonScalar,
62  R: Rng + ?Sized,
63{
64  {
65    let mut seen = BTreeSet::new();
66    for pt in pts.iter() {
67      if !seen.insert(pt) {
68        return Err(Error::DuplicatePoints);
69      }
70    }
71  }
72  if pts.len() < 3 {
73    return Err(Error::InsufficientVertices);
74  }
75  let mut poly = Polygon::new_unchecked(pts);
76  resolve_self_intersections(&mut poly, rng)?;
77  Ok(poly)
78}
79
80fn endpoint<T: TotalOrd>(a: PointId, b: PointId, c: PointId, t: T) -> EndPoint<T> {
81  if a == b || a == c {
82    EndPoint::Exclusive(t)
83  } else {
84    EndPoint::Inclusive(t)
85  }
86}
87
88fn intersects<T>(poly: &Polygon<T>, a: IndexEdge, b: IndexEdge) -> Option<IndexIntersection>
89where
90  T: PolygonScalar,
91{
92  let a_min = endpoint(a.min, b.min, b.max, poly.point(a.min));
93  let a_max = endpoint(a.max, b.min, b.max, poly.point(a.max));
94  let b_min = endpoint(b.min, a.min, a.max, poly.point(b.min));
95  let b_max = endpoint(b.max, a.min, a.max, poly.point(b.max));
96  let e1 = LineSegmentView::new(a_min, a_max);
97  let e2 = LineSegmentView::new(b_min, b_max);
98  // dbg!(e1, e2);
99  e1.intersect(e2)?; // Returns Some(...) if there exist a point shared by both line segments.
100  Some(IndexIntersection::new(a, b))
101}
102
103// untangle takes two edges that overlap and finds a solution that reduces the circumference
104// of the polygon. The solution is trivial when the two edges are not parallel: Simply uncross
105// the edges and the new edge lengths are guaranteed to be smaller than before.
106// If the edges are parallel, things get a bit more complicated.
107// Two overlapping parallel edges must have at least one vertex that entirely contained by one
108// of the edges. For example:
109//    a1 -> a2
110// b1 -> b2
111//
112// It's tempting to cut a1 and put it between b1 and b2. This doesn't change the edge lengths from
113// b1 to b2 and it may decrease the edge length from a1.prev to a2. However, if a1 is on a straight
114// line then we haven't reduced the circumference of the polygon at all. So, we need to find a point
115// that doesn't lie on a straight line with its neighbours but still lies on the line given by the
116// overlapping edges. Once this point has been found (along with its corresponding edge), it can be
117// cut with the guarantee that it'll reduce the polygon circumference. Like this:
118//
119//     prev
120//       |
121//       a1 -----> a2
122// b1 -------> b2
123//
124// Cut 'a1' and place it between 'b1' and 'b2':
125//
126//     prev
127//        \------> a2
128// b1 -> a1 -> b2
129//
130// The edge length from 'b1->b2' is identical to 'b1->a1->b2'.
131// The edge length from 'prev->a1->a2' is always greater than 'prev -> a2'.
132// We therefore know that the total circumference has decreased. QED.
133fn untangle<T: PolygonScalar>(
134  poly: &mut Polygon<T>,
135  set: &mut IndexIntersectionSet,
136  isect: IndexIntersection,
137) {
138  // dbg!(vertex_list.vertices().collect::<Vec<Vertex>>());
139  // dbg!(isect);
140  // eprintln!("Poly order: {:?}", poly.order);
141  // eprintln!("Poly pos:   {:?}", poly.positions);
142  // eprintln!("Untangle: {:?}", isect);
143  // let da = poly.direct(isect.min);
144  // let db = poly.direct(isect.max);
145  let da = poly.cursor(poly.direct(isect.min).src);
146  let db = poly.cursor(poly.direct(isect.max).src);
147
148  let inserted_edges;
149
150  if parallel_edges(da, db) {
151    let (a_min, a_max) = linear_extremes(da);
152    let (b_min, b_max) = linear_extremes(db);
153
154    // A kink is a point which would shorten the circumference of the polygon
155    // if it was removed.
156    // If we can find a kink and an edge which contains it then we can move it
157    // and shorten the circumference.
158    let mut kinks = Vec::new();
159    for elt in a_min.to(Included(a_max)).chain(b_min.to(Included(b_max))) {
160      let inner_segment: LineSegmentView<T> = (elt.prev().point()..elt.next().point()).into();
161      if elt.orientation() != Orientation::CoLinear || !inner_segment.contains(elt.point()) {
162        // We're either at a corner:
163        //   prev
164        //    \
165        //     x -- next
166        // Or we're at u-turn:
167        //   prev ->  x
168        //   next <-  x
169        // In both cases, the circumference will be shortened if we can remove 'x'.
170        kinks.push(elt);
171      }
172    }
173
174    let mut mergable = None;
175    'outer: for edge in a_min.to(Excluded(a_max)).chain(b_min.to(Excluded(b_max))) {
176      let segment = LineSegmentView::new(
177        EndPoint::Exclusive(edge.point()),
178        EndPoint::Exclusive(edge.next().point()),
179      );
180      for &kink in kinks.iter() {
181        if segment.contains(kink.point()) {
182          mergable = Some((kink, edge));
183          break 'outer;
184        }
185      }
186    }
187    // if mergable.is_none() {
188    //   dbg!(&kinks);
189    //   dbg!(a_min.point());
190    //   dbg!(a_max.point());
191    //   dbg!(b_min.point());
192    //   dbg!(b_max.point());
193    //   for (nth, edge) in a_min.to(a_max).chain(b_min.to(b_max)).enumerate() {
194    //     dbg!(nth, edge.point(), edge.next().point());
195    //   }
196    // }
197    // elt is not linear. That is, prev -> elt -> next is not a straight line.
198    // Therefore, cutting it and adding to a straight line will shorten the polygon
199    // circumference. Since there's a lower limit on the circumference, this algorithm
200    // is guaranteed to terminate.
201    let (kink, edge) = mergable.expect("There must be at least one mergable edge");
202    // dbg!(elt, edge);
203    // let elt_edges = vertex_list.vertex_edges(elt);
204    // vertex_list.hoist(elt, edge);
205    let del_edge_1 = IndexEdge::new(kink.prev().point_id(), kink.point_id());
206    let del_edge_2 = IndexEdge::new(kink.point_id(), kink.next().point_id());
207    let del_edge_3 = IndexEdge::new(edge.point_id(), edge.next().point_id());
208    // eprintln!(
209    //   "Del edges: {:?} {:?} {:?}",
210    //   del_edge_1, del_edge_2, del_edge_3
211    // );
212    set.remove_all(del_edge_1);
213    set.remove_all(del_edge_2);
214    set.remove_all(del_edge_3);
215
216    inserted_edges = vec![
217      IndexEdge::new(kink.prev().point_id(), kink.next().point_id()),
218      IndexEdge::new(edge.point_id(), kink.point_id()),
219      IndexEdge::new(kink.point_id(), edge.next().point_id()),
220    ];
221
222    let p1 = edge.position;
223    let p2 = kink.position;
224    // eprintln!("Hoist: {:?} {:?}", p1, p2);
225    poly.vertices_join(p1, p2);
226  } else {
227    // vertex_list.uncross(da, db);
228    set.remove_all(IndexEdge::new(da.point_id(), da.next().point_id()));
229    set.remove_all(IndexEdge::new(db.point_id(), db.next().point_id()));
230
231    inserted_edges = vec![
232      IndexEdge::new(da.point_id(), db.point_id()),
233      IndexEdge::new(da.next().point_id(), db.next().point_id()),
234    ];
235
236    let p1 = da.next().position;
237    let p2 = db.position;
238    // eprintln!("Uncross: {:?} {:?}", p1, p2);
239    poly.vertices_reverse(p1, p2);
240    // dbg!(&poly.rings[0]);
241  }
242  // dbg!(&removed_edges, &inserted_edges);
243  // eprintln!("New edges: {:?}", &inserted_edges);
244  for &edge in inserted_edges.iter() {
245    for e1 in edges(poly) {
246      if e1 != edge {
247        if let Some(isect) = intersects(poly, e1, edge) {
248          // eprintln!("Inserting new intersection: {:?} {:?}", e1, edge);
249          set.push(isect)
250        }
251      }
252    }
253  }
254  // sanity_check(&poly, &set);
255}
256
257#[allow(dead_code)]
258#[cfg(not(tarpaulin_include))]
259fn sanity_check<T: PolygonScalar>(poly: &Polygon<T>, isects: &IndexIntersectionSet) {
260  let naive_set = naive_intersection_set(poly);
261  let fast_set = isects.iter().collect();
262  let missing: Vec<&IndexIntersection> = naive_set.difference(&fast_set).collect();
263  let extra: Vec<&IndexIntersection> = fast_set.difference(&naive_set).collect();
264  if !missing.is_empty() {
265    panic!("Fast set is too small! {:?}", missing);
266  }
267  if !extra.is_empty() {
268    panic!("Fast set is too large! {:?}", extra);
269  }
270}
271
272#[cfg(not(tarpaulin_include))]
273fn naive_intersection_set<T: PolygonScalar>(poly: &Polygon<T>) -> BTreeSet<IndexIntersection> {
274  let mut set = BTreeSet::new();
275  for e1 in edges(poly) {
276    for e2 in edges(poly) {
277      if e1 < e2 {
278        if let Some(isect) = intersects(poly, e1, e2) {
279          set.insert(isect);
280        }
281      }
282    }
283  }
284  set
285}
286
287fn parallel_edges<T>(a: Cursor<'_, T>, b: Cursor<'_, T>) -> bool
288where
289  T: PolygonScalar,
290{
291  let a1 = a.point();
292  let a2 = a.next().point();
293  let b1 = b.point();
294  let b2 = b.next().point();
295  Point::orient(a1, a2, b1).is_colinear() && Point::orient(a1, a2, b2).is_colinear()
296}
297
298/// Find the leftmost and rightmost vertices that are not linear.
299/// Example:
300///    a                    f
301///     \- b - c - d -> e -/
302/// linear_extremes('c') = ('b', 'e').
303/// 'c' is linear (there's a straight line from 'b' to 'c' to 'd').
304/// 'b' is not linear since the line from 'a' to 'b' to 'c' bends.
305/// 'b' is therefore the first leftmost non-linear vertex and 'e'
306/// is the first rightmost non-linear vertex.
307fn linear_extremes<T>(at: Cursor<'_, T>) -> (Cursor<'_, T>, Cursor<'_, T>)
308where
309  T: PolygonScalar,
310{
311  let mut at_min = at;
312  let mut at_max = at;
313  at_max.move_next();
314  while at_min.is_colinear() {
315    at_min.move_prev();
316  }
317  while at_max.is_colinear() {
318    at_max.move_next();
319  }
320  (at_min, at_max)
321}
322
323fn edges<T>(poly: &Polygon<T>) -> impl Iterator<Item = IndexEdge> + '_ {
324  poly
325    .iter_boundary()
326    .map(|cursor| IndexEdge::new(cursor.point_id(), cursor.next().point_id()))
327}
328
329#[cfg(test)]
330#[cfg(not(tarpaulin_include))]
331pub mod tests {
332  use super::*;
333
334  use crate::testing::any_nn;
335  use ordered_float::NotNan;
336  use proptest::collection::vec;
337  use proptest::prelude::*;
338  use rand::prelude::SliceRandom;
339  use rand::rngs::mock::StepRng;
340  use rand::SeedableRng;
341  use test_strategy::proptest;
342
343  #[test]
344  fn unit_1() {
345    let pts: Vec<Point<i8>> = vec![
346      Point { array: [-71, 91] },
347      Point { array: [-17, -117] },
348      Point { array: [-13, 98] },
349      Point { array: [-84, 67] },
350      Point { array: [-12, -92] },
351      Point { array: [-95, 71] },
352      Point { array: [-81, -2] },
353      Point { array: [-91, -9] },
354      Point { array: [-42, -66] },
355      Point { array: [-107, 105] },
356      Point { array: [-49, 9] },
357      Point { array: [-96, 92] },
358      Point { array: [42, 11] },
359      Point { array: [-63, 56] },
360      Point { array: [122, -53] },
361      Point { array: [93, 29] },
362      Point { array: [-93, 89] },
363      Point { array: [40, -63] },
364      Point { array: [-127, -44] },
365      Point { array: [-108, 74] },
366      Point { array: [96, -5] },
367      Point { array: [46, 3] },
368      Point { array: [-103, -94] },
369      Point { array: [125, 73] },
370      Point { array: [104, 60] },
371      Point { array: [-55, -55] },
372      Point { array: [-112, -42] },
373      Point { array: [107, -16] },
374      Point { array: [38, -111] },
375      Point { array: [57, 123] },
376      Point { array: [-107, 108] },
377      Point { array: [46, -61] },
378      Point { array: [0, -35] },
379      Point { array: [35, -115] },
380      Point { array: [-120, 31] },
381      Point { array: [123, -87] },
382      Point { array: [-22, -87] },
383      Point { array: [-91, 27] },
384      Point { array: [101, -6] },
385      Point { array: [43, 6] },
386      Point { array: [-31, -73] },
387      Point {
388        array: [-107, -115],
389      },
390      Point { array: [-60, -98] },
391      Point { array: [-18, -94] },
392      Point { array: [52, -22] },
393      Point { array: [-71, -128] },
394      Point { array: [80, -26] },
395      Point { array: [104, -91] },
396      Point { array: [-91, 45] },
397      Point { array: [-79, -91] },
398      Point { array: [-47, -124] },
399      Point { array: [14, 101] },
400      Point { array: [-21, -69] },
401      Point { array: [16, 55] },
402      Point { array: [105, -76] },
403      Point { array: [-78, 39] },
404      Point { array: [80, -114] },
405      Point { array: [-6, 9] },
406      Point { array: [-65, -104] },
407      Point { array: [16, -1] },
408      Point { array: [122, -67] },
409      Point { array: [-93, -123] },
410      Point {
411        array: [-121, -120],
412      },
413      Point { array: [112, 32] },
414      Point { array: [-87, -126] },
415      Point { array: [-120, -38] },
416      Point { array: [90, -111] },
417    ];
418    let ret = two_opt_moves(pts, &mut rand::rngs::SmallRng::seed_from_u64(0));
419
420    assert_eq!(ret.and_then(|val| val.validate()).err(), None);
421  }
422
423  #[test]
424  fn unit_2() {
425    let pts: Vec<Point<i8>> = vec![
426      Point { array: [-59, -36] },
427      Point { array: [-62, 88] },
428      Point { array: [8, 124] },
429      Point { array: [110, -81] },
430      Point { array: [-93, 27] },
431      Point { array: [96, 98] },
432      Point { array: [66, 87] },
433      Point { array: [-80, 20] },
434      Point { array: [-21, -17] },
435      Point { array: [-8, 21] },
436      Point { array: [0, -4] },
437      Point { array: [-63, 40] },
438      Point { array: [-24, 78] },
439      Point { array: [83, 23] },
440      Point { array: [0, 93] },
441      Point { array: [57, 52] },
442      Point { array: [-87, -17] },
443      Point { array: [38, 6] },
444      Point { array: [0, -118] },
445      Point {
446        array: [-101, -119],
447      },
448      Point { array: [-30, 90] },
449      Point { array: [0, -83] },
450      Point {
451        array: [-103, -112],
452      },
453      Point { array: [6, 75] },
454      Point { array: [65, -18] },
455      Point { array: [-126, 56] },
456      Point { array: [-86, 97] },
457      Point { array: [42, 44] },
458      Point { array: [-128, 23] },
459      Point { array: [-100, -53] },
460      Point { array: [-85, 96] },
461      Point { array: [120, 24] },
462      Point { array: [74, 98] },
463      Point { array: [63, -43] },
464      Point { array: [-42, 45] },
465      Point { array: [-2, 109] },
466      Point { array: [-107, -94] },
467      Point { array: [-12, 73] },
468      Point { array: [99, 86] },
469      Point { array: [62, 91] },
470      Point { array: [-84, 81] },
471      Point { array: [-128, 76] },
472      Point { array: [-27, -45] },
473      Point { array: [-56, 74] },
474      Point { array: [-2, -59] },
475      Point { array: [-65, 57] },
476      Point { array: [-9, 66] },
477      Point { array: [52, 40] },
478      Point { array: [13, -70] },
479      Point { array: [93, -1] },
480      Point { array: [47, 38] },
481      Point { array: [-85, -119] },
482      Point { array: [-91, 52] },
483      Point { array: [-107, 69] },
484      Point { array: [31, -97] },
485      Point { array: [118, 42] },
486      Point { array: [61, -85] },
487      Point { array: [0, 45] },
488      Point { array: [-128, -48] },
489      Point { array: [-94, 28] },
490      Point { array: [-86, -56] },
491      Point { array: [-128, 55] },
492      Point { array: [0, 58] },
493      Point { array: [75, -45] },
494      Point { array: [-76, -21] },
495      Point { array: [-10, -113] },
496      Point { array: [-96, -21] },
497      Point { array: [-84, 72] },
498      Point { array: [100, -26] },
499      Point { array: [-120, -50] },
500      Point { array: [-94, -19] },
501      Point { array: [17, -4] },
502      Point { array: [56, -23] },
503      Point { array: [11, 43] },
504      Point { array: [-14, 57] },
505      Point { array: [-42, -21] },
506      Point { array: [0, -95] },
507      Point { array: [8, 48] },
508      Point { array: [-21, -46] },
509      Point { array: [16, 81] },
510      Point { array: [0, 120] },
511      Point { array: [26, 27] },
512      Point { array: [-69, -44] },
513      Point { array: [97, 42] },
514    ];
515    let mut rng = StepRng::new(0, 0);
516    // let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
517    let ret = two_opt_moves(pts, &mut rng);
518
519    assert_eq!(ret.and_then(|val| val.validate()).err(), None);
520  }
521
522  #[test]
523  fn unit_3() {
524    let pts: Vec<Point<i8>> = vec![
525      Point { array: [0, 0] },
526      Point { array: [2, 0] },
527      Point { array: [1, 0] },
528      Point { array: [3, 0] },
529      Point { array: [3, 1] },
530      Point { array: [0, 1] },
531    ];
532    let mut rng = StepRng::new(0, 0);
533    let ret = two_opt_moves(pts, &mut rng);
534
535    assert_eq!(ret.and_then(|val| val.validate()).err(), None);
536  }
537
538  #[proptest]
539  fn points_to_polygon(#[strategy(vec(any::<Point<i8>>(), 3..100))] mut pts: Vec<Point<i8>>) {
540    let mut set = BTreeSet::new();
541    pts.retain(|pt| set.insert(*pt));
542    if pts.len() >= 3 && !Point::all_colinear(&pts) {
543      let mut rng = StepRng::new(0, 0);
544      let ret = two_opt_moves(pts, &mut rng);
545      prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
546    }
547  }
548
549  #[proptest]
550  fn f64_to_polygon(#[strategy(vec(any_nn(), 3..100))] mut pts: Vec<Point<NotNan<f64>>>) {
551    let mut set = BTreeSet::new();
552    pts.retain(|pt| set.insert(*pt));
553    if pts.len() >= 3 && !Point::all_colinear(&pts) {
554      let mut rng = StepRng::new(0, 0);
555      let ret = two_opt_moves(pts, &mut rng);
556      prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
557    }
558  }
559
560  #[proptest]
561  fn fuzz_f64(#[strategy(vec(any_nn(), 0..100))] pts: Vec<Point<NotNan<f64>>>) {
562    let mut rng = StepRng::new(0, 0);
563    two_opt_moves(pts, &mut rng).ok();
564  }
565
566  #[proptest]
567  fn fuzz_i8(#[strategy(vec(any::<Point<i8>>(), 0..100))] pts: Vec<Point<i8>>) {
568    let mut rng = StepRng::new(0, 0);
569    two_opt_moves(pts, &mut rng).ok();
570  }
571
572  #[proptest]
573  fn linear_fuzz(#[strategy(2..10_i8)] n: i8, seed: u64) {
574    let mut linear: Vec<i8> = (0..n).collect();
575    let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
576    linear.shuffle(&mut rng);
577    let mut pts: Vec<Point<i8>> = linear.iter().map(|&n| Point::new([n, 0])).collect();
578    pts.push(Point::new([0, 1]));
579
580    let mut rng = StepRng::new(0, 0);
581    // dbg!(&linear);
582    let ret = two_opt_moves(pts, &mut rng);
583    prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
584  }
585}