1#![allow(
20 clippy::float_cmp,
21 reason = "exact equality identifies stored endpoint identity for DE-9IM boundary classification"
22)]
23
24use alloc::vec::Vec;
25
26use geometry_coords::{CoordinateScalar, precise_math};
27use geometry_cs::{Cartesian, CartesianFamily, CoordinateSystem};
28use geometry_model::{DynGeometry, Point2D, Polygon, Ring};
29use geometry_tag::{
30 BoxTag, DynamicGeometryTag, GeometryCollectionTag, LinestringTag, MultiLinestringTag,
31 MultiPointTag, MultiPolygonTag, PointTag, PolygonTag, RingTag, SameAs, SegmentTag,
32};
33use geometry_trait::{
34 Box as BoxTrait, Geometry, GeometryCollection, Linestring as LinestringTrait, MultiLinestring,
35 MultiPoint, MultiPolygon, Point, PointMut, Polygon as PolygonTrait, Ring as RingTrait,
36 Segment as SegmentTrait, corner,
37};
38
39use crate::operation::OverlayError;
40use crate::predicate::range_guard::{SAFE_ABS_MAX, polygon_in_range};
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum Dimension {
48 Empty,
50 Point,
52 Curve,
54 Area,
56}
57
58impl Dimension {
59 #[must_use]
62 pub fn is_set(self) -> bool {
63 self != Dimension::Empty
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct De9im {
75 pub m: [[Dimension; 3]; 3],
77}
78
79pub mod feature {
81 pub const INTERIOR: usize = 0;
83 pub const BOUNDARY: usize = 1;
85 pub const EXTERIOR: usize = 2;
87}
88
89impl De9im {
90 #[must_use]
92 pub fn transposed(self) -> Self {
93 let mut result = [[Dimension::Empty; 3]; 3];
94 for (row, cells) in self.m.iter().enumerate() {
95 for (column, dimension) in cells.iter().enumerate() {
96 result[column][row] = *dimension;
97 }
98 }
99 Self { m: result }
100 }
101
102 #[must_use]
105 pub fn interior_interior(&self) -> Dimension {
106 self.m[feature::INTERIOR][feature::INTERIOR]
107 }
108
109 #[must_use]
111 pub fn boundary_boundary(&self) -> Dimension {
112 self.m[feature::BOUNDARY][feature::BOUNDARY]
113 }
114
115 #[must_use]
118 pub fn interior_exterior(&self) -> Dimension {
119 self.m[feature::INTERIOR][feature::EXTERIOR]
120 }
121
122 #[must_use]
124 pub fn exterior_interior(&self) -> Dimension {
125 self.m[feature::EXTERIOR][feature::INTERIOR]
126 }
127
128 pub fn matches(&self, mask: &str) -> Result<bool, RelateError> {
140 let bytes = mask.as_bytes();
141 if bytes.len() != 9 {
142 return Err(RelateError::InvalidMask);
143 }
144
145 let mut result = true;
146 for (dimension, expected) in self.m.iter().flatten().zip(bytes) {
147 result &= match expected {
148 b'*' => true,
149 b'T' => dimension.is_set(),
150 b'F' => *dimension == Dimension::Empty,
151 b'0' => *dimension == Dimension::Point,
152 b'1' => *dimension == Dimension::Curve,
153 b'2' => *dimension == Dimension::Area,
154 _ => return Err(RelateError::InvalidMask),
155 };
156 }
157 Ok(result)
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum RelateError {
168 Overlay(OverlayError),
171 InvalidMask,
173}
174
175impl From<OverlayError> for RelateError {
178 fn from(error: OverlayError) -> Self {
179 Self::Overlay(error)
180 }
181}
182
183#[doc(hidden)]
188pub trait RelateStrategy<A, B> {
189 fn relate(&self, first: &A, second: &B) -> Result<De9im, OverlayError>;
191}
192
193#[doc(hidden)]
195pub trait RelatePairStrategy<Other> {
196 type Strategy: Default;
198}
199
200#[doc(hidden)]
201#[derive(Debug, Default, Clone, Copy)]
202pub struct RelatePointPoint;
203#[doc(hidden)]
204#[derive(Debug, Default, Clone, Copy)]
205pub struct RelatePointLinestring;
206#[doc(hidden)]
207#[derive(Debug, Default, Clone, Copy)]
208pub struct RelatePointPolygon;
209#[doc(hidden)]
210#[derive(Debug, Default, Clone, Copy)]
211pub struct RelateLinestringPoint;
212#[doc(hidden)]
213#[derive(Debug, Default, Clone, Copy)]
214pub struct RelateLinestringLinestring;
215#[doc(hidden)]
216#[derive(Debug, Default, Clone, Copy)]
217pub struct RelateLinestringPolygon;
218#[doc(hidden)]
219#[derive(Debug, Default, Clone, Copy)]
220pub struct RelatePolygonPoint;
221#[doc(hidden)]
222#[derive(Debug, Default, Clone, Copy)]
223pub struct RelatePolygonLinestring;
224#[doc(hidden)]
225#[derive(Debug, Default, Clone, Copy)]
226pub struct RelatePolygonPolygon;
227#[doc(hidden)]
228#[derive(Debug, Default, Clone, Copy)]
229pub struct RelateTopology;
230
231impl RelatePairStrategy<PointTag> for PointTag {
232 type Strategy = RelatePointPoint;
233}
234impl RelatePairStrategy<LinestringTag> for PointTag {
235 type Strategy = RelatePointLinestring;
236}
237impl RelatePairStrategy<PolygonTag> for PointTag {
238 type Strategy = RelatePointPolygon;
239}
240impl RelatePairStrategy<PointTag> for LinestringTag {
241 type Strategy = RelateLinestringPoint;
242}
243impl RelatePairStrategy<LinestringTag> for LinestringTag {
244 type Strategy = RelateLinestringLinestring;
245}
246impl RelatePairStrategy<PolygonTag> for LinestringTag {
247 type Strategy = RelateLinestringPolygon;
248}
249impl RelatePairStrategy<PointTag> for PolygonTag {
250 type Strategy = RelatePolygonPoint;
251}
252impl RelatePairStrategy<LinestringTag> for PolygonTag {
253 type Strategy = RelatePolygonLinestring;
254}
255impl RelatePairStrategy<PolygonTag> for PolygonTag {
256 type Strategy = RelatePolygonPolygon;
257}
258
259trait TopologyKind {}
260
261impl TopologyKind for PointTag {}
262impl TopologyKind for LinestringTag {}
263impl TopologyKind for PolygonTag {}
264impl TopologyKind for SegmentTag {}
265impl TopologyKind for RingTag {}
266impl TopologyKind for BoxTag {}
267impl TopologyKind for MultiPointTag {}
268impl TopologyKind for MultiLinestringTag {}
269impl TopologyKind for MultiPolygonTag {}
270impl TopologyKind for DynamicGeometryTag {}
271impl TopologyKind for GeometryCollectionTag {}
272
273macro_rules! topology_pair_for_single {
274 ($single:ty, $($other:ty),+ $(,)?) => {
275 $(
276 impl RelatePairStrategy<$other> for $single {
277 type Strategy = RelateTopology;
278 }
279 )+
280 };
281}
282
283topology_pair_for_single!(
284 PointTag,
285 SegmentTag,
286 RingTag,
287 BoxTag,
288 MultiPointTag,
289 MultiLinestringTag,
290 MultiPolygonTag,
291 DynamicGeometryTag,
292 GeometryCollectionTag,
293);
294topology_pair_for_single!(
295 LinestringTag,
296 SegmentTag,
297 RingTag,
298 BoxTag,
299 MultiPointTag,
300 MultiLinestringTag,
301 MultiPolygonTag,
302 DynamicGeometryTag,
303 GeometryCollectionTag,
304);
305topology_pair_for_single!(
306 PolygonTag,
307 SegmentTag,
308 RingTag,
309 BoxTag,
310 MultiPointTag,
311 MultiLinestringTag,
312 MultiPolygonTag,
313 DynamicGeometryTag,
314 GeometryCollectionTag,
315);
316
317impl<Other: TopologyKind> RelatePairStrategy<Other> for SegmentTag {
318 type Strategy = RelateTopology;
319}
320impl<Other: TopologyKind> RelatePairStrategy<Other> for RingTag {
321 type Strategy = RelateTopology;
322}
323impl<Other: TopologyKind> RelatePairStrategy<Other> for BoxTag {
324 type Strategy = RelateTopology;
325}
326impl<Other: TopologyKind> RelatePairStrategy<Other> for MultiPointTag {
327 type Strategy = RelateTopology;
328}
329impl<Other: TopologyKind> RelatePairStrategy<Other> for MultiLinestringTag {
330 type Strategy = RelateTopology;
331}
332impl<Other: TopologyKind> RelatePairStrategy<Other> for MultiPolygonTag {
333 type Strategy = RelateTopology;
334}
335impl<Other: TopologyKind> RelatePairStrategy<Other> for DynamicGeometryTag {
336 type Strategy = RelateTopology;
337}
338impl<Other: TopologyKind> RelatePairStrategy<Other> for GeometryCollectionTag {
339 type Strategy = RelateTopology;
340}
341
342type PairStrategy<A, B> =
343 <<A as Geometry>::Kind as RelatePairStrategy<<B as Geometry>::Kind>>::Strategy;
344
345#[inline]
356#[must_use = "relation computation can fail and the matrix should be used"]
357pub fn relate<A, B>(first: &A, second: &B) -> Result<De9im, OverlayError>
358where
359 A: Geometry,
360 B: Geometry,
361 A::Kind: RelatePairStrategy<B::Kind>,
362 PairStrategy<A, B>: RelateStrategy<A, B> + Default,
363{
364 PairStrategy::<A, B>::default().relate(first, second)
365}
366
367impl<A, B> RelateStrategy<A, B> for RelatePointPoint
368where
369 A: Point,
370 B: Point<Scalar = A::Scalar>,
371 <A::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
372 <B::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
373{
374 fn relate(&self, first: &A, second: &B) -> Result<De9im, OverlayError> {
375 Ok(relate_point_point(first, second))
376 }
377}
378
379impl<P, L> RelateStrategy<P, L> for RelatePointLinestring
380where
381 P: Point,
382 L: LinestringTrait<Point = P>,
383 P::Scalar: Into<f64>,
384 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
385{
386 fn relate(&self, point: &P, line: &L) -> Result<De9im, OverlayError> {
387 Ok(relate_point_linestring(point, line))
388 }
389}
390
391impl<P, G> RelateStrategy<P, G> for RelatePointPolygon
392where
393 P: Point + Copy,
394 G: PolygonTrait<Point = P>,
395 P::Scalar: Into<f64>,
396 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
397{
398 fn relate(&self, point: &P, polygon: &G) -> Result<De9im, OverlayError> {
399 Ok(relate_point_polygon(point, polygon))
400 }
401}
402
403impl<L, P> RelateStrategy<L, P> for RelateLinestringPoint
404where
405 P: Point,
406 L: LinestringTrait<Point = P>,
407 P::Scalar: Into<f64>,
408 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
409{
410 fn relate(&self, line: &L, point: &P) -> Result<De9im, OverlayError> {
411 Ok(relate_point_linestring(point, line).transposed())
412 }
413}
414
415impl<A, B, P> RelateStrategy<A, B> for RelateLinestringLinestring
416where
417 A: LinestringTrait<Point = P>,
418 B: LinestringTrait<Point = P>,
419 P: Point,
420 P::Scalar: Into<f64>,
421 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
422{
423 fn relate(&self, first: &A, second: &B) -> Result<De9im, OverlayError> {
424 Ok(relate_linestring_linestring(first, second))
425 }
426}
427
428impl<L, G, P> RelateStrategy<L, G> for RelateLinestringPolygon
429where
430 L: LinestringTrait<Point = P>,
431 G: PolygonTrait<Point = P>,
432 P: Point + Copy,
433 P::Scalar: Into<f64>,
434 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
435{
436 fn relate(&self, line: &L, polygon: &G) -> Result<De9im, OverlayError> {
437 Ok(relate_linestring_polygon(line, polygon))
438 }
439}
440
441impl<G, P> RelateStrategy<G, P> for RelatePolygonPoint
442where
443 G: PolygonTrait<Point = P>,
444 P: Point + Copy,
445 P::Scalar: Into<f64>,
446 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
447{
448 fn relate(&self, polygon: &G, point: &P) -> Result<De9im, OverlayError> {
449 Ok(relate_point_polygon(point, polygon).transposed())
450 }
451}
452
453impl<G, L, P> RelateStrategy<G, L> for RelatePolygonLinestring
454where
455 G: PolygonTrait<Point = P>,
456 L: LinestringTrait<Point = P>,
457 P: Point + Copy,
458 P::Scalar: Into<f64>,
459 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
460{
461 fn relate(&self, polygon: &G, line: &L) -> Result<De9im, OverlayError> {
462 Ok(relate_linestring_polygon(line, polygon).transposed())
463 }
464}
465
466impl<A, B, P> RelateStrategy<A, B> for RelatePolygonPolygon
467where
468 A: PolygonTrait<Point = P>,
469 B: PolygonTrait<Point = P>,
470 P: PointMut + Default + Copy,
471 P::Scalar: CoordinateScalar + Into<f64>,
472 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
473{
474 fn relate(&self, first: &A, second: &B) -> Result<De9im, OverlayError> {
475 relate_polygon_polygon(first, second)
476 }
477}
478
479type TopologyPointModel = Point2D<f64, Cartesian>;
480
481#[derive(Debug, Default, Clone)]
482struct Topology {
483 points: Vec<[f64; 2]>,
484 lines: Vec<Vec<[f64; 2]>>,
485 polygons: Vec<Polygon<TopologyPointModel>>,
486}
487
488impl Topology {
489 fn in_range(&self) -> bool {
490 let in_range = |point: [f64; 2]| {
491 point[0].is_finite()
492 && point[1].is_finite()
493 && point[0].abs() <= SAFE_ABS_MAX
494 && point[1].abs() <= SAFE_ABS_MAX
495 };
496 if !self
497 .points
498 .iter()
499 .chain(self.lines.iter().flatten())
500 .copied()
501 .all(in_range)
502 {
503 return false;
504 }
505 self.polygons.iter().all(|polygon| {
506 polygon
507 .outer
508 .0
509 .iter()
510 .chain(polygon.inners.iter().flat_map(|ring| ring.0.iter()))
511 .all(|point| in_range([point.x(), point.y()]))
512 })
513 }
514}
515
516trait TopologyBuilder<G> {
517 fn append(&self, geometry: &G, topology: &mut Topology);
518}
519
520trait TopologyBuilderForKind {
521 type Strategy: Default;
522}
523
524#[derive(Debug, Default, Clone, Copy)]
525struct TopologyPoint;
526#[derive(Debug, Default, Clone, Copy)]
527struct TopologyLinestring;
528#[derive(Debug, Default, Clone, Copy)]
529struct TopologyPolygon;
530#[derive(Debug, Default, Clone, Copy)]
531struct TopologySegment;
532#[derive(Debug, Default, Clone, Copy)]
533struct TopologyRing;
534#[derive(Debug, Default, Clone, Copy)]
535struct TopologyBox;
536#[derive(Debug, Default, Clone, Copy)]
537struct TopologyMultiPoint;
538#[derive(Debug, Default, Clone, Copy)]
539struct TopologyMultiLinestring;
540#[derive(Debug, Default, Clone, Copy)]
541struct TopologyMultiPolygon;
542#[derive(Debug, Default, Clone, Copy)]
543struct TopologyDynamic;
544#[derive(Debug, Default, Clone, Copy)]
545struct TopologyCollection;
546
547impl TopologyBuilderForKind for PointTag {
548 type Strategy = TopologyPoint;
549}
550impl TopologyBuilderForKind for LinestringTag {
551 type Strategy = TopologyLinestring;
552}
553impl TopologyBuilderForKind for PolygonTag {
554 type Strategy = TopologyPolygon;
555}
556impl TopologyBuilderForKind for SegmentTag {
557 type Strategy = TopologySegment;
558}
559impl TopologyBuilderForKind for RingTag {
560 type Strategy = TopologyRing;
561}
562impl TopologyBuilderForKind for BoxTag {
563 type Strategy = TopologyBox;
564}
565impl TopologyBuilderForKind for MultiPointTag {
566 type Strategy = TopologyMultiPoint;
567}
568impl TopologyBuilderForKind for MultiLinestringTag {
569 type Strategy = TopologyMultiLinestring;
570}
571impl TopologyBuilderForKind for MultiPolygonTag {
572 type Strategy = TopologyMultiPolygon;
573}
574impl TopologyBuilderForKind for DynamicGeometryTag {
575 type Strategy = TopologyDynamic;
576}
577impl TopologyBuilderForKind for GeometryCollectionTag {
578 type Strategy = TopologyCollection;
579}
580
581type TopologyBuilderStrategy<G> = <<G as Geometry>::Kind as TopologyBuilderForKind>::Strategy;
582
583impl<G> TopologyBuilder<G> for TopologyPoint
584where
585 G: Point,
586 G::Scalar: Into<f64>,
587 <G::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
588{
589 fn append(&self, geometry: &G, topology: &mut Topology) {
590 topology.points.push(xy(geometry));
591 }
592}
593
594impl<G> TopologyBuilder<G> for TopologyLinestring
595where
596 G: LinestringTrait,
597 <G::Point as Point>::Scalar: Into<f64>,
598 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
599{
600 fn append(&self, geometry: &G, topology: &mut Topology) {
601 append_topology_line(geometry.points().map(xy).collect(), topology);
602 }
603}
604
605impl<G> TopologyBuilder<G> for TopologyPolygon
606where
607 G: PolygonTrait,
608 <G::Point as Point>::Scalar: Into<f64>,
609 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
610{
611 fn append(&self, geometry: &G, topology: &mut Topology) {
612 append_topology_polygon(geometry, topology);
613 }
614}
615
616impl<G> TopologyBuilder<G> for TopologySegment
617where
618 G: SegmentTrait,
619 <G::Point as Point>::Scalar: Into<f64>,
620 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
621{
622 fn append(&self, geometry: &G, topology: &mut Topology) {
623 append_topology_line(
624 alloc::vec![
625 [
626 geometry.get_indexed::<0, 0>().into(),
627 geometry.get_indexed::<0, 1>().into(),
628 ],
629 [
630 geometry.get_indexed::<1, 0>().into(),
631 geometry.get_indexed::<1, 1>().into(),
632 ],
633 ],
634 topology,
635 );
636 }
637}
638
639impl<G> TopologyBuilder<G> for TopologyRing
640where
641 G: RingTrait,
642 <G::Point as Point>::Scalar: Into<f64>,
643 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
644{
645 fn append(&self, geometry: &G, topology: &mut Topology) {
646 topology
647 .polygons
648 .push(Polygon::new(topology_ring(geometry)));
649 }
650}
651
652impl<G> TopologyBuilder<G> for TopologyBox
653where
654 G: BoxTrait,
655 <G::Point as Point>::Scalar: Into<f64>,
656 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
657{
658 fn append(&self, geometry: &G, topology: &mut Topology) {
659 let minimum = [
660 geometry.get_indexed::<{ corner::MIN }, 0>().into(),
661 geometry.get_indexed::<{ corner::MIN }, 1>().into(),
662 ];
663 let maximum = [
664 geometry.get_indexed::<{ corner::MAX }, 0>().into(),
665 geometry.get_indexed::<{ corner::MAX }, 1>().into(),
666 ];
667 topology
668 .polygons
669 .push(Polygon::new(Ring::from_vec(alloc::vec![
670 topology_point(minimum),
671 topology_point([minimum[0], maximum[1]]),
672 topology_point(maximum),
673 topology_point([maximum[0], minimum[1]]),
674 topology_point(minimum),
675 ])));
676 }
677}
678
679impl<G> TopologyBuilder<G> for TopologyMultiPoint
680where
681 G: MultiPoint,
682 <G::ItemPoint as Point>::Scalar: Into<f64>,
683 <<G::ItemPoint as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
684{
685 fn append(&self, geometry: &G, topology: &mut Topology) {
686 topology.points.extend(geometry.points().map(xy));
687 }
688}
689
690impl<G> TopologyBuilder<G> for TopologyMultiLinestring
691where
692 G: MultiLinestring,
693 <G::Point as Point>::Scalar: Into<f64>,
694 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
695{
696 fn append(&self, geometry: &G, topology: &mut Topology) {
697 for line in geometry.linestrings() {
698 append_topology_line(line.points().map(xy).collect(), topology);
699 }
700 }
701}
702
703impl<G> TopologyBuilder<G> for TopologyMultiPolygon
704where
705 G: MultiPolygon,
706 <G::Point as Point>::Scalar: Into<f64>,
707 <<G::Point as Point>::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
708{
709 fn append(&self, geometry: &G, topology: &mut Topology) {
710 for polygon in geometry.polygons() {
711 append_topology_polygon(polygon, topology);
712 }
713 }
714}
715
716impl<Scalar, Cs> TopologyBuilder<DynGeometry<Scalar, Cs>> for TopologyDynamic
717where
718 Scalar: CoordinateScalar + Into<f64>,
719 Cs: CoordinateSystem,
720 Cs::Family: SameAs<CartesianFamily>,
721{
722 fn append(&self, geometry: &DynGeometry<Scalar, Cs>, topology: &mut Topology) {
723 append_dynamic_topology(geometry, topology);
724 }
725}
726
727impl<G> TopologyBuilder<G> for TopologyCollection
728where
729 G: GeometryCollection,
730 G::Item: Geometry,
731 <G::Item as Geometry>::Kind: TopologyBuilderForKind,
732 TopologyBuilderStrategy<G::Item>: TopologyBuilder<G::Item> + Default,
733{
734 fn append(&self, geometry: &G, topology: &mut Topology) {
735 for item in geometry.items() {
736 TopologyBuilderStrategy::<G::Item>::default().append(item, topology);
737 }
738 }
739}
740
741impl<A, B> RelateStrategy<A, B> for RelateTopology
742where
743 A: Geometry,
744 B: Geometry,
745 A::Kind: TopologyBuilderForKind,
746 B::Kind: TopologyBuilderForKind,
747 TopologyBuilderStrategy<A>: TopologyBuilder<A> + Default,
748 TopologyBuilderStrategy<B>: TopologyBuilder<B> + Default,
749{
750 fn relate(&self, first: &A, second: &B) -> Result<De9im, OverlayError> {
751 let mut first_topology = Topology::default();
752 TopologyBuilderStrategy::<A>::default().append(first, &mut first_topology);
753 let mut second_topology = Topology::default();
754 TopologyBuilderStrategy::<B>::default().append(second, &mut second_topology);
755 relate_topologies(&first_topology, &second_topology)
756 }
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq)]
760enum Location {
761 Interior,
762 Boundary,
763 Exterior,
764}
765
766impl Location {
767 fn index(self) -> usize {
768 match self {
769 Self::Interior => feature::INTERIOR,
770 Self::Boundary => feature::BOUNDARY,
771 Self::Exterior => feature::EXTERIOR,
772 }
773 }
774}
775
776fn empty_matrix() -> De9im {
777 let mut matrix = De9im {
778 m: [[Dimension::Empty; 3]; 3],
779 };
780 matrix.m[feature::EXTERIOR][feature::EXTERIOR] = Dimension::Area;
781 matrix
782}
783
784fn relate_point_point<A, B>(first: &A, second: &B) -> De9im
785where
786 A: Point,
787 B: Point<Scalar = A::Scalar>,
788{
789 let mut matrix = empty_matrix();
790 if point_equal(first, second) {
791 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Point;
792 } else {
793 matrix.m[feature::INTERIOR][feature::EXTERIOR] = Dimension::Point;
794 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Point;
795 }
796 matrix
797}
798
799fn relate_point_linestring<P, L>(point: &P, line: &L) -> De9im
800where
801 P: Point,
802 L: LinestringTrait<Point = P>,
803 P::Scalar: Into<f64>,
804{
805 let mut matrix = empty_matrix();
806 let location = point_location_linestring(point, line);
807 matrix.m[feature::INTERIOR][location.index()] = Dimension::Point;
808 if line_has_curve(line) {
809 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Curve;
810 }
811 let boundaries = line_boundary_points(line);
812 if boundaries
813 .iter()
814 .any(|boundary| !point_equal(point, *boundary))
815 {
816 matrix.m[feature::EXTERIOR][feature::BOUNDARY] = Dimension::Point;
817 }
818 matrix
819}
820
821fn relate_point_polygon<P, G>(point: &P, polygon: &G) -> De9im
822where
823 P: Point + Copy,
824 G: PolygonTrait<Point = P>,
825 P::Scalar: Into<f64>,
826{
827 let mut matrix = empty_matrix();
828 let location = point_location_polygon(point, polygon);
829 matrix.m[feature::INTERIOR][location.index()] = Dimension::Point;
830 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Area;
831 matrix.m[feature::EXTERIOR][feature::BOUNDARY] = Dimension::Curve;
832 matrix
833}
834
835fn relate_linestring_linestring<A, B, P>(first: &A, second: &B) -> De9im
836where
837 A: LinestringTrait<Point = P>,
838 B: LinestringTrait<Point = P>,
839 P: Point,
840 P::Scalar: Into<f64>,
841{
842 let mut matrix = empty_matrix();
843 let mut first_segments = Vec::new();
844 for_each_line_segment(first, |start, end| {
845 first_segments.push((xy(start), xy(end)));
846 });
847 let mut second_segments = Vec::new();
848 for_each_line_segment(second, |start, end| {
849 second_segments.push((xy(start), xy(end)));
850 });
851 for point in line_boundary_points(first) {
852 let location = point_location_linestring(point, second);
853 matrix.m[feature::BOUNDARY][location.index()] = Dimension::Point;
854 }
855 for point in line_boundary_points(second) {
856 let location = point_location_linestring(point, first);
857 matrix.m[location.index()][feature::BOUNDARY] = Dimension::Point;
858 }
859
860 for &first_segment in &first_segments {
861 for &second_segment in &second_segments {
862 match segment_relation(
863 first_segment.0,
864 first_segment.1,
865 second_segment.0,
866 second_segment.1,
867 ) {
868 SegmentRelation::Disjoint => {}
869 SegmentRelation::Point(point) => {
870 let first_location = xy_location_linestring(point, first);
871 let second_location = xy_location_linestring(point, second);
872 matrix.m[first_location.index()][second_location.index()] = Dimension::Point;
873 }
874 SegmentRelation::Overlap => {
875 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Curve;
876 }
877 }
878 }
879 if !xy_equal(first_segment.0, first_segment.1) {
880 for interval in segment_parameters(first_segment, &second_segments, &[]).windows(2) {
881 debug_assert!(interval[1] - interval[0] > f64::EPSILON);
882 let sample = interpolate(
883 first_segment.0,
884 first_segment.1,
885 (interval[0] + interval[1]) * 0.5,
886 );
887 let location = xy_location_linestring(sample, second);
888 matrix.m[feature::INTERIOR][location.index()] = Dimension::Curve;
889 }
890 }
891 }
892 for &second_segment in &second_segments {
893 if !xy_equal(second_segment.0, second_segment.1) {
894 for interval in segment_parameters(second_segment, &first_segments, &[]).windows(2) {
895 debug_assert!(interval[1] - interval[0] > f64::EPSILON);
896 let sample = interpolate(
897 second_segment.0,
898 second_segment.1,
899 (interval[0] + interval[1]) * 0.5,
900 );
901 let location = xy_location_linestring(sample, first);
902 matrix.m[location.index()][feature::INTERIOR] = Dimension::Curve;
903 }
904 }
905 }
906 matrix
907}
908
909fn relate_linestring_polygon<L, G, P>(line: &L, polygon: &G) -> De9im
910where
911 L: LinestringTrait<Point = P>,
912 G: PolygonTrait<Point = P>,
913 P: Point + Copy,
914 P::Scalar: Into<f64>,
915{
916 let mut matrix = empty_matrix();
917 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Area;
918 matrix.m[feature::EXTERIOR][feature::BOUNDARY] = Dimension::Curve;
919 for point in line_boundary_points(line) {
920 let location = point_location_polygon(point, polygon);
921 matrix.m[feature::BOUNDARY][location.index()] = Dimension::Point;
922 }
923
924 let mut boundary_crossings = 0usize;
925 for_each_line_segment(line, |line1, line2| {
926 for fraction in [0.125, 0.375, 0.625, 0.875] {
927 match xy_location_polygon(interpolate(xy(line1), xy(line2), fraction), polygon) {
928 Location::Interior => {
929 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Curve;
930 }
931 Location::Boundary => {
932 matrix.m[feature::INTERIOR][feature::BOUNDARY] = Dimension::Point;
933 }
934 Location::Exterior => {
935 matrix.m[feature::INTERIOR][feature::EXTERIOR] = Dimension::Curve;
936 }
937 }
938 }
939 for_each_polygon_segment(polygon, |polygon1, polygon2| {
940 if let SegmentRelation::Point(point) =
941 segment_relation(xy(line1), xy(line2), xy(polygon1), xy(polygon2))
942 {
943 boundary_crossings += 1;
944 let line_location = xy_location_linestring(point, line);
945 matrix.m[line_location.index()][feature::BOUNDARY] = Dimension::Point;
946 }
947 });
948 });
949 if boundary_crossings >= 2 {
950 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Curve;
951 }
952 matrix
953}
954
955fn point_equal<A, B>(first: &A, second: &B) -> bool
956where
957 A: Point,
958 B: Point<Scalar = A::Scalar>,
959{
960 first.get::<0>() == second.get::<0>() && first.get::<1>() == second.get::<1>()
961}
962
963fn line_has_curve<L>(line: &L) -> bool
964where
965 L: LinestringTrait,
966 L::Point: Point,
967{
968 let mut points = line.points();
969 let Some(mut previous) = points.next() else {
970 return false;
971 };
972 for current in points {
973 if !point_equal(previous, current) {
974 return true;
975 }
976 previous = current;
977 }
978 false
979}
980
981fn line_boundary_points<L>(line: &L) -> alloc::vec::Vec<&L::Point>
982where
983 L: LinestringTrait,
984 L::Point: Point,
985{
986 let points = line.points();
987 let Some(first) = points.clone().next() else {
988 return alloc::vec::Vec::new();
989 };
990 let last = points
991 .last()
992 .expect("a non-empty iterator has a last point");
993 if point_equal(first, last) {
994 alloc::vec::Vec::new()
995 } else {
996 alloc::vec![first, last]
997 }
998}
999
1000fn point_location_linestring<P, L>(point: &P, line: &L) -> Location
1001where
1002 P: Point,
1003 L: LinestringTrait<Point = P>,
1004 P::Scalar: Into<f64>,
1005{
1006 xy_location_linestring(xy(point), line)
1007}
1008
1009fn xy_location_linestring<L>(point: [f64; 2], line: &L) -> Location
1010where
1011 L: LinestringTrait,
1012 L::Point: Point,
1013 <L::Point as Point>::Scalar: Into<f64>,
1014{
1015 for boundary in line_boundary_points(line) {
1016 if xy_equal(point, xy(boundary)) {
1017 return Location::Boundary;
1018 }
1019 }
1020 let mut interior = false;
1021 for_each_line_segment(line, |first, second| {
1022 if point_on_segment(point, xy(first), xy(second)) {
1023 interior = true;
1024 }
1025 });
1026 if interior {
1027 Location::Interior
1028 } else {
1029 Location::Exterior
1030 }
1031}
1032
1033fn point_location_polygon<P, G>(point: &P, polygon: &G) -> Location
1034where
1035 P: Point + Copy,
1036 G: PolygonTrait<Point = P>,
1037 P::Scalar: Into<f64>,
1038{
1039 xy_location_polygon(xy(point), polygon)
1040}
1041
1042fn xy_location_polygon<G>(point: [f64; 2], polygon: &G) -> Location
1043where
1044 G: PolygonTrait,
1045 G::Point: Point,
1046 <G::Point as Point>::Scalar: Into<f64>,
1047{
1048 let mut boundary = false;
1049 for_each_polygon_segment(polygon, |first, second| {
1050 if point_on_segment(point, xy(first), xy(second)) {
1051 boundary = true;
1052 }
1053 });
1054 if boundary {
1055 return Location::Boundary;
1056 }
1057 if point_in_ring_xy(point, polygon.exterior())
1058 && !polygon
1059 .interiors()
1060 .any(|ring| point_in_ring_xy(point, ring))
1061 {
1062 Location::Interior
1063 } else {
1064 Location::Exterior
1065 }
1066}
1067
1068fn point_in_ring_xy<R>(point: [f64; 2], ring: &R) -> bool
1069where
1070 R: RingTrait,
1071 R::Point: Point,
1072 <R::Point as Point>::Scalar: Into<f64>,
1073{
1074 let mut inside = false;
1075 for_each_ring_segment(ring, |first, second| {
1076 let first = xy(first);
1077 let second = xy(second);
1078 if (first[1] > point[1]) != (second[1] > point[1])
1079 && point[0]
1080 < (second[0] - first[0]) * (point[1] - first[1]) / (second[1] - first[1]) + first[0]
1081 {
1082 inside = !inside;
1083 }
1084 });
1085 inside
1086}
1087
1088fn for_each_line_segment<L>(line: &L, mut function: impl FnMut(&L::Point, &L::Point))
1089where
1090 L: LinestringTrait,
1091{
1092 let mut points = line.points();
1093 let Some(mut previous) = points.next() else {
1094 return;
1095 };
1096 for current in points {
1097 function(previous, current);
1098 previous = current;
1099 }
1100}
1101
1102fn for_each_ring_segment<R>(ring: &R, mut function: impl FnMut(&R::Point, &R::Point))
1103where
1104 R: RingTrait,
1105{
1106 let mut points = ring.points();
1107 let Some(first) = points.next() else {
1108 return;
1109 };
1110 let mut previous = first;
1111 for current in points {
1112 function(previous, current);
1113 previous = current;
1114 }
1115 if !point_equal(previous, first) {
1116 function(previous, first);
1117 }
1118}
1119
1120fn for_each_polygon_segment<G>(polygon: &G, mut function: impl FnMut(&G::Point, &G::Point))
1121where
1122 G: PolygonTrait,
1123{
1124 for_each_ring_segment(polygon.exterior(), &mut function);
1125 for ring in polygon.interiors() {
1126 for_each_ring_segment(ring, &mut function);
1127 }
1128}
1129
1130#[derive(Debug, Clone, Copy, PartialEq)]
1131enum SegmentRelation {
1132 Disjoint,
1133 Point([f64; 2]),
1134 Overlap,
1135}
1136
1137fn segment_relation(
1138 first1: [f64; 2],
1139 first2: [f64; 2],
1140 second1: [f64; 2],
1141 second2: [f64; 2],
1142) -> SegmentRelation {
1143 let d1 = precise_math::orient2d(second1, second2, first1);
1144 let d2 = precise_math::orient2d(second1, second2, first2);
1145 let d3 = precise_math::orient2d(first1, first2, second1);
1146 let d4 = precise_math::orient2d(first1, first2, second2);
1147 if opposite(d1, d2) && opposite(d3, d4) {
1148 return SegmentRelation::Point(line_cross(first1, first2, second1, second2));
1149 }
1150 if d1 == 0.0 && d2 == 0.0 && d3 == 0.0 && d4 == 0.0 {
1151 let overlap = collinear_overlap_length(first1, first2, second1, second2);
1152 return if overlap > 0.0 {
1153 SegmentRelation::Overlap
1154 } else if overlap == 0.0 {
1155 [first1, first2, second1, second2]
1156 .into_iter()
1157 .find(|point| {
1158 point_on_segment(*point, first1, first2)
1159 && point_on_segment(*point, second1, second2)
1160 })
1161 .map_or(SegmentRelation::Disjoint, SegmentRelation::Point)
1162 } else {
1163 SegmentRelation::Disjoint
1164 };
1165 }
1166 for (value, point, start, end) in [
1167 (d1, first1, second1, second2),
1168 (d2, first2, second1, second2),
1169 (d3, second1, first1, first2),
1170 (d4, second2, first1, first2),
1171 ] {
1172 if value == 0.0 && point_on_segment(point, start, end) {
1173 return SegmentRelation::Point(point);
1174 }
1175 }
1176 SegmentRelation::Disjoint
1177}
1178
1179fn xy<P>(point: &P) -> [f64; 2]
1180where
1181 P: Point,
1182 P::Scalar: Into<f64>,
1183{
1184 [point.get::<0>().into(), point.get::<1>().into()]
1185}
1186
1187fn xy_equal(first: [f64; 2], second: [f64; 2]) -> bool {
1188 first[0] == second[0] && first[1] == second[1]
1189}
1190
1191fn point_on_segment(point: [f64; 2], start: [f64; 2], end: [f64; 2]) -> bool {
1192 precise_math::orient2d(start, end, point) == 0.0
1193 && point[0] >= start[0].min(end[0])
1194 && point[0] <= start[0].max(end[0])
1195 && point[1] >= start[1].min(end[1])
1196 && point[1] <= start[1].max(end[1])
1197}
1198
1199fn opposite(first: f64, second: f64) -> bool {
1200 (first > 0.0 && second < 0.0) || (first < 0.0 && second > 0.0)
1201}
1202
1203fn line_cross(
1204 first1: [f64; 2],
1205 first2: [f64; 2],
1206 second1: [f64; 2],
1207 second2: [f64; 2],
1208) -> [f64; 2] {
1209 let denominator = (first1[0] - first2[0]) * (second1[1] - second2[1])
1210 - (first1[1] - first2[1]) * (second1[0] - second2[0]);
1211 let first_cross = first1[0] * first2[1] - first1[1] * first2[0];
1212 let second_cross = second1[0] * second2[1] - second1[1] * second2[0];
1213 [
1214 (first_cross * (second1[0] - second2[0]) - (first1[0] - first2[0]) * second_cross)
1215 / denominator,
1216 (first_cross * (second1[1] - second2[1]) - (first1[1] - first2[1]) * second_cross)
1217 / denominator,
1218 ]
1219}
1220
1221fn collinear_overlap_length(
1222 first1: [f64; 2],
1223 first2: [f64; 2],
1224 second1: [f64; 2],
1225 second2: [f64; 2],
1226) -> f64 {
1227 let use_x = (first1[0] - first2[0]).abs() >= (first1[1] - first2[1]).abs();
1228 let index = usize::from(!use_x);
1229 first1[index]
1230 .max(first2[index])
1231 .min(second1[index].max(second2[index]))
1232 - first1[index]
1233 .min(first2[index])
1234 .max(second1[index].min(second2[index]))
1235}
1236
1237fn interpolate(first: [f64; 2], second: [f64; 2], fraction: f64) -> [f64; 2] {
1238 [
1239 first[0] + (second[0] - first[0]) * fraction,
1240 first[1] + (second[1] - first[1]) * fraction,
1241 ]
1242}
1243
1244fn topology_point(coordinates: [f64; 2]) -> TopologyPointModel {
1245 TopologyPointModel::new(coordinates[0], coordinates[1])
1246}
1247
1248fn topology_ring<R>(ring: &R) -> Ring<TopologyPointModel>
1249where
1250 R: RingTrait,
1251 <R::Point as Point>::Scalar: Into<f64>,
1252{
1253 Ring::from_vec(
1254 ring.points()
1255 .map(|point| topology_point(xy(point)))
1256 .collect(),
1257 )
1258}
1259
1260fn append_topology_line(mut points: Vec<[f64; 2]>, topology: &mut Topology) {
1261 points.dedup_by(|first, second| xy_equal(*first, *second));
1262 if points.len() >= 2 {
1263 topology.lines.push(points);
1264 } else if let Some(point) = points.first() {
1265 topology.points.push(*point);
1266 }
1267}
1268
1269fn append_topology_polygon<G>(polygon: &G, topology: &mut Topology)
1270where
1271 G: PolygonTrait,
1272 <G::Point as Point>::Scalar: Into<f64>,
1273{
1274 let outer = topology_ring(polygon.exterior());
1275 if outer.0.len() < 3 {
1276 append_topology_line(outer.0.iter().map(xy).collect(), topology);
1277 return;
1278 }
1279 topology.polygons.push(Polygon::with_inners(
1280 outer,
1281 polygon.interiors().map(topology_ring).collect(),
1282 ));
1283}
1284
1285fn append_dynamic_topology<Scalar, Cs>(geometry: &DynGeometry<Scalar, Cs>, topology: &mut Topology)
1286where
1287 Scalar: CoordinateScalar + Into<f64>,
1288 Cs: CoordinateSystem,
1289 Cs::Family: SameAs<CartesianFamily>,
1290{
1291 match geometry {
1292 DynGeometry::Point(point) => topology.points.push(xy(point)),
1293 DynGeometry::LineString(line) => {
1294 append_topology_line(line.points().map(xy).collect(), topology);
1295 }
1296 DynGeometry::Polygon(polygon) => append_topology_polygon(polygon, topology),
1297 DynGeometry::MultiPoint(points) => topology.points.extend(points.points().map(xy)),
1298 DynGeometry::MultiLineString(lines) => {
1299 for line in lines.linestrings() {
1300 append_topology_line(line.points().map(xy).collect(), topology);
1301 }
1302 }
1303 DynGeometry::MultiPolygon(polygons) => {
1304 for polygon in polygons.polygons() {
1305 append_topology_polygon(polygon, topology);
1306 }
1307 }
1308 DynGeometry::GeometryCollection(items) => {
1309 for item in items {
1310 append_dynamic_topology(item, topology);
1311 }
1312 }
1313 }
1314}
1315
1316fn topology_segments(topology: &Topology) -> Vec<([f64; 2], [f64; 2])> {
1317 let mut segments = Vec::new();
1318 for line in &topology.lines {
1319 for points in line.windows(2) {
1320 debug_assert!(!xy_equal(points[0], points[1]));
1321 segments.push((points[0], points[1]));
1322 }
1323 }
1324 for polygon in &topology.polygons {
1325 append_boundary_segments(&polygon.outer, &mut segments);
1326 for ring in &polygon.inners {
1327 append_boundary_segments(ring, &mut segments);
1328 }
1329 }
1330 segments
1331}
1332
1333fn topology_location(topology: &Topology, point: [f64; 2]) -> Location {
1334 let mut polygon_boundary = false;
1335 for polygon in &topology.polygons {
1336 match xy_location_polygon(point, polygon) {
1337 Location::Interior => return Location::Interior,
1338 Location::Boundary => polygon_boundary = true,
1339 Location::Exterior => {}
1340 }
1341 }
1342
1343 let mut on_line = false;
1344 let mut endpoint_count = 0usize;
1345 for line in &topology.lines {
1346 for segment in line.windows(2) {
1347 if point_on_segment(point, segment[0], segment[1]) {
1348 on_line = true;
1349 }
1350 }
1351 let first = *line
1352 .first()
1353 .expect("topology lines have at least two points");
1354 let last = *line
1355 .last()
1356 .expect("topology lines have at least two points");
1357 if !xy_equal(first, last) {
1358 endpoint_count += usize::from(xy_equal(point, first));
1359 endpoint_count += usize::from(xy_equal(point, last));
1360 }
1361 }
1362 if on_line {
1363 return if endpoint_count % 2 == 1 && !polygon_boundary {
1364 Location::Boundary
1365 } else {
1366 Location::Interior
1367 };
1368 }
1369 if topology
1370 .points
1371 .iter()
1372 .any(|candidate| xy_equal(*candidate, point))
1373 {
1374 return Location::Interior;
1375 }
1376 if polygon_boundary {
1377 Location::Boundary
1378 } else {
1379 Location::Exterior
1380 }
1381}
1382
1383fn dimension_rank(dimension: Dimension) -> u8 {
1384 match dimension {
1385 Dimension::Empty => 0,
1386 Dimension::Point => 1,
1387 Dimension::Curve => 2,
1388 Dimension::Area => 3,
1389 }
1390}
1391
1392fn set_dimension(matrix: &mut De9im, row: Location, column: Location, dimension: Dimension) {
1393 let cell = &mut matrix.m[row.index()][column.index()];
1394 if dimension_rank(dimension) > dimension_rank(*cell) {
1395 *cell = dimension;
1396 }
1397}
1398
1399fn segment_parameter(point: [f64; 2], start: [f64; 2], end: [f64; 2]) -> f64 {
1400 let dx = end[0] - start[0];
1401 let dy = end[1] - start[1];
1402 if dx.abs() >= dy.abs() {
1403 debug_assert_ne!(dx, 0.0);
1404 (point[0] - start[0]) / dx
1405 } else {
1406 debug_assert_ne!(dy, 0.0);
1407 (point[1] - start[1]) / dy
1408 }
1409}
1410
1411fn segment_parameters(
1412 segment: ([f64; 2], [f64; 2]),
1413 all_segments: &[([f64; 2], [f64; 2])],
1414 split_points: &[[f64; 2]],
1415) -> Vec<f64> {
1416 let mut parameters = alloc::vec![0.0, 1.0];
1417 for &(start, end) in all_segments {
1418 match segment_relation(segment.0, segment.1, start, end) {
1419 SegmentRelation::Point(point) => {
1420 parameters.push(segment_parameter(point, segment.0, segment.1));
1421 }
1422 SegmentRelation::Overlap => {
1423 for point in [start, end] {
1424 if point_on_segment(point, segment.0, segment.1) {
1425 parameters.push(segment_parameter(point, segment.0, segment.1));
1426 }
1427 }
1428 }
1429 SegmentRelation::Disjoint => {}
1430 }
1431 }
1432 for &point in split_points {
1433 if point_on_segment(point, segment.0, segment.1) {
1434 parameters.push(segment_parameter(point, segment.0, segment.1));
1435 }
1436 }
1437 parameters.retain(|parameter| (-f64::EPSILON..=1.0 + f64::EPSILON).contains(parameter));
1438 parameters.sort_by(f64::total_cmp);
1439 parameters.dedup_by(|first, second| (*first - *second).abs() <= f64::EPSILON);
1440 parameters
1441}
1442
1443fn record_segment_cells(
1444 matrix: &mut De9im,
1445 first: &Topology,
1446 second: &Topology,
1447 segment: ([f64; 2], [f64; 2]),
1448 all_segments: &[([f64; 2], [f64; 2])],
1449) {
1450 let parameters = segment_parameters(segment, all_segments, &second.points);
1451 for interval in parameters.windows(2) {
1452 debug_assert!(interval[1] - interval[0] > f64::EPSILON);
1453 let midpoint = interpolate(segment.0, segment.1, (interval[0] + interval[1]) * 0.5);
1454 let first_location = topology_location(first, midpoint);
1455 let second_location = topology_location(second, midpoint);
1456 debug_assert_ne!(first_location, Location::Exterior);
1457 set_dimension(matrix, first_location, second_location, Dimension::Curve);
1458 }
1459}
1460
1461fn append_topology_candidates(
1462 topology: &Topology,
1463 segments: &[([f64; 2], [f64; 2])],
1464 output: &mut Vec<[f64; 2]>,
1465) {
1466 output.extend(topology.points.iter().copied());
1467 for &(start, end) in segments {
1468 output.push(start);
1469 output.push(end);
1470 }
1471}
1472
1473fn areas_intersect(first: &Topology, second: &Topology) -> Result<bool, OverlayError> {
1474 for first_polygon in &first.polygons {
1475 for second_polygon in &second.polygons {
1476 if !crate::operation::intersection(first_polygon, second_polygon)?
1477 .0
1478 .is_empty()
1479 {
1480 return Ok(true);
1481 }
1482 }
1483 }
1484 Ok(false)
1485}
1486
1487fn has_area_outside(first: &Topology, second: &Topology) -> Result<bool, OverlayError> {
1488 for polygon in &first.polygons {
1489 let mut pieces = alloc::vec![polygon.clone()];
1490 for clip in &second.polygons {
1491 let mut remainder = Vec::new();
1492 for piece in pieces {
1493 remainder.extend(crate::operation::difference(&piece, clip)?.0);
1494 }
1495 pieces = remainder;
1496 if pieces.is_empty() {
1497 break;
1498 }
1499 }
1500 if !pieces.is_empty() {
1501 return Ok(true);
1502 }
1503 }
1504 Ok(false)
1505}
1506
1507fn relate_topologies(first: &Topology, second: &Topology) -> Result<De9im, OverlayError> {
1508 if !first.in_range() || !second.in_range() {
1509 return Err(OverlayError::Unsupported);
1510 }
1511
1512 let first_segments = topology_segments(first);
1513 let second_segments = topology_segments(second);
1514 let all_segments = first_segments
1515 .iter()
1516 .chain(&second_segments)
1517 .copied()
1518 .collect::<Vec<_>>();
1519 let mut matrix = empty_matrix();
1520
1521 if areas_intersect(first, second)? {
1522 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Area;
1523 }
1524 if has_area_outside(first, second)? {
1525 matrix.m[feature::INTERIOR][feature::EXTERIOR] = Dimension::Area;
1526 }
1527 if has_area_outside(second, first)? {
1528 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Area;
1529 }
1530
1531 for &segment in &first_segments {
1532 record_segment_cells(&mut matrix, first, second, segment, &all_segments);
1533 }
1534 for &segment in &second_segments {
1535 for interval in segment_parameters(segment, &all_segments, &first.points).windows(2) {
1536 debug_assert!(interval[1] - interval[0] > f64::EPSILON);
1537 let midpoint = interpolate(segment.0, segment.1, (interval[0] + interval[1]) * 0.5);
1538 let first_location = topology_location(first, midpoint);
1539 let second_location = topology_location(second, midpoint);
1540 debug_assert_ne!(second_location, Location::Exterior);
1541 set_dimension(
1542 &mut matrix,
1543 first_location,
1544 second_location,
1545 Dimension::Curve,
1546 );
1547 }
1548 }
1549
1550 let mut candidates = Vec::new();
1551 append_topology_candidates(first, &first_segments, &mut candidates);
1552 append_topology_candidates(second, &second_segments, &mut candidates);
1553 for &(first_start, first_end) in &first_segments {
1554 for &(second_start, second_end) in &second_segments {
1555 match segment_relation(first_start, first_end, second_start, second_end) {
1556 SegmentRelation::Point(point) => candidates.push(point),
1557 SegmentRelation::Overlap => {
1558 for point in [first_start, first_end, second_start, second_end] {
1559 if point_on_segment(point, first_start, first_end)
1560 && point_on_segment(point, second_start, second_end)
1561 {
1562 candidates.push(point);
1563 }
1564 }
1565 }
1566 SegmentRelation::Disjoint => {}
1567 }
1568 }
1569 }
1570 candidates.sort_by(|first, second| {
1571 first[0]
1572 .total_cmp(&second[0])
1573 .then_with(|| first[1].total_cmp(&second[1]))
1574 });
1575 candidates.dedup_by(|first, second| xy_equal(*first, *second));
1576 for point in candidates {
1577 let first_location = topology_location(first, point);
1578 let second_location = topology_location(second, point);
1579 debug_assert!(
1580 first_location != Location::Exterior || second_location != Location::Exterior
1581 );
1582 set_dimension(
1583 &mut matrix,
1584 first_location,
1585 second_location,
1586 Dimension::Point,
1587 );
1588 }
1589
1590 Ok(matrix)
1591}
1592
1593fn relate_polygon_polygon<G1, G2, P>(g1: &G1, g2: &G2) -> Result<De9im, OverlayError>
1619where
1620 G1: PolygonTrait<Point = P>,
1621 G2: PolygonTrait<Point = P>,
1622 P: PointMut + Default + Copy,
1623 P::Scalar: CoordinateScalar + Into<f64>,
1624 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
1625{
1626 if !polygon_in_range(g1) || !polygon_in_range(g2) {
1627 return Err(OverlayError::Unsupported);
1628 }
1629
1630 let interiors_overlap = !crate::operation::intersection(g1, g2)?.0.is_empty();
1631 let first_outside = !crate::operation::difference(g1, g2)?.0.is_empty();
1632 let second_outside = !crate::operation::difference(g2, g1)?.0.is_empty();
1633 let boundary_boundary = polygon_boundary_dimension(g1, g2);
1634
1635 let mut matrix = empty_matrix();
1636 if interiors_overlap {
1637 matrix.m[feature::INTERIOR][feature::INTERIOR] = Dimension::Area;
1638 }
1639 if first_outside {
1640 matrix.m[feature::INTERIOR][feature::EXTERIOR] = Dimension::Area;
1641 matrix.m[feature::BOUNDARY][feature::EXTERIOR] = Dimension::Curve;
1642 }
1643 if second_outside {
1644 matrix.m[feature::EXTERIOR][feature::INTERIOR] = Dimension::Area;
1645 matrix.m[feature::EXTERIOR][feature::BOUNDARY] = Dimension::Curve;
1646 }
1647 matrix.m[feature::BOUNDARY][feature::BOUNDARY] = boundary_boundary;
1648
1649 if interiors_overlap {
1650 match (first_outside, second_outside) {
1651 (true, true) => {
1652 matrix.m[feature::INTERIOR][feature::BOUNDARY] = Dimension::Curve;
1653 matrix.m[feature::BOUNDARY][feature::INTERIOR] = Dimension::Curve;
1654 }
1655 (true, false) => {
1656 matrix.m[feature::INTERIOR][feature::BOUNDARY] = Dimension::Curve;
1657 }
1658 (false, true) => {
1659 matrix.m[feature::BOUNDARY][feature::INTERIOR] = Dimension::Curve;
1660 }
1661 (false, false) => {}
1662 }
1663 }
1664
1665 Ok(matrix)
1666}
1667
1668fn polygon_boundary_dimension<G1, G2, P>(first: &G1, second: &G2) -> Dimension
1669where
1670 G1: PolygonTrait<Point = P>,
1671 G2: PolygonTrait<Point = P>,
1672 P: Point,
1673 P::Scalar: Into<f64>,
1674{
1675 let first_segments = polygon_boundary_segments(first);
1676 let second_segments = polygon_boundary_segments(second);
1677 let mut dimension = Dimension::Empty;
1678 for (first_start, first_end) in &first_segments {
1679 for (second_start, second_end) in &second_segments {
1680 match segment_relation(*first_start, *first_end, *second_start, *second_end) {
1681 SegmentRelation::Overlap => return Dimension::Curve,
1682 SegmentRelation::Point(_) => dimension = Dimension::Point,
1683 SegmentRelation::Disjoint => {}
1684 }
1685 }
1686 }
1687 dimension
1688}
1689
1690fn polygon_boundary_segments<G, P>(polygon: &G) -> alloc::vec::Vec<([f64; 2], [f64; 2])>
1691where
1692 G: PolygonTrait<Point = P>,
1693 P: Point,
1694 P::Scalar: Into<f64>,
1695{
1696 let mut segments = alloc::vec::Vec::new();
1697 append_boundary_segments(polygon.exterior(), &mut segments);
1698 for ring in polygon.interiors() {
1699 append_boundary_segments(ring, &mut segments);
1700 }
1701 segments
1702}
1703
1704fn append_boundary_segments<R>(ring: &R, output: &mut alloc::vec::Vec<([f64; 2], [f64; 2])>)
1705where
1706 R: RingTrait,
1707 <R::Point as Point>::Scalar: Into<f64>,
1708{
1709 let points: alloc::vec::Vec<_> = ring.points().map(xy).collect();
1710 for pair in points.windows(2) {
1711 if !xy_equal(pair[0], pair[1]) {
1712 output.push((pair[0], pair[1]));
1713 }
1714 }
1715 if let (Some(first), Some(last)) = (points.first(), points.last())
1716 && !xy_equal(*first, *last)
1717 {
1718 output.push((*last, *first));
1719 }
1720}
1721
1722#[inline]
1734#[must_use = "relate can fail and its predicate result should be used"]
1735pub fn relate_mask<G1, G2>(g1: &G1, g2: &G2, mask: &str) -> Result<bool, RelateError>
1736where
1737 G1: Geometry,
1738 G2: Geometry,
1739 G1::Kind: RelatePairStrategy<G2::Kind>,
1740 PairStrategy<G1, G2>: RelateStrategy<G1, G2> + Default,
1741{
1742 relate(g1, g2)?.matches(mask)
1743}
1744
1745#[inline]
1755#[must_use = "contains_properly can fail and its predicate result should be used"]
1756pub fn contains_properly<G1, G2>(g1: &G1, g2: &G2) -> Result<bool, OverlayError>
1757where
1758 G1: Geometry,
1759 G2: Geometry,
1760 G1::Kind: RelatePairStrategy<G2::Kind>,
1761 PairStrategy<G1, G2>: RelateStrategy<G1, G2> + Default,
1762{
1763 let matrix = relate(g1, g2)?;
1764 Ok(matrix.interior_interior().is_set()
1765 && !matrix.m[feature::BOUNDARY][feature::INTERIOR].is_set()
1766 && !matrix.m[feature::BOUNDARY][feature::BOUNDARY].is_set()
1767 && !matrix.m[feature::EXTERIOR][feature::INTERIOR].is_set()
1768 && !matrix.m[feature::EXTERIOR][feature::BOUNDARY].is_set())
1769}
1770
1771#[inline]
1781#[must_use = "touches can fail and its predicate result should be used"]
1782pub fn touches<G1, G2>(g1: &G1, g2: &G2) -> Result<bool, OverlayError>
1783where
1784 G1: Geometry,
1785 G2: Geometry,
1786 G1::Kind: RelatePairStrategy<G2::Kind>,
1787 PairStrategy<G1, G2>: RelateStrategy<G1, G2> + Default,
1788{
1789 let matrix = relate(g1, g2)?;
1790 Ok(!matrix.interior_interior().is_set()
1791 && (matrix.m[feature::INTERIOR][feature::BOUNDARY].is_set()
1792 || matrix.m[feature::BOUNDARY][feature::INTERIOR].is_set()
1793 || matrix.boundary_boundary().is_set()))
1794}
1795
1796#[inline]
1806#[must_use = "overlaps can fail and its predicate result should be used"]
1807pub fn overlaps<G1, G2>(g1: &G1, g2: &G2) -> Result<bool, OverlayError>
1808where
1809 G1: Geometry,
1810 G2: Geometry,
1811 G1::Kind: RelatePairStrategy<G2::Kind>,
1812 PairStrategy<G1, G2>: RelateStrategy<G1, G2> + Default,
1813{
1814 let matrix = relate(g1, g2)?;
1815 let dimension = matrix.interior_interior();
1816 Ok(matches!(
1817 dimension,
1818 Dimension::Point | Dimension::Curve | Dimension::Area
1819 ) && matrix.interior_exterior() == dimension
1820 && matrix.exterior_interior() == dimension)
1821}
1822
1823#[inline]
1833#[must_use = "crosses can fail and its predicate result should be used"]
1834pub fn crosses<G1, G2>(g1: &G1, g2: &G2) -> Result<bool, OverlayError>
1835where
1836 G1: Geometry,
1837 G2: Geometry,
1838 G1::Kind: RelatePairStrategy<G2::Kind>,
1839 PairStrategy<G1, G2>: RelateStrategy<G1, G2> + Default,
1840{
1841 let matrix = relate(g1, g2)?;
1842 Ok((matrix.interior_interior() == Dimension::Point
1843 && matrix.interior_exterior() == Dimension::Curve
1844 && matrix.exterior_interior() == Dimension::Curve)
1845 || (matrix.interior_interior() == Dimension::Curve
1846 && (matrix.interior_exterior() == Dimension::Curve
1847 || matrix.exterior_interior() == Dimension::Curve)))
1848}
1849
1850#[cfg(test)]
1851mod tests {
1852 use super::{Dimension, contains_properly, crosses, overlaps, relate, touches};
1857 use geometry_cs::Cartesian;
1858 use geometry_model::{Point2D, Polygon, polygon};
1859
1860 type P = Point2D<f64, Cartesian>;
1861
1862 fn square(x: f64, y: f64, s: f64) -> Polygon<P> {
1863 polygon![[(x, y), (x + s, y), (x + s, y + s), (x, y + s), (x, y)]]
1864 }
1865
1866 #[test]
1867 fn overlapping_squares_overlap() {
1868 let a = square(0.0, 0.0, 2.0);
1869 let b = square(1.0, 1.0, 2.0);
1870 assert_eq!(relate(&a, &b).unwrap().interior_interior(), Dimension::Area);
1871 assert!(overlaps(&a, &b).unwrap());
1872 assert!(!touches(&a, &b).unwrap());
1873 assert!(!crosses(&a, &b).unwrap());
1874 }
1875
1876 #[test]
1877 fn proper_containment_excludes_boundary_contact() {
1878 let container = square(0.0, 0.0, 5.0);
1879 assert!(contains_properly(&container, &square(1.0, 1.0, 1.0)).unwrap());
1880 assert!(!contains_properly(&container, &square(0.0, 1.0, 1.0)).unwrap());
1881 }
1882
1883 #[test]
1884 fn edge_touching_squares_have_curve_boundary_intersection() {
1885 let a = square(0.0, 0.0, 2.0);
1886 let b = square(2.0, 0.0, 2.0);
1887 assert_eq!(
1888 relate(&a, &b).unwrap().boundary_boundary(),
1889 Dimension::Curve
1890 );
1891 assert!(touches(&a, &b).unwrap());
1892 assert!(!overlaps(&a, &b).unwrap());
1893 }
1894
1895 #[test]
1896 fn edge_aligned_overlap_is_detected() {
1897 let a: Polygon<P> = polygon![[(0.0, 0.0), (3.0, 0.0), (3.0, 1.0), (0.0, 1.0), (0.0, 0.0)]];
1898 let b: Polygon<P> = polygon![[(2.0, 0.0), (5.0, 0.0), (5.0, 1.0), (2.0, 1.0), (2.0, 0.0)]];
1899 assert!(overlaps(&a, &b).unwrap());
1900 }
1901
1902 #[test]
1903 fn out_of_range_coordinates_are_unsupported() {
1904 use crate::operation::OverlayError;
1909 let a: Polygon<P> = polygon![[
1910 (0.0, 0.0),
1911 (2e14, 0.0),
1912 (2e14, 2e14),
1913 (0.0, 2e14),
1914 (0.0, 0.0)
1915 ]];
1916 let b: Polygon<P> = polygon![[
1917 (1e14, 1e14),
1918 (3e14, 1e14),
1919 (3e14, 3e14),
1920 (1e14, 3e14),
1921 (1e14, 1e14)
1922 ]];
1923 assert_eq!(relate(&a, &b), Err(OverlayError::Unsupported));
1924 assert_eq!(overlaps(&a, &b), Err(OverlayError::Unsupported));
1925 }
1926
1927 #[test]
1928 fn disjoint_squares_neither() {
1929 let a = square(0.0, 0.0, 1.0);
1930 let b = square(5.0, 5.0, 1.0);
1931 assert!(!touches(&a, &b).unwrap());
1932 assert!(!overlaps(&a, &b).unwrap());
1933 assert_eq!(
1934 relate(&a, &b).unwrap().interior_interior(),
1935 Dimension::Empty
1936 );
1937 }
1938
1939 #[test]
1940 fn contained_square_does_not_overlap_or_touch() {
1941 let big = square(0.0, 0.0, 10.0);
1946 let small = square(3.0, 3.0, 2.0);
1947 assert_eq!(
1948 relate(&big, &small).unwrap().interior_interior(),
1949 Dimension::Area
1950 );
1951 assert!(!overlaps(&big, &small).unwrap());
1952 assert!(!touches(&big, &small).unwrap());
1953 }
1954}