use crate::Intersects;
use crate::data::Cursor;
use crate::data::EndPoint;
use crate::data::IndexEdge;
use crate::data::LineSegmentView;
use crate::data::Point;
use crate::data::PointId;
use crate::data::Polygon;
use crate::data::{IndexIntersection, IndexIntersectionSet};
use crate::{Error, PolygonScalar};
use rand::Rng;
use std::collections::BTreeSet;
use std::ops::Bound::*;
use crate::Orientation;
pub fn resolve_self_intersections<T, R>(poly: &mut Polygon<T>, rng: &mut R) -> Result<(), Error>
where
T: PolygonScalar,
R: Rng + ?Sized,
{
assert_eq!(poly.rings.len(), 1);
if poly.iter_boundary().all(|pt| pt.is_colinear()) {
return Err(Error::CoLinearViolation);
}
let mut isects = IndexIntersectionSet::new(poly.iter_boundary().len());
for e1 in edges(poly) {
for e2 in edges(poly) {
if e1 < e2
&& let Some(isect) = intersects(poly, e1, e2)
{
isects.push(isect)
}
}
}
while let Some(isect) = isects.random(rng) {
untangle(poly, &mut isects, isect)
}
poly.ensure_ccw()?;
Ok(())
}
pub fn two_opt_moves<T, R>(pts: Vec<Point<T>>, rng: &mut R) -> Result<Polygon<T>, Error>
where
T: PolygonScalar,
R: Rng + ?Sized,
{
{
let mut seen = BTreeSet::new();
for pt in pts.iter() {
if !seen.insert(pt) {
return Err(Error::DuplicatePoints);
}
}
}
if pts.len() < 3 {
return Err(Error::InsufficientVertices);
}
let mut poly = Polygon::new_unchecked(pts);
resolve_self_intersections(&mut poly, rng)?;
Ok(poly)
}
fn endpoint<T>(a: PointId, b: PointId, c: PointId, t: T) -> EndPoint<T> {
if a == b || a == c {
EndPoint::Exclusive(t)
} else {
EndPoint::Inclusive(t)
}
}
fn intersects<T>(poly: &Polygon<T>, a: IndexEdge, b: IndexEdge) -> Option<IndexIntersection>
where
T: PolygonScalar,
{
let a_min = endpoint(a.min, b.min, b.max, poly.point(a.min));
let a_max = endpoint(a.max, b.min, b.max, poly.point(a.max));
let b_min = endpoint(b.min, a.min, a.max, poly.point(b.min));
let b_max = endpoint(b.max, a.min, a.max, poly.point(b.max));
let e1 = LineSegmentView::new(a_min, a_max);
let e2 = LineSegmentView::new(b_min, b_max);
e1.intersect(e2)?; Some(IndexIntersection::new(a, b))
}
fn untangle<T: PolygonScalar>(
poly: &mut Polygon<T>,
set: &mut IndexIntersectionSet,
isect: IndexIntersection,
) {
let da = poly.cursor(poly.direct(isect.min).src);
let db = poly.cursor(poly.direct(isect.max).src);
let inserted_edges;
if parallel_edges(da, db) {
let (a_min, a_max) = linear_extremes(da);
let (b_min, b_max) = linear_extremes(db);
let mut kinks = Vec::new();
for elt in a_min.to(Included(a_max)).chain(b_min.to(Included(b_max))) {
let inner_segment: LineSegmentView<T> = (elt.prev().point()..elt.next().point()).into();
if elt.orientation() != Orientation::CoLinear || !inner_segment.contains(elt.point()) {
kinks.push(elt);
}
}
let mut mergable = None;
'outer: for edge in a_min.to(Excluded(a_max)).chain(b_min.to(Excluded(b_max))) {
let segment = LineSegmentView::new(
EndPoint::Exclusive(edge.point()),
EndPoint::Exclusive(edge.next().point()),
);
for &kink in kinks.iter() {
if segment.contains(kink.point()) {
mergable = Some((kink, edge));
break 'outer;
}
}
}
let (kink, edge) = mergable.expect("There must be at least one mergable edge");
let del_edge_1 = IndexEdge::new(kink.prev().point_id(), kink.point_id());
let del_edge_2 = IndexEdge::new(kink.point_id(), kink.next().point_id());
let del_edge_3 = IndexEdge::new(edge.point_id(), edge.next().point_id());
set.remove_all(del_edge_1);
set.remove_all(del_edge_2);
set.remove_all(del_edge_3);
inserted_edges = vec![
IndexEdge::new(kink.prev().point_id(), kink.next().point_id()),
IndexEdge::new(edge.point_id(), kink.point_id()),
IndexEdge::new(kink.point_id(), edge.next().point_id()),
];
let p1 = edge.position;
let p2 = kink.position;
poly.vertices_join(p1, p2);
} else {
set.remove_all(IndexEdge::new(da.point_id(), da.next().point_id()));
set.remove_all(IndexEdge::new(db.point_id(), db.next().point_id()));
inserted_edges = vec![
IndexEdge::new(da.point_id(), db.point_id()),
IndexEdge::new(da.next().point_id(), db.next().point_id()),
];
let p1 = da.next().position;
let p2 = db.position;
poly.vertices_reverse(p1, p2);
}
for &edge in inserted_edges.iter() {
for e1 in edges(poly) {
if e1 != edge
&& let Some(isect) = intersects(poly, e1, edge)
{
set.push(isect)
}
}
}
}
#[allow(dead_code)]
#[cfg(not(tarpaulin_include))]
fn sanity_check<T: PolygonScalar>(poly: &Polygon<T>, isects: &IndexIntersectionSet) {
let naive_set = naive_intersection_set(poly);
let fast_set = isects.iter().collect();
let missing: Vec<&IndexIntersection> = naive_set.difference(&fast_set).collect();
let extra: Vec<&IndexIntersection> = fast_set.difference(&naive_set).collect();
if !missing.is_empty() {
panic!("Fast set is too small! {:?}", missing);
}
if !extra.is_empty() {
panic!("Fast set is too large! {:?}", extra);
}
}
#[cfg(not(tarpaulin_include))]
fn naive_intersection_set<T: PolygonScalar>(poly: &Polygon<T>) -> BTreeSet<IndexIntersection> {
let mut set = BTreeSet::new();
for e1 in edges(poly) {
for e2 in edges(poly) {
if e1 < e2
&& let Some(isect) = intersects(poly, e1, e2)
{
set.insert(isect);
}
}
}
set
}
fn parallel_edges<T>(a: Cursor<'_, T>, b: Cursor<'_, T>) -> bool
where
T: PolygonScalar,
{
let a1 = a.point();
let a2 = a.next().point();
let b1 = b.point();
let b2 = b.next().point();
Point::orient(a1, a2, b1).is_colinear() && Point::orient(a1, a2, b2).is_colinear()
}
fn linear_extremes<T>(at: Cursor<'_, T>) -> (Cursor<'_, T>, Cursor<'_, T>)
where
T: PolygonScalar,
{
let mut at_min = at;
let mut at_max = at;
at_max.move_next();
while at_min.is_colinear() {
at_min.move_prev();
}
while at_max.is_colinear() {
at_max.move_next();
}
(at_min, at_max)
}
fn edges<T>(poly: &Polygon<T>) -> impl Iterator<Item = IndexEdge> + '_ {
poly
.iter_boundary()
.map(|cursor| IndexEdge::new(cursor.point_id(), cursor.next().point_id()))
}
#[cfg(test)]
#[cfg(not(tarpaulin_include))]
pub mod tests {
use super::*;
use crate::testing::any_nn;
use proptest::collection::vec;
use proptest::prelude::*;
use rand::SeedableRng;
use rand::prelude::SliceRandom;
use test_strategy::proptest;
#[test]
fn unit_1() {
let pts: Vec<Point<i8>> = vec![
Point { array: [-71, 91] },
Point { array: [-17, -117] },
Point { array: [-13, 98] },
Point { array: [-84, 67] },
Point { array: [-12, -92] },
Point { array: [-95, 71] },
Point { array: [-81, -2] },
Point { array: [-91, -9] },
Point { array: [-42, -66] },
Point { array: [-107, 105] },
Point { array: [-49, 9] },
Point { array: [-96, 92] },
Point { array: [42, 11] },
Point { array: [-63, 56] },
Point { array: [122, -53] },
Point { array: [93, 29] },
Point { array: [-93, 89] },
Point { array: [40, -63] },
Point { array: [-127, -44] },
Point { array: [-108, 74] },
Point { array: [96, -5] },
Point { array: [46, 3] },
Point { array: [-103, -94] },
Point { array: [125, 73] },
Point { array: [104, 60] },
Point { array: [-55, -55] },
Point { array: [-112, -42] },
Point { array: [107, -16] },
Point { array: [38, -111] },
Point { array: [57, 123] },
Point { array: [-107, 108] },
Point { array: [46, -61] },
Point { array: [0, -35] },
Point { array: [35, -115] },
Point { array: [-120, 31] },
Point { array: [123, -87] },
Point { array: [-22, -87] },
Point { array: [-91, 27] },
Point { array: [101, -6] },
Point { array: [43, 6] },
Point { array: [-31, -73] },
Point {
array: [-107, -115],
},
Point { array: [-60, -98] },
Point { array: [-18, -94] },
Point { array: [52, -22] },
Point { array: [-71, -128] },
Point { array: [80, -26] },
Point { array: [104, -91] },
Point { array: [-91, 45] },
Point { array: [-79, -91] },
Point { array: [-47, -124] },
Point { array: [14, 101] },
Point { array: [-21, -69] },
Point { array: [16, 55] },
Point { array: [105, -76] },
Point { array: [-78, 39] },
Point { array: [80, -114] },
Point { array: [-6, 9] },
Point { array: [-65, -104] },
Point { array: [16, -1] },
Point { array: [122, -67] },
Point { array: [-93, -123] },
Point {
array: [-121, -120],
},
Point { array: [112, 32] },
Point { array: [-87, -126] },
Point { array: [-120, -38] },
Point { array: [90, -111] },
];
let ret = two_opt_moves(pts, &mut rand::rngs::SmallRng::seed_from_u64(0));
assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
#[test]
fn unit_2() {
let pts: Vec<Point<i8>> = vec![
Point { array: [-59, -36] },
Point { array: [-62, 88] },
Point { array: [8, 124] },
Point { array: [110, -81] },
Point { array: [-93, 27] },
Point { array: [96, 98] },
Point { array: [66, 87] },
Point { array: [-80, 20] },
Point { array: [-21, -17] },
Point { array: [-8, 21] },
Point { array: [0, -4] },
Point { array: [-63, 40] },
Point { array: [-24, 78] },
Point { array: [83, 23] },
Point { array: [0, 93] },
Point { array: [57, 52] },
Point { array: [-87, -17] },
Point { array: [38, 6] },
Point { array: [0, -118] },
Point {
array: [-101, -119],
},
Point { array: [-30, 90] },
Point { array: [0, -83] },
Point {
array: [-103, -112],
},
Point { array: [6, 75] },
Point { array: [65, -18] },
Point { array: [-126, 56] },
Point { array: [-86, 97] },
Point { array: [42, 44] },
Point { array: [-128, 23] },
Point { array: [-100, -53] },
Point { array: [-85, 96] },
Point { array: [120, 24] },
Point { array: [74, 98] },
Point { array: [63, -43] },
Point { array: [-42, 45] },
Point { array: [-2, 109] },
Point { array: [-107, -94] },
Point { array: [-12, 73] },
Point { array: [99, 86] },
Point { array: [62, 91] },
Point { array: [-84, 81] },
Point { array: [-128, 76] },
Point { array: [-27, -45] },
Point { array: [-56, 74] },
Point { array: [-2, -59] },
Point { array: [-65, 57] },
Point { array: [-9, 66] },
Point { array: [52, 40] },
Point { array: [13, -70] },
Point { array: [93, -1] },
Point { array: [47, 38] },
Point { array: [-85, -119] },
Point { array: [-91, 52] },
Point { array: [-107, 69] },
Point { array: [31, -97] },
Point { array: [118, 42] },
Point { array: [61, -85] },
Point { array: [0, 45] },
Point { array: [-128, -48] },
Point { array: [-94, 28] },
Point { array: [-86, -56] },
Point { array: [-128, 55] },
Point { array: [0, 58] },
Point { array: [75, -45] },
Point { array: [-76, -21] },
Point { array: [-10, -113] },
Point { array: [-96, -21] },
Point { array: [-84, 72] },
Point { array: [100, -26] },
Point { array: [-120, -50] },
Point { array: [-94, -19] },
Point { array: [17, -4] },
Point { array: [56, -23] },
Point { array: [11, 43] },
Point { array: [-14, 57] },
Point { array: [-42, -21] },
Point { array: [0, -95] },
Point { array: [8, 48] },
Point { array: [-21, -46] },
Point { array: [16, 81] },
Point { array: [0, 120] },
Point { array: [26, 27] },
Point { array: [-69, -44] },
Point { array: [97, 42] },
];
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let ret = two_opt_moves(pts, &mut rng);
assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
#[test]
fn unit_3() {
let pts: Vec<Point<i8>> = vec![
Point { array: [0, 0] },
Point { array: [2, 0] },
Point { array: [1, 0] },
Point { array: [3, 0] },
Point { array: [3, 1] },
Point { array: [0, 1] },
];
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let ret = two_opt_moves(pts, &mut rng);
assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
#[proptest]
fn points_to_polygon(#[strategy(vec(any::<Point<i8>>(), 3..100))] mut pts: Vec<Point<i8>>) {
let mut set = BTreeSet::new();
pts.retain(|pt| set.insert(*pt));
if pts.len() >= 3 && !Point::all_colinear(&pts) {
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let ret = two_opt_moves(pts, &mut rng);
prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
}
#[proptest]
fn f64_to_polygon(#[strategy(vec(any_nn(), 3..100))] mut pts: Vec<Point<f64>>) {
let mut set = BTreeSet::new();
pts.retain(|pt| set.insert(*pt));
if pts.len() >= 3 && !Point::all_colinear(&pts) {
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let ret = two_opt_moves(pts, &mut rng);
prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
}
#[proptest]
fn fuzz_f64(#[strategy(vec(any_nn(), 0..100))] pts: Vec<Point<f64>>) {
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
two_opt_moves(pts, &mut rng).ok();
}
#[proptest]
fn fuzz_i8(#[strategy(vec(any::<Point<i8>>(), 0..100))] pts: Vec<Point<i8>>) {
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
two_opt_moves(pts, &mut rng).ok();
}
#[proptest]
fn linear_fuzz(#[strategy(2..10_i8)] n: i8, seed: u64) {
let mut linear: Vec<i8> = (0..n).collect();
let mut rng = rand::rngs::SmallRng::seed_from_u64(seed);
linear.shuffle(&mut rng);
let mut pts: Vec<Point<i8>> = linear.iter().map(|&n| Point::new([n, 0])).collect();
pts.push(Point::new([0, 1]));
let mut rng = rand::rngs::SmallRng::seed_from_u64(0);
let ret = two_opt_moves(pts, &mut rng);
prop_assert_eq!(ret.and_then(|val| val.validate()).err(), None);
}
}