diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index ce5bb2338..a870f1ff7 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -318,7 +318,7 @@ where return false; } - let mut mp_zipper = self.into_iter().zip(other.into_iter()); + let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index 5cc84b515..0e05673af 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -189,7 +189,7 @@ where return false; } - let mut mp_zipper = self.into_iter().zip(other.into_iter()); + let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } diff --git a/geo-types/src/geometry/multi_point.rs b/geo-types/src/geometry/multi_point.rs index c59622013..d28e59d8e 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -170,7 +170,7 @@ where return false; } - let mut mp_zipper = self.into_iter().zip(other.into_iter()); + let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } diff --git a/geo-types/src/geometry/multi_polygon.rs b/geo-types/src/geometry/multi_polygon.rs index 15116353e..86f68768d 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -166,7 +166,7 @@ where return false; } - let mut mp_zipper = self.into_iter().zip(other.into_iter()); + let mut mp_zipper = self.into_iter().zip(other); mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon)) } } diff --git a/geo/src/algorithm/monotone/builder.rs b/geo/src/algorithm/monotone/builder.rs index bb33e82ae..2e1328f12 100644 --- a/geo/src/algorithm/monotone/builder.rs +++ b/geo/src/algorithm/monotone/builder.rs @@ -2,7 +2,7 @@ //! //! This implementation is based on these awesome [lecture notes] by David //! Mount. The broad idea is to run a left-right planar sweep on the segments -//! of the polygon and try to iteratively extend parallel monotone chains. +//! of the polygon and try to iteratively extend parallel monotone chains. //! //! [lecture notes]: //! //www.cs.umd.edu/class/spring2020/cmsc754/Lects/lect05-triangulate.pdf @@ -41,7 +41,7 @@ impl Builder { let (ext, ints) = polygon.into_inner(); Some(ext) .into_iter() - .chain(ints.into_iter()) + .chain(ints) .flat_map(|ls| -> Vec<_> { ls.lines().collect() }) .filter_map(|line| { if line.start == line.end { diff --git a/geo/src/algorithm/sweep/active.rs b/geo/src/algorithm/sweep/active.rs index ba9757d12..f051a79d9 100644 --- a/geo/src/algorithm/sweep/active.rs +++ b/geo/src/algorithm/sweep/active.rs @@ -1,10 +1,4 @@ -use std::{ - borrow::Borrow, - cmp::Ordering, - collections::BTreeSet, - fmt::Debug, - ops::{Bound, Deref}, -}; +use std::{borrow::Borrow, cmp::Ordering, fmt::Debug, ops::Deref}; /// A segment currently active in the sweep. /// @@ -86,41 +80,3 @@ pub(in crate::algorithm) trait ActiveSet: Default { fn insert_active(&mut self, segment: Self::Seg); fn remove_active(&mut self, segment: &Self::Seg); } - -impl ActiveSet for BTreeSet> { - type Seg = T; - - fn previous_find) -> bool>( - &self, - segment: &Self::Seg, - mut f: F, - ) -> Option<&Active> { - self.range::, _>(( - Bound::Unbounded, - Bound::Excluded(Active::active_ref(segment)), - )) - .rev() - .find(|&a| f(a)) - } - fn next_find) -> bool>( - &self, - segment: &Self::Seg, - mut f: F, - ) -> Option<&Active> { - self.range::, _>(( - Bound::Excluded(Active::active_ref(segment)), - Bound::Unbounded, - )) - .find(|&a| f(a)) - } - - fn insert_active(&mut self, segment: Self::Seg) { - let result = self.insert(Active(segment)); - debug_assert!(result); - } - - fn remove_active(&mut self, segment: &Self::Seg) { - let result = self.remove(Active::active_ref(segment)); - debug_assert!(result); - } -} diff --git a/geo/src/algorithm/sweep/line_or_point.rs b/geo/src/algorithm/sweep/line_or_point.rs index d87d261b5..7bd26f935 100644 --- a/geo/src/algorithm/sweep/line_or_point.rs +++ b/geo/src/algorithm/sweep/line_or_point.rs @@ -12,38 +12,42 @@ use crate::{ /// segment must have distinct points (use the `Point` variant if the /// coordinates are the equal). #[derive(Clone, Copy)] -pub struct LineOrPoint { - left: SweepPoint, - right: SweepPoint, +pub enum LineOrPoint { + Point(SweepPoint), + Line { + left: SweepPoint, + right: SweepPoint, + }, } impl std::fmt::Debug for LineOrPoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple(if self.is_line() { "LPt" } else { "Pt" }) - .field(&self.left.x_y()) - .field(&self.right.x_y()) - .finish() + match self { + LineOrPoint::Point(p) => f.debug_tuple("Pt").field(&p.x_y()).finish(), + LineOrPoint::Line { left, right } => f + .debug_tuple("LPt") + .field(&left.x_y()) + .field(&right.x_y()) + .finish(), + } } } impl From> for LineOrPoint { fn from(pt: SweepPoint) -> Self { - Self { - left: pt, - right: pt, - } + Self::Point(pt) } } impl From<(SweepPoint, SweepPoint)> for LineOrPoint { - fn from(pt: (SweepPoint, SweepPoint)) -> Self { - let (start, end) = pt; + fn from((start, end): (SweepPoint, SweepPoint)) -> Self { match start.cmp(&end) { - Ordering::Less => Self { + Ordering::Less => Self::Line { left: start, right: end, }, - _ => Self { + Ordering::Equal => Self::Point(start), + Ordering::Greater => Self::Line { left: end, right: start, }, @@ -63,10 +67,7 @@ impl From> for LineOrPoint { /// Convert from a [`Coord`] impl From> for LineOrPoint { fn from(c: Coord) -> Self { - Self { - left: c.into(), - right: c.into(), - } + Self::Point(c.into()) } } @@ -74,23 +75,32 @@ impl LineOrPoint { /// Checks if the variant is a line. #[inline] pub fn is_line(&self) -> bool { - self.left != self.right + matches!(self, Self::Line { .. }) } /// Return a [`Line`] representation of self. #[inline] pub fn line(&self) -> Line { - Line::new(*self.left, *self.right) + match self { + LineOrPoint::Point(p) => Line::new(**p, **p), + LineOrPoint::Line { left, right } => Line::new(**left, **right), + } } #[inline] pub fn left(&self) -> SweepPoint { - self.left + match self { + LineOrPoint::Point(p) => *p, + LineOrPoint::Line { left, .. } => *left, + } } #[inline] pub fn right(&self) -> SweepPoint { - self.right + match self { + LineOrPoint::Point(p) => *p, + LineOrPoint::Line { right, .. } => *right, + } } #[cfg(test)] @@ -100,15 +110,26 @@ impl LineOrPoint { #[inline] pub fn end_points(&self) -> (SweepPoint, SweepPoint) { - (self.left, self.right) + match self { + LineOrPoint::Point(p) => (*p, *p), + LineOrPoint::Line { left, right } => (*left, *right), + } } pub fn new(left: SweepPoint, right: SweepPoint) -> Self { - Self { left, right } + if left == right { + Self::Point(left) + } else { + Self::Line { left, right } + } } pub fn orient2d(&self, other: Coord) -> Orientation { - T::Ker::orient2d(*self.left, *self.right, other) + let (left, right) = match self { + LineOrPoint::Point(p) => (**p, **p), + LineOrPoint::Line { left, right } => (**left, **right), + }; + T::Ker::orient2d(left, right, other) } } @@ -131,9 +152,9 @@ impl PartialEq for LineOrPoint { /// centered at its coordinates. impl PartialOrd for LineOrPoint { fn partial_cmp(&self, other: &Self) -> Option { - match (self.is_line(), other.is_line()) { - (false, false) => { - if self.left == other.left { + match (self, other) { + (LineOrPoint::Point(p), LineOrPoint::Point(o)) => { + if p == o { Some(Ordering::Equal) } else { // Unequal points do not satisfy pre-condition and @@ -141,35 +162,44 @@ impl PartialOrd for LineOrPoint { None } } - (false, true) => other.partial_cmp(self).map(Ordering::reverse), - (true, false) => { - let (p, q) = self.end_points(); - let r = other.left; - if r > q || p > r { + (LineOrPoint::Point(_), LineOrPoint::Line { .. }) => { + other.partial_cmp(self).map(Ordering::reverse) + } + (LineOrPoint::Line { left, right }, LineOrPoint::Point(p)) => { + if p > right || left > p { return None; } Some( - T::Ker::orient2d(*p, *q, *r) + T::Ker::orient2d(**left, **right, **p) .as_ordering() .then(Ordering::Greater), ) } - (true, true) => { - let (p1, q1) = self.end_points(); - let (p2, q2) = other.end_points(); - if p1 > p2 { + ( + LineOrPoint::Line { + left: left_a, + right: right_a, + }, + LineOrPoint::Line { + left: left_b, + right: right_b, + }, + ) => { + if left_a > left_b { return other.partial_cmp(self).map(Ordering::reverse); } - if p1 >= q2 || p2 >= q1 { + if left_a >= right_b || left_b >= right_a { return None; } // Assertion: p1 <= p2 // Assertion: pi < q_j Some( - T::Ker::orient2d(*p1, *q1, *p2) + T::Ker::orient2d(**left_a, **right_a, **left_b) .as_ordering() - .then_with(|| T::Ker::orient2d(*p1, *q1, *q2).as_ordering()), + .then_with(|| { + T::Ker::orient2d(**left_a, **right_a, **right_b).as_ordering() + }), ) } } @@ -184,39 +214,41 @@ impl LineOrPoint { debug_assert!(other.is_line(), "tried to intersect with a point variant!"); let line = other.line(); - if !self.is_line() { - let p = self.left; - use crate::Intersects; - if line.intersects(&*p) { - Some(*self) - } else { - None + match self { + LineOrPoint::Point(p) => { + use crate::Intersects; + if line.intersects(&**p) { + Some(*self) + } else { + None + } } - } else { - line_intersection(self.line(), line).map(|l| match l { - LineIntersection::SinglePoint { - intersection, - is_proper, - } => { - let mut pt = intersection; - if is_proper && (&pt == self.left.deref()) { - if self.left.x == self.right.x { - pt.y = pt.y.next_after(T::infinity()); - } else { - pt.x = pt.x.next_after(T::infinity()); + LineOrPoint::Line { left, right } => { + line_intersection(self.line(), line).map(|l| match l { + LineIntersection::SinglePoint { + intersection, + is_proper, + } => { + let mut pt = intersection; + if is_proper && (&pt == left.deref()) { + if left.x == right.x { + pt.y = pt.y.next_after(T::infinity()); + } else { + pt.x = pt.x.next_after(T::infinity()); + } } + pt.into() } - pt.into() - } - LineIntersection::Collinear { intersection } => intersection.into(), - }) + LineIntersection::Collinear { intersection } => intersection.into(), + }) + } } } pub fn intersect_line_ordered(&self, other: &Self) -> Option { let ord = self.partial_cmp(other); match self.intersect_line(other) { - Some(lp) if !lp.is_line() => { + Some(Self::Point(p)) => { // NOTE: A key issue with using non-exact numbers (f64, etc.) in // this algo. is that line-intersection may return // counter-intuitive points. @@ -243,38 +275,34 @@ impl LineOrPoint { // specifically for this algo., that can track the neighbors of // tree-nodes, and fix / report this issue. The crate // `btree-slab` seems like a great starting point. - let pt = lp.left; - let (mut x, y) = pt.x_y(); + let (mut x, y) = p.x_y(); - let c = self.left; + let c = self.left(); if x == c.x && y < c.y { x = x.next_after(T::infinity()); } - let pt: SweepPoint<_> = Coord { x, y }.into(); + let p = Coord { x, y }.into(); debug_assert!( - pt >= self.left, - "line intersection before first line: {pt:?}\n\tLine({lp1:?} - {lp2:?}) X Line({lp3:?} - {lp4:?})", - lp1 = self.left, - lp2 = self.right, - lp3 = other.left, - lp4 = other.right, + p >= self.left(), + "line intersection before first line: {p:?}\n\tLine({lp1:?} - {lp2:?}) X Line({lp3:?} - {lp4:?})", + lp1 = self.left(), + lp2 = self.right(), + lp3 = other.left(), + lp4 = other.right(), ); debug_assert!( - pt >= other.left, - "line intersection before second line: {pt:?}\n\tLine({lp1:?} - {lp2:?}) X Line({lp3:?} - {lp4:?})", - lp1 = self.left, - lp2 = self.right, - lp3 = other.left, - lp4 = other.right, + p >= other.left(), + "line intersection before second line: {p:?}\n\tLine({lp1:?} - {lp2:?}) X Line({lp3:?} - {lp4:?})", + lp1 = self.left(), + lp2 = self.right(), + lp3 = other.left(), + lp4 = other.right(), ); if let Some(ord) = ord { - let l1 = LineOrPoint::from((self.left, pt)); - let l2 = LineOrPoint { - left: other.left, - right: pt, - }; + let l1 = LineOrPoint::from((self.left(), p)); + let l2 = LineOrPoint::from((other.left(), p)); let cmp = l1.partial_cmp(&l2).unwrap(); if l1.is_line() && l2.is_line() && cmp.then(ord) != ord { debug!( @@ -283,18 +311,18 @@ impl LineOrPoint { l2 = other ); debug!("\tparts: {l1:?}, {l2:?}"); - debug!("\tintersection: {pt:?} {cmp:?}"); + debug!("\tintersection: {p:?} {cmp:?}"); // RM: This is a complicated intersection that is changing the ordering. // Heuristic: approximate with a trivial intersection point that preserves the topology. - return Some(if self.left > other.left { - self.left.into() + return Some(if self.left() > other.left() { + self.left().into() } else { - other.left.into() + other.left().into() }); } } - Some((*pt).into()) + Some(Self::Point(p)) } e => e, }