1extern crate alloc;
42
43use geometry_coords::CoordinateScalar;
44use geometry_cs::{CartesianFamily, CoordinateSystem};
45use geometry_tag::{LinestringTag, PointTag, PolygonTag, SameAs, SegmentTag};
46use geometry_trait::{
47 Geometry, Linestring as LinestringTrait, Point as PointTrait, Polygon as PolygonTrait,
48 Ring as RingTrait, Segment as SegmentTrait, segment_end, segment_start,
49};
50
51use crate::within::{WithinPoly, WithinStrategy};
52
53pub trait IntersectsStrategy<A, B> {
61 fn intersects(&self, a: &A, b: &B) -> bool;
63}
64
65#[derive(Debug, Default, Clone, Copy)]
74pub struct CartesianIntersects;
75
76impl<A, B> IntersectsStrategy<A, B> for CartesianIntersects
77where
78 A: Geometry,
79 B: Geometry,
80 A::Kind: IntersectsPairStrategy<B::Kind>,
81 <A::Kind as IntersectsPairStrategy<B::Kind>>::S: IntersectsStrategy<A, B>,
82{
83 #[inline]
84 fn intersects(&self, a: &A, b: &B) -> bool {
85 <<A::Kind as IntersectsPairStrategy<B::Kind>>::S as Default>::default().intersects(a, b)
86 }
87}
88
89#[derive(Debug, Default, Clone, Copy)]
105pub struct Reversed<S>(pub S);
106
107impl<A, B, S> IntersectsStrategy<B, A> for Reversed<S>
108where
109 S: IntersectsStrategy<A, B>,
110{
111 #[inline]
112 fn intersects(&self, b: &B, a: &A) -> bool {
113 self.0.intersects(a, b)
114 }
115}
116
117#[derive(Debug, Default, Clone, Copy)]
127pub struct IxPointPoint;
128#[derive(Debug, Default, Clone, Copy)]
130pub struct IxPointSegment;
131#[derive(Debug, Default, Clone, Copy)]
133pub struct IxSegmentSegment;
134#[derive(Debug, Default, Clone, Copy)]
136pub struct IxLinestringSegment;
137#[derive(Debug, Default, Clone, Copy)]
139pub struct IxLinestringLinestring;
140#[derive(Debug, Default, Clone, Copy)]
142pub struct IxPointPolygon;
143#[derive(Debug, Default, Clone, Copy)]
145pub struct IxLinestringPolygon;
146#[derive(Debug, Default, Clone, Copy)]
148pub struct IxPolygonPolygon;
149#[derive(Debug, Default, Clone, Copy)]
151pub struct IxSegmentPoint;
152#[derive(Debug, Default, Clone, Copy)]
155pub struct IxSegmentLinestring;
156#[derive(Debug, Default, Clone, Copy)]
158pub struct IxPolygonPoint;
159#[derive(Debug, Default, Clone, Copy)]
162pub struct IxPolygonLinestring;
163
164impl<A, B> IntersectsStrategy<A, B> for IxPointPoint
171where
172 A: PointTrait,
173 B: PointTrait<Scalar = A::Scalar>,
174 <A::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
175{
176 #[inline]
177 fn intersects(&self, a: &A, b: &B) -> bool {
178 points_equal::<A, B>(a, b, A::DIM)
179 }
180}
181
182impl<P, S> IntersectsStrategy<P, S> for IxPointSegment
188where
189 P: PointTrait,
190 S: SegmentTrait<Point = P>,
191 P: geometry_trait::PointMut + Default,
192 P::Scalar: CoordinateScalar,
193 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
194{
195 #[inline]
196 fn intersects(&self, p: &P, s: &S) -> bool {
197 point_on_segment(p, &segment_start(s), &segment_end(s))
198 }
199}
200
201impl<A, B, P> IntersectsStrategy<A, B> for IxSegmentSegment
204where
205 A: SegmentTrait<Point = P>,
206 B: SegmentTrait<Point = P>,
207 P: PointTrait + geometry_trait::PointMut + Default,
208 P::Scalar: CoordinateScalar,
209 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
210{
211 #[inline]
212 fn intersects(&self, a: &A, b: &B) -> bool {
213 segments_intersect(
214 &segment_start(a),
215 &segment_end(a),
216 &segment_start(b),
217 &segment_end(b),
218 )
219 }
220}
221
222impl<L, S, P> IntersectsStrategy<L, S> for IxLinestringSegment
225where
226 L: LinestringTrait<Point = P>,
227 S: SegmentTrait<Point = P>,
228 P: PointTrait + geometry_trait::PointMut + Default,
229 P::Scalar: CoordinateScalar,
230 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
231{
232 #[inline]
233 fn intersects(&self, ls: &L, s: &S) -> bool {
234 let s1 = segment_start(s);
235 let s2 = segment_end(s);
236 let mut it = ls.points();
237 let Some(mut prev) = it.next() else {
238 return false;
239 };
240 for curr in it {
241 if segments_intersect(prev, curr, &s1, &s2) {
242 return true;
243 }
244 prev = curr;
245 }
246 false
247 }
248}
249
250impl<A, B, P> IntersectsStrategy<A, B> for IxLinestringLinestring
253where
254 A: LinestringTrait<Point = P>,
255 B: LinestringTrait<Point = P>,
256 P: PointTrait,
257 P::Scalar: CoordinateScalar,
258 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
259{
260 #[inline]
261 fn intersects(&self, a: &A, b: &B) -> bool {
262 let mut ia = a.points();
263 let Some(mut pa) = ia.next() else {
264 return false;
265 };
266 for qa in ia {
267 let mut ib = b.points();
268 let Some(mut pb) = ib.next() else {
269 return false;
270 };
271 for qb in ib {
272 if segments_intersect(pa, qa, pb, qb) {
273 return true;
274 }
275 pb = qb;
276 }
277 pa = qa;
278 }
279 false
280 }
281}
282
283impl<P, G> IntersectsStrategy<P, G> for IxPointPolygon
289where
290 P: PointTrait,
291 G: PolygonTrait<Point = P>,
292 P::Scalar: CoordinateScalar,
293 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
294{
295 #[inline]
296 fn intersects(&self, p: &P, pg: &G) -> bool {
297 WithinPoly.covered_by(p, pg)
298 }
299}
300
301impl<L, G, P> IntersectsStrategy<L, G> for IxLinestringPolygon
304where
305 L: LinestringTrait<Point = P>,
306 G: PolygonTrait<Point = P>,
307 P: PointTrait,
308 P::Scalar: CoordinateScalar,
309 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
310{
311 fn intersects(&self, ls: &L, pg: &G) -> bool {
312 for v in ls.points() {
314 if WithinPoly.covered_by(v, pg) {
315 return true;
316 }
317 }
318 if linestring_crosses_ring(ls, pg.exterior()) {
320 return true;
321 }
322 for hole in pg.interiors() {
323 if linestring_crosses_ring(ls, hole) {
324 return true;
325 }
326 }
327 false
328 }
329}
330
331impl<A, B, P> IntersectsStrategy<A, B> for IxPolygonPolygon
334where
335 A: PolygonTrait<Point = P>,
336 B: PolygonTrait<Point = P>,
337 P: PointTrait,
338 P::Scalar: CoordinateScalar,
339 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
340{
341 fn intersects(&self, a: &A, b: &B) -> bool {
342 if let Some(v) = a.exterior().points().next() {
344 if WithinPoly.covered_by(v, b) {
345 return true;
346 }
347 }
348 if let Some(v) = b.exterior().points().next() {
349 if WithinPoly.covered_by(v, a) {
350 return true;
351 }
352 }
353 if rings_cross(a.exterior(), b.exterior()) {
355 return true;
356 }
357 for hole in a.interiors() {
358 if rings_cross(hole, b.exterior()) {
359 return true;
360 }
361 }
362 for hole in b.interiors() {
363 if rings_cross(a.exterior(), hole) {
364 return true;
365 }
366 }
367 false
368 }
369}
370
371impl<S, P> IntersectsStrategy<S, P> for IxSegmentPoint
378where
379 S: SegmentTrait<Point = P>,
380 P: PointTrait + geometry_trait::PointMut + Default,
381 P::Scalar: CoordinateScalar,
382 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
383{
384 #[inline]
385 fn intersects(&self, s: &S, p: &P) -> bool {
386 IxPointSegment.intersects(p, s)
387 }
388}
389
390impl<S, L, P> IntersectsStrategy<S, L> for IxSegmentLinestring
391where
392 S: SegmentTrait<Point = P>,
393 L: LinestringTrait<Point = P>,
394 P: PointTrait + geometry_trait::PointMut + Default,
395 P::Scalar: CoordinateScalar,
396 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
397{
398 #[inline]
399 fn intersects(&self, s: &S, ls: &L) -> bool {
400 IxLinestringSegment.intersects(ls, s)
401 }
402}
403
404impl<G, P> IntersectsStrategy<G, P> for IxPolygonPoint
405where
406 G: PolygonTrait<Point = P>,
407 P: PointTrait,
408 P::Scalar: CoordinateScalar,
409 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
410{
411 #[inline]
412 fn intersects(&self, pg: &G, p: &P) -> bool {
413 IxPointPolygon.intersects(p, pg)
414 }
415}
416
417impl<G, L, P> IntersectsStrategy<G, L> for IxPolygonLinestring
418where
419 G: PolygonTrait<Point = P>,
420 L: LinestringTrait<Point = P>,
421 P: PointTrait,
422 P::Scalar: CoordinateScalar,
423 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
424{
425 #[inline]
426 fn intersects(&self, pg: &G, ls: &L) -> bool {
427 IxLinestringPolygon.intersects(ls, pg)
428 }
429}
430
431#[doc(hidden)]
438pub trait IntersectsPairStrategy<K2> {
439 type S: Default;
441}
442
443impl IntersectsPairStrategy<PointTag> for PointTag {
444 type S = IxPointPoint;
445}
446impl IntersectsPairStrategy<SegmentTag> for PointTag {
447 type S = IxPointSegment;
448}
449impl IntersectsPairStrategy<SegmentTag> for SegmentTag {
450 type S = IxSegmentSegment;
451}
452impl IntersectsPairStrategy<SegmentTag> for LinestringTag {
453 type S = IxLinestringSegment;
454}
455impl IntersectsPairStrategy<LinestringTag> for LinestringTag {
456 type S = IxLinestringLinestring;
457}
458impl IntersectsPairStrategy<PolygonTag> for PointTag {
459 type S = IxPointPolygon;
460}
461impl IntersectsPairStrategy<PolygonTag> for LinestringTag {
462 type S = IxLinestringPolygon;
463}
464impl IntersectsPairStrategy<PolygonTag> for PolygonTag {
465 type S = IxPolygonPolygon;
466}
467impl IntersectsPairStrategy<PointTag> for SegmentTag {
468 type S = IxSegmentPoint;
469}
470impl IntersectsPairStrategy<LinestringTag> for SegmentTag {
471 type S = IxSegmentLinestring;
472}
473impl IntersectsPairStrategy<PointTag> for PolygonTag {
474 type S = IxPolygonPoint;
475}
476impl IntersectsPairStrategy<LinestringTag> for PolygonTag {
477 type S = IxPolygonLinestring;
478}
479
480#[inline]
484fn points_equal<A, B>(a: &A, b: &B, dim: usize) -> bool
485where
486 A: PointTrait,
487 B: PointTrait<Scalar = A::Scalar>,
488{
489 let mut i = 0;
490 while i < dim {
491 let eq = match i {
494 0 => a.get::<0>() == b.get::<0>(),
495 1 => a.get::<1>() == b.get::<1>(),
496 2 => a.get::<2>() == b.get::<2>(),
497 _ => true,
498 };
499 if !eq {
500 return false;
501 }
502 i += 1;
503 }
504 true
505}
506
507fn point_on_segment<P>(p: &P, s1: &P, s2: &P) -> bool
513where
514 P: PointTrait,
515 P::Scalar: CoordinateScalar,
516{
517 let px = p.get::<0>();
518 let py = p.get::<1>();
519 let ax = s1.get::<0>();
520 let ay = s1.get::<1>();
521 let bx = s2.get::<0>();
522 let by = s2.get::<1>();
523 let cross = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
524 if cross != P::Scalar::ZERO {
525 return false;
526 }
527 let (xlo, xhi) = if ax <= bx { (ax, bx) } else { (bx, ax) };
528 let (ylo, yhi) = if ay <= by { (ay, by) } else { (by, ay) };
529 xlo <= px && px <= xhi && ylo <= py && py <= yhi
530}
531
532fn segments_intersect<P>(p1: &P, p2: &P, p3: &P, p4: &P) -> bool
537where
538 P: PointTrait,
539 P::Scalar: CoordinateScalar,
540{
541 let x1 = p1.get::<0>();
542 let y1 = p1.get::<1>();
543 let x2 = p2.get::<0>();
544 let y2 = p2.get::<1>();
545 let x3 = p3.get::<0>();
546 let y3 = p3.get::<1>();
547 let x4 = p4.get::<0>();
548 let y4 = p4.get::<1>();
549
550 let d1 = side_sign((x3, y3), (x4, y4), (x1, y1));
551 let d2 = side_sign((x3, y3), (x4, y4), (x2, y2));
552 let d3 = side_sign((x1, y1), (x2, y2), (x3, y3));
553 let d4 = side_sign((x1, y1), (x2, y2), (x4, y4));
554
555 if ((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)) {
558 return true;
559 }
560
561 if d1 == 0 && point_on_segment(p1, p3, p4) {
565 return true;
566 }
567 if d2 == 0 && point_on_segment(p2, p3, p4) {
568 return true;
569 }
570 if d3 == 0 && point_on_segment(p3, p1, p2) {
571 return true;
572 }
573 if d4 == 0 && point_on_segment(p4, p1, p2) {
574 return true;
575 }
576 false
577}
578
579#[inline]
583fn side_sign<T: CoordinateScalar>(a: (T, T), b: (T, T), c: (T, T)) -> i32 {
584 let v = (b.0 - a.0) * (c.1 - a.1) - (b.1 - a.1) * (c.0 - a.0);
585 if v > T::ZERO {
586 1
587 } else if v < T::ZERO {
588 -1
589 } else {
590 0
591 }
592}
593
594fn linestring_crosses_ring<L, R, P>(ls: &L, r: &R) -> bool
597where
598 L: LinestringTrait<Point = P>,
599 R: RingTrait<Point = P>,
600 P: PointTrait,
601 P::Scalar: CoordinateScalar,
602{
603 let mut ils = ls.points();
604 let Some(mut pls) = ils.next() else {
605 return false;
606 };
607 for qls in ils {
608 if ring_edge_crosses_segment(r, pls, qls) {
609 return true;
610 }
611 pls = qls;
612 }
613 false
614}
615
616fn ring_edge_crosses_segment<R, P>(r: &R, pls: &P, qls: &P) -> bool
618where
619 R: RingTrait<Point = P>,
620 P: PointTrait,
621 P::Scalar: CoordinateScalar,
622{
623 let mut ir = r.points();
624 let Some(mut pr) = ir.next() else {
625 return false;
626 };
627 let first = pr;
628 for qr in ir {
629 if segments_intersect(pls, qls, pr, qr) {
630 return true;
631 }
632 pr = qr;
633 }
634 segments_intersect(pls, qls, pr, first)
639}
640
641fn rings_cross<Ra, Rb, P>(a: &Ra, b: &Rb) -> bool
646where
647 Ra: RingTrait<Point = P>,
648 Rb: RingTrait<Point = P>,
649 P: PointTrait,
650 P::Scalar: CoordinateScalar,
651{
652 let edges_a = ring_edges(a);
653 let edges_b = ring_edges(b);
654 for (pa, qa) in &edges_a {
655 for (pb, qb) in &edges_b {
656 if segments_intersect(*pa, *qa, *pb, *qb) {
657 return true;
658 }
659 }
660 }
661 false
662}
663
664fn ring_edges<R>(r: &R) -> alloc::vec::Vec<(&R::Point, &R::Point)>
668where
669 R: RingTrait,
670 R::Point: PointTrait,
671{
672 let pts: alloc::vec::Vec<&R::Point> = r.points().collect();
673 let mut out = alloc::vec::Vec::with_capacity(pts.len());
674 if pts.len() < 2 {
675 return out;
676 }
677 for w in pts.windows(2) {
678 out.push((w[0], w[1]));
679 }
680 out.push((*pts.last().unwrap(), *pts.first().unwrap()));
681 out
682}
683
684#[cfg(test)]
685mod tests {
686 use super::{CartesianIntersects, IntersectsStrategy, Reversed};
691 use geometry_cs::Cartesian;
692 use geometry_model::{Point2D, Polygon, Segment, linestring, polygon};
693
694 type P = Point2D<f64, Cartesian>;
695
696 fn pt(x: f64, y: f64) -> P {
697 Point2D::new(x, y)
698 }
699
700 use geometry_model::Linestring;
701 type LS = Linestring<P>;
702
703 #[test]
705 fn ls_crosses_segment() {
706 let ls: LS = linestring![(1.0, 1.0), (3.0, 3.0), (2.0, 5.0)];
707 let s = Segment::new(pt(2.0, 0.0), pt(2.0, 6.0));
708 assert!(CartesianIntersects.intersects(&ls, &s));
709 }
710
711 #[test]
713 fn ls_touches_segment_endpoint() {
714 let ls: LS = linestring![(1.0, 1.0), (3.0, 3.0)];
715 let s = Segment::new(pt(1.0, 0.0), pt(1.0, 1.0));
716 assert!(CartesianIntersects.intersects(&ls, &s));
717 }
718
719 #[test]
721 fn ls_disjoint_from_segment() {
722 let ls: LS = linestring![(1.0, 1.0), (3.0, 3.0)];
723 let s = Segment::new(pt(3.0, 0.0), pt(4.0, 1.0));
724 assert!(!CartesianIntersects.intersects(&ls, &s));
725 }
726
727 #[test]
729 fn ls_crosses_ls() {
730 let a: LS = linestring![(0.0, 0.0), (2.0, 0.0), (3.0, 0.0)];
731 let b: LS = linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)];
732 assert!(CartesianIntersects.intersects(&a, &b));
733 }
734
735 #[test]
737 fn ls_overlap_collinear() {
738 let a: LS = linestring![(0.0, 0.0), (2.0, 0.0), (3.0, 0.0)];
739 let b: LS = linestring![(1.0, 0.0), (4.0, 0.0), (5.0, 0.0)];
740 assert!(CartesianIntersects.intersects(&a, &b));
741 }
742
743 #[test]
745 fn ls_inside_polygon() {
746 let ls: LS = linestring![(1.0, 1.0), (2.0, 2.0)];
747 let p: Polygon<P> = polygon![[
748 (0.0, 0.0),
749 (10.0, 0.0),
750 (10.0, 10.0),
751 (0.0, 10.0),
752 (0.0, 0.0)
753 ]];
754 assert!(CartesianIntersects.intersects(&ls, &p));
755 }
756
757 #[test]
759 fn ls_outside_polygon() {
760 let ls: LS = linestring![(11.0, 0.0), (12.0, 12.0)];
761 let p: Polygon<P> = polygon![[
762 (0.0, 0.0),
763 (10.0, 0.0),
764 (10.0, 10.0),
765 (0.0, 10.0),
766 (0.0, 0.0)
767 ]];
768 assert!(!CartesianIntersects.intersects(&ls, &p));
769 }
770
771 #[test]
773 fn reversed_pair_compiles_and_agrees() {
774 let ls: LS = linestring![(1.0, 1.0), (2.0, 2.0)];
775 let p: Polygon<P> = polygon![[
776 (0.0, 0.0),
777 (10.0, 0.0),
778 (10.0, 10.0),
779 (0.0, 10.0),
780 (0.0, 0.0)
781 ]];
782 let forward = CartesianIntersects.intersects(&ls, &p);
783 let reversed = Reversed(CartesianIntersects).intersects(&p, &ls);
784 assert_eq!(forward, reversed);
785 }
786
787 fn _accepts_readonly_point<A, B, S>(s: &S, a: &A, b: &B) -> bool
791 where
792 A: geometry_trait::Point,
793 B: geometry_trait::Point,
794 S: IntersectsStrategy<A, B>,
795 {
796 s.intersects(a, b)
797 }
798}