Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
intersects.hpp
Go to the documentation of this file.
1#pragma once
2
4
9
10#include <limits>
13
14
15namespace pgl {
16
22
23template <class Number, class Label>
24template<PointConcept OtherPoint>
25constexpr bool Point<Number, Label>::intersects(const OtherPoint& other) const {
26 return contains(other);
27}
28
29
30template <class Number, class Label>
31constexpr bool Point<Number, Label>::intersects(const Shape<Point<Number, Label>>& other) const {
32 return std::visit(
33 [this](const auto& value) {
34 return this->intersects(value);
35 },
36 other.variant());
37}
38
45
46template <class PointType, class LabelType>
47template<PointConcept OtherPoint>
48constexpr bool Segment<PointType, LabelType>::intersects(const OtherPoint& other) const {
49 return contains(other);
50}
51
52template <class PointType, class LabelType>
53template<SegmentConcept OtherSegment>
54constexpr bool Segment<PointType, LabelType>::intersects(const OtherSegment& other) const {
55 using Coordinate = detail::sign_coordinate_t<NumberType, typename OtherSegment::NumberType>;
56
57 // A segment pair needs four orientation signs, but each of its four
58 // endpoints occurs in three of them. Filtering the endpoints once has an
59 // ERational-to-double conversion happen once per coordinate rather than
60 // three times, and every sign the filter proves is one the exact arithmetic
61 // below never evaluates. Where the filter would not pay for itself the
62 // wrappers carry nothing but the points, and this is the plain sequence of
63 // exact orientation tests.
64 const auto a = detail::filtered<Coordinate>(min());
65 const auto b = detail::filtered<Coordinate>(max());
66 const auto c = detail::filtered<Coordinate>(other.min());
67 const auto d = detail::filtered<Coordinate>(other.max());
68 const auto s1 = detail::orientationSignOf(a, b, c);
69 const auto s2 = detail::orientationSignOf(a, b, d);
70 const auto s3 = detail::orientationSignOf(c, d, a);
71 const auto s4 = detail::orientationSignOf(c, d, b);
72
73 // Four proved signs are all nonzero and decide the predicate outright, so
74 // they also spare the costly exact rational bounding-box comparisons.
75 if (detail::allDecided(s1, s2, s3, s4)) {
76 return s1.value() != s2.value() && s3.value() != s4.value();
77 }
78
80 const int cross = boundingBoxesCross(other);
81 if (cross == 0) {
82 return false;
83 }
84 if (cross == 2) {
85 return true;
86 }
87 }
88 else if (!boundingBoxesOverlap(other)) {
89 return false;
90 }
91
92 // A possible zero always reaches here, so the closed-boundary checks stay
93 // exact.
94 const auto d1 = s1.value();
95 if (d1 == 0 && containsCollinear(other.min())) {
96 return true;
97 }
98 const auto d2 = s2.value();
99 if (d2 == 0 && containsCollinear(other.max())) {
100 return true;
101 }
102 const auto d3 = s3.value();
103 if (d3 == 0 && other.containsCollinear(min())) {
104 return true;
105 }
106 const auto d4 = s4.value();
107 if (d4 == 0 && other.containsCollinear(max())) {
108 return true;
109 }
110 if (d1 == 0 || d2 == 0 || d3 == 0 || d4 == 0) {
111 return false;
112 }
113 return d1 != d2 && d3 != d4;
114}
115
116
117template <class PointType, class LabelType>
119 return std::visit(
120 [this](const auto& value) {
121 return this->intersects(value);
122 },
123 other.variant());
124}
125
131
132template <class PointType, class LabelType>
133template<PointConcept OtherPoint>
134constexpr bool Triangle<PointType, LabelType>::intersects(const OtherPoint& other) const {
135 return contains(other);
136}
137
138template <class PointType, class LabelType>
139template<LineConcept OtherLine>
140constexpr bool Triangle<PointType, LabelType>::intersects(const OtherLine& other) const {
141 if (other.isDegenerate()) {
142 return contains(other.min());
143 }
144 // The line meets the closed triangle unless all three vertices lie
145 // strictly on one side of it.
146 bool positive = false, negative = false;
147 for (auto p : *this) {
148 auto o = orientationSign(other[0], other[1], p);
149 if (o == 0) {
150 return true;
151 }
152 negative = (negative || o < 0);
153 positive = (positive || o > 0);
154 if (positive && negative) {
155 return true;
156 }
157 }
158 return false;
159}
160
161template <class PointType, class LabelType>
162template<OrientedLineConcept OtherOrientedLine>
163constexpr bool Triangle<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
165}
166
167template <class PointType, class LabelType>
168template<SegmentConcept OtherSegment>
169constexpr bool Triangle<PointType, LabelType>::intersects(const OtherSegment& other) const {
170 // Either an endpoint lies in the closed triangle, or the segment crosses
171 // one of the triangle's edges.
172 if (contains(other.min()) || contains(other.max())) {
173 return true;
174 }
175 for (const auto& edge : edges()) {
176 if (edge.intersects(other)) {
177 return true;
178 }
179 }
180 return false;
181}
182
183template <class PointType, class LabelType>
184template<OrientedSegmentConcept OtherOrientedSegment>
185constexpr bool Triangle<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
187}
188
189template <class PointType, class LabelType>
190template<RayConcept OtherRay>
191constexpr bool Triangle<PointType, LabelType>::intersects(const OtherRay& other) const {
192 // Either the source lies in the closed triangle, or the ray crosses an edge.
193 if (contains(other.source())) {
194 return true;
195 }
196 for (const auto& edge : edges()) {
197 if (edge.intersects(other)) {
198 return true;
199 }
200 }
201 return false;
202}
203
204template <class PointType, class LabelType>
205template<HalfplaneConcept OtherHalfplane>
206constexpr bool Triangle<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
207 if (other.contains(a()) || other.contains(b()) || other.contains(c())) {
208 return true;
209 }
210 for (const auto& edge : edges()) {
211 if (other.intersects(edge)) {
212 return true;
213 }
214 }
215 return false;
216}
217
218template <class PointType, class LabelType>
219template<RectangleConcept OtherRectangle>
220constexpr bool Triangle<PointType, LabelType>::intersects(const OtherRectangle& other) const {
221 if (other.empty()) {
222 // The empty set meets nothing and disconnects nothing.
223 return false;
224 }
225 // Cheapest rejection first: the two bounding boxes must overlap. Without
226 // it a rectangle off to one side still pays the corner containment below,
227 // which the three edge tests can never short-circuit.
228 if (!other.intersects(bbox())) {
229 return false;
230 }
231 // Convex against convex. If no edge of the triangle meets the rectangle
232 // then the rectangle, being connected, lies wholly inside the triangle or
233 // wholly outside it, and one of its corners settles which. Going through
234 // the vertex pairs rather than through 'edges()' keeps the three segments
235 // from being built at all.
236 return detail::segmentIntersectsRectangle(other, a(), b()) ||
237 detail::segmentIntersectsRectangle(other, b(), c()) ||
238 detail::segmentIntersectsRectangle(other, c(), a()) ||
239 contains(other.min());
240}
241
242template <class PointType, class LabelType>
243template<TriangleConcept OtherTriangle>
244constexpr bool Triangle<PointType, LabelType>::intersects(const OtherTriangle& other) const {
245 if (contains(other.a()) || contains(other.b()) || contains(other.c()) ||
246 other.contains(a()) || other.contains(b()) || other.contains(c())) {
247 return true;
248 }
249 const auto this_edges = edges();
250 const auto other_edges = other.edges();
251 for (const auto& left : this_edges) {
252 for (const auto& right : other_edges) {
253 if (left.intersects(right)) {
254 return true;
255 }
256 }
257 }
258 return false;
259}
260
261template <class PointType, class LabelType>
263 return std::visit(
264 [this](const auto& value) {
265 return this->intersects(value);
266 },
267 other.variant());
268}
269
275
276template <class PointType, class LabelType>
277template<PointConcept OtherPoint>
278constexpr bool OrientedSegment<PointType, LabelType>::intersects(const OtherPoint& other) const {
279 return static_cast<Segment<PointType>>(*this).intersects(other);
280}
281
282template <class PointType, class LabelType>
283template<SegmentConcept OtherSegment>
284constexpr bool OrientedSegment<PointType, LabelType>::intersects(const OtherSegment& other) const {
285 return static_cast<Segment<PointType>>(*this).intersects(other);
286}
287
288template <class PointType, class LabelType>
289template<OrientedSegmentConcept OtherOrientedSegment>
290constexpr bool OrientedSegment<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
291 return static_cast<Segment<PointType>>(*this).intersects(static_cast<Segment<typename OtherOrientedSegment::PointType>>(other));
292}
293
294
295template <class PointType, class LabelType>
297 return std::visit(
298 [this](const auto& value) {
299 return this->intersects(value);
300 },
301 other.variant());
302}
303
309
310template <class PointType, class LabelType>
311template<PointConcept OtherPoint>
312constexpr bool Line<PointType, LabelType>::intersects(const OtherPoint& other) const {
313 return contains(other);
314}
315
316template <class PointType, class LabelType>
317template<LineConcept OtherLine>
318constexpr bool Line<PointType, LabelType>::intersects(const OtherLine& other) const {
319 if (isDegenerate()) {
320 return other.contains(min());
321 }
322 if (other.isDegenerate()) {
323 return contains(other.min());
324 }
325 return !parallel(other) || contains(other.min());
326}
327
328template <class PointType, class LabelType>
329template<SegmentConcept OtherSegment>
330constexpr bool Line<PointType, LabelType>::intersects(const OtherSegment& other) const {
331 if (other.isDegenerate()) {
332 return contains(other.min());
333 }
334 const auto first = orientationSign(min(), max(), other.min());
335 const auto second = orientationSign(min(), max(), other.max());
336 return first == std::partial_ordering::equivalent ||
337 second == std::partial_ordering::equivalent ||
338 first != second;
339}
340
341template <class PointType, class LabelType>
342template<OrientedSegmentConcept OtherOrientedSegment>
343constexpr bool Line<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
345}
346
347template <class PointType, class LabelType>
349 return std::visit(
350 [this](const auto& value) {
351 return this->intersects(value);
352 },
353 other.variant());
354}
355
361
362template <class PointType, class LabelType>
363template<PointConcept OtherPoint>
364constexpr bool OrientedLine<PointType, LabelType>::intersects(const OtherPoint& other) const {
365 return this->asLine().intersects(other);
366}
367
368template <class PointType, class LabelType>
369template<LineConcept OtherLine>
370constexpr bool OrientedLine<PointType, LabelType>::intersects(const OtherLine& other) const {
371 return this->asLine().intersects(other);
372}
373
374template <class PointType, class LabelType>
375template<OrientedLineConcept OtherOrientedLine>
376constexpr bool OrientedLine<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
377 return this->asLine().intersects(other.asLine());
378}
379
380template <class PointType, class LabelType>
381template<SegmentConcept OtherSegment>
382constexpr bool OrientedLine<PointType, LabelType>::intersects(const OtherSegment& other) const {
383 return this->asLine().intersects(other);
384}
385
386template <class PointType, class LabelType>
387template<OrientedSegmentConcept OtherOrientedSegment>
388constexpr bool OrientedLine<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
389 return this->asLine().intersects(other);
390}
391
392template <class PointType, class LabelType>
394 return std::visit(
395 [this](const auto& value) {
396 return this->intersects(value);
397 },
398 other.variant());
399}
400
406
407template <class PointType, class LabelType>
408template<PointConcept OtherPoint>
409constexpr bool Ray<PointType, LabelType>::intersects(const OtherPoint& other) const {
410 return contains(other);
411}
412
413template <class PointType, class LabelType>
414template<LineConcept OtherLine>
415constexpr bool Ray<PointType, LabelType>::intersects(const OtherLine& other) const {
416 if (other.isDegenerate()) {
417 return contains(other.min());
418 }
419 const auto source_side = orientationSign(other.min(), other.max(), source());
420 if (source_side == 0) {
421 return true; // source lies on the line
422 }
423 // As the ray runs to infinity it tends to the side given by its direction.
424 // It meets the line exactly when that side is opposite the source's (a
425 // forward crossing); parallel (equivalent) or same-side rays never reach it.
426 const auto direction_side =
427 orientationSign(other.min(), other.max(), other.min() + (target() - source()));
428 return direction_side != 0 && direction_side != source_side;
429}
430
431template <class PointType, class LabelType>
432template<OrientedLineConcept OtherOrientedLine>
433constexpr bool Ray<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
434 return intersects(other.asLine());
435}
436
437template <class PointType, class LabelType>
438template<SegmentConcept OtherSegment>
439constexpr bool Ray<PointType, LabelType>::intersects(const OtherSegment& other) const {
440 if (other.isDegenerate()) {
441 return contains(other.min());
442 }
443
444 // A segment collinear with the ray satisfies both line-vs-line tests below
445 // even when it does not overlap the ray, so handle it as a 1D overlap: they
446 // meet iff the ray reaches an endpoint of the segment, or the segment covers
447 // the ray's source.
448 if (orientationSign(source(), target(), other.min()) == 0 &&
449 orientationSign(source(), target(), other.max()) == 0) {
450 return contains(other.min()) || contains(other.max()) || other.contains(source());
451 }
452
453 return intersects(other.asLine()) &&
454 this->asLine().intersects(other);
455}
456
457template <class PointType, class LabelType>
458template<OrientedSegmentConcept OtherOrientedSegment>
459constexpr bool Ray<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
461}
462
463template <class PointType, class LabelType>
464template<RayConcept OtherRay>
465constexpr bool Ray<PointType, LabelType>::intersects(const OtherRay& other) const {
466 if (isDegenerate()) {
467 return other.contains(source());
468 }
469 if (other.isDegenerate()) {
470 return contains(other.source());
471 }
472 // Collinear rays both satisfy the line-vs-line test below even when they do
473 // not overlap, so handle them as a 1D overlap: the rays meet iff one reaches
474 // the other's source.
475 if (orientationSign(source(), target(), other.source()) == 0 &&
476 orientationSign(source(), target(), other.target()) == 0) {
477 return contains(other.source()) || other.contains(source());
478 }
479 // Otherwise the supporting lines meet in at most one point; the rays meet
480 // iff that point lies on both (each ray reaches the other's line).
481 return intersects(other.asLine()) &&
482 this->asLine().intersects(other);
483}
484
485template <class PointType, class LabelType>
486constexpr bool Ray<PointType, LabelType>::intersects(const Shape<PointType>& other) const {
487 return std::visit(
488 [this](const auto& value) {
489 return this->intersects(value);
490 },
491 other.variant());
492}
493
499
500template <class PointType, class LabelType>
501template<PointConcept OtherPoint>
502constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherPoint& other) const {
503 // The empty set meets nothing, and it needs no case of its own: it contains
504 // no point, so the test below is already false for it.
505 return contains(other);
506}
507
508template <class PointType, class LabelType>
509template<RectangleConcept OtherRectangle>
510constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherRectangle& other) const {
511 // The empty set meets nothing, and the inverted corners of an empty
512 // rectangle can pass both interval tests, so emptiness has to be ruled out
513 // before answering true. It trails the geometry rather than guarding the
514 // function because false is the common answer, and that path then never
515 // pays for the check.
516 return intervalsOverlap(min().x(), max().x(), other.min().x(), other.max().x()) &&
517 intervalsOverlap(min().y(), max().y(), other.min().y(), other.max().y()) &&
518 !empty() && !other.empty();
519}
520
521template <class PointType, class LabelType>
522template<LineConcept OtherLine>
523constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherLine& other) const {
524 if (empty()) {
525 // The empty set meets nothing and disconnects nothing.
526 return false;
527 }
528 if (other.isDegenerate()) {
529 return contains(other.min());
530 }
531 return detail::lineIntersectsRectangle(*this, other.min(), other.max());
532}
533
534template <class PointType, class LabelType>
535template<OrientedLineConcept OtherOrientedLine>
536constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
537 if (empty()) {
538 // The empty set meets nothing and disconnects nothing.
539 return false;
540 }
541 if (other.isDegenerate()) {
542 return contains(other.source());
543 }
544 return detail::lineIntersectsRectangle(*this, other.source(), other.target());
545}
546
547template <class PointType, class LabelType>
548template<SegmentConcept OtherSegment>
549constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherSegment& other) const {
550 if (empty()) {
551 // The empty set meets nothing and disconnects nothing.
552 return false;
553 }
554 return detail::segmentIntersectsRectangle(*this, other.min(), other.max());
555}
556
557template <class PointType, class LabelType>
558template<OrientedSegmentConcept OtherOrientedSegment>
559constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
560 if (empty()) {
561 // The empty set meets nothing and disconnects nothing.
562 return false;
563 }
564 return detail::segmentIntersectsRectangle(*this, other.source(), other.target());
565}
566
567template <class PointType, class LabelType>
568template<RayConcept OtherRay>
569constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherRay& other) const {
570 if (empty()) {
571 // The empty set meets nothing and disconnects nothing.
572 return false;
573 }
574 if (contains(other.source())) {
575 return true;
576 }
577 const auto rectangle_edges = edges();
578 for (const auto& edge : rectangle_edges) {
579 if (other.intersects(edge)) {
580 return true;
581 }
582 }
583 return false;
584}
585
586template <class PointType, class LabelType>
587template<HalfplaneConcept OtherHalfplane>
588constexpr bool Rectangle<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
589 if (empty()) {
590 // The empty set meets nothing and disconnects nothing.
591 return false;
592 }
593 if (other.isDegenerate()) {
594 return contains(other.source());
595 }
596 const auto rectangle_vertices = vertices();
597 for (const auto& vertex : rectangle_vertices) {
598 if (other.contains(vertex)) {
599 return true;
600 }
601 }
602 return false;
603}
604
605template <class PointType, class LabelType>
607 return std::visit(
608 [this](const auto& value) {
609 return this->intersects(value);
610 },
611 other.variant());
612}
613
619
620template <class PointType, class LabelType>
621template<PointConcept OtherPoint>
622constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherPoint& other) const {
623 return contains(other);
624}
625
626template <class PointType, class LabelType>
627template<LineConcept OtherLine>
628constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherLine& other) const {
629 if (isDegenerate()) {
630 return other.contains(source());
631 }
632 if (other.isDegenerate()) {
633 return contains(other.min());
634 }
635 // Not parallel to the boundary and the line must cross it; the parallel
636 // test is the sign of the difference of the two endpoint determinants,
637 // which is one cross product of the two directions.
638 return crossSign(source(), target(), other.min(), other.max()) != 0 ||
639 contains(other.min());
640}
641
642template <class PointType, class LabelType>
643template<OrientedLineConcept OtherOrientedLine>
644constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
645 if (isDegenerate()) {
646 return other.contains(source());
647 }
648 if (other.isDegenerate()) {
649 return contains(other.source());
650 }
651 return crossSign(source(), target(), other.source(), other.target()) != 0 ||
652 contains(other.source());
653}
654
655template <class PointType, class LabelType>
656template<SegmentConcept OtherSegment>
657constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherSegment& other) const {
658 if (isDegenerate()) {
659 return other.contains(source());
660 }
661 return contains(other.min()) || contains(other.max());
662}
663
664template <class PointType, class LabelType>
665template<OrientedSegmentConcept OtherOrientedSegment>
666constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
667 if (isDegenerate()) {
668 return other.contains(source());
669 }
670 return contains(other.source()) || contains(other.target());
671}
672
673template <class PointType, class LabelType>
674template<RayConcept OtherRay>
675constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherRay& other) const {
676 if (isDegenerate()) {
677 return other.contains(source());
678 }
679 if (other.isDegenerate()) {
680 return contains(other.source());
681 }
682 return !(orientationSign(source(), target(), other.source()) < 0) ||
683 crossSign(source(), target(), other.source(), other.target()) > 0;
684}
685
686template <class PointType, class LabelType>
687template<HalfplaneConcept OtherHalfplane>
688constexpr bool Halfplane<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
689 // Crossing boundaries leave a wedge in both, and that wedge can lie
690 // arbitrarily far from either boundary's two defining points, so the four
691 // point tests below decide nothing here. They do decide the parallel case:
692 // two parallel half-planes meet exactly when one boundary point lies in the
693 // other half-plane, whether they are nested or face each other across a
694 // slab.
695 if (!isDegenerate() && !other.isDegenerate() && !asLine().parallel(other.asLine())) {
696 return true;
697 }
698 return contains(other.source()) || contains(other.target()) || other.contains(source()) || other.contains(target());
699}
700
701template <class PointType, class LabelType>
703 return std::visit(
704 [this](const auto& value) {
705 return this->intersects(value);
706 },
707 other.variant());
708}
709
710
711// ---------------------------------------------------------------------------
712// Convex
713
714template <class PointType, class LabelType>
715template<SegmentConcept OtherSegment>
716constexpr bool Convex<PointType, LabelType>::intersects(const OtherSegment& other) const {
717 if (size() == 0 || !bbox().intersects(other.bbox())) {
718 return false;
719 }
720 if (contains(other.min()) || contains(other.max())) {
721 return true;
722 }
723 auto translatedOther = other - translation_;
724 auto it1 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
725 return orientationDeterminant(translatedOther[0], translatedOther[1], a);
726 });
727 auto it2 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
728 return orientationDeterminant(translatedOther[1], translatedOther[0], a);
729 });
730 Segment<PointType> s(*it1, *it2);
731 return s.intersects(translatedOther);
732}
733
734template <class PointType, class LabelType>
735template<OrientedSegmentConcept OtherOrientedSegment>
736constexpr bool Convex<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
738}
739
740template <class PointType, class LabelType>
741template<LineConcept OtherLine>
742constexpr bool Convex<PointType, LabelType>::intersects(const OtherLine& other) const {
743 auto translatedOther = other - translation_;
744 auto it1 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
745 return orientationDeterminant(translatedOther[0], translatedOther[1], a);
746 });
747 auto it2 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
748 return orientationDeterminant(translatedOther[1], translatedOther[0], a);
749 });
750 Segment<PointType> s(*it1, *it2);
751 return s.intersects(translatedOther);
752}
753
754template <class PointType, class LabelType>
755template<OrientedLineConcept OtherOrientedLine>
756constexpr bool Convex<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
758}
759
760template <class PointType, class LabelType>
761template<RayConcept OtherRay>
762constexpr bool Convex<PointType, LabelType>::intersects(const OtherRay& other) const {
763 if (contains(other.source())) {
764 return true;
765 }
766
767 auto translatedOther = other - translation_;
768 auto it1 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
769 return orientationDeterminant(translatedOther[0], translatedOther[1], a);
770 });
771 auto it2 = detail::cyclicMaxOrPositive(points_.begin(), points_.end(), [&translatedOther](const PointType& a) {
772 return orientationDeterminant(translatedOther[1], translatedOther[0], a);
773 });
774 Segment<PointType> s(*it1, *it2);
775 return s.intersects(translatedOther);
776}
777
778template <class PointType, class LabelType>
779template<RectangleConcept OtherRectangle>
780constexpr bool Convex<PointType, LabelType>::intersects(const OtherRectangle& other) const {
781 if (other.empty()) {
782 // The empty set meets nothing and disconnects nothing.
783 return false;
784 }
785 if (size() == 0 || !bbox().intersects(other)) {
786 return false;
787 }
788 if (bbox().separates(other) || other.separates(bbox())) {
789 return true;
790 }
791
792 if (other.contains(points_[0])) {
793 return true;
794 }
795
796 for (auto &edge : other.edges()) {
797 if (intersects(edge)) {
798 return true;
799 }
800 }
801
802 return false;
803}
804
805template <class PointType, class LabelType>
806template<TriangleConcept OtherTriangle>
807constexpr bool Convex<PointType, LabelType>::intersects(const OtherTriangle& other) const {
808 if (size() == 0 || !bbox().intersects(other)) {
809 return false;
810 }
811 if (bbox().separates(other.bbox()) || other.bbox().separates(bbox())) {
812 return true;
813 }
814 if (other.contains(points_[0])) {
815 return true;
816 }
817
818 for (auto &edge : other.edges()) {
819 if (intersects(edge)) {
820 return true;
821 }
822 }
823
824 return false;
825}
826
827template <class PointType, class LabelType>
828template<PointConcept OtherPoint>
829constexpr bool Convex<PointType, LabelType>::intersects(const OtherPoint& other) const {
830 if (size() == 0) {
831 return false;
832 }
833 return bbox().contains(other) && contains(other);
834}
835
836template <class PointType, class LabelType>
837template<HalfplaneConcept OtherHalfplane>
838constexpr bool Convex<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
839 if (points_.empty()) {
840 return false;
841 }
842 if (other.isDegenerate()) {
843 return contains(other.source());
844 }
845 // The vertex maximizing the inward orientation is the deepest into
846 // the half-plane; if it is not in the closed half-plane, no other
847 // vertex is either. The orientation determinant is unimodal along
848 // the convex polygon's vertex sequence.
849 const auto it = detail::cyclicMaxOrPositive(points_.begin(), points_.end(),
850 [&other, this](const PointType& a) {
851 return orientationDeterminant(other.source(), other.target(), a + translation_);
852 });
853 return other.contains(*it + translation_);
854}
855
856template <class PointType, class LabelType>
857template<ConvexConcept OtherConvex>
858constexpr bool Convex<PointType, LabelType>::intersects(const OtherConvex& other) const {
859 if (size() > other.size()) {
860 return other.intersects(*this);
861 }
862 if (size() == 0 || other.size() == 0) {
863 return false;
864 }
865 if (!bbox().intersects(other.bbox())) {
866 return false;
867 }
868 if (bbox().separates(other.bbox()) || other.bbox().separates(this->bbox())) {
869 return true;
870 }
871
872 if (contains(other[0]) || other.contains((*this)[0])) {
873 return true;
874 }
875
876 for (const auto& edge : edgesView()) {
877 if (other.intersects(edge)) {
878 return true;
879 }
880 }
881
882 return false;
883}
884
885template <class PointType, class LabelType>
886template<DiskConcept OtherDisk>
887constexpr bool Convex<PointType, LabelType>::intersects(const OtherDisk& other) const {
888 if (contains(other[0])) {
889 return true;
890 }
891 for (const auto& edge : edgesView()) {
892 if (edge.intersects(other)) {
893 return true;
894 }
895 }
896 return false;
897}
898
899// ---------------------------------------------------------------------------
900// Disk
901
902template <class PointType, class LabelType>
903template<SegmentConcept OtherSegment>
904constexpr bool Disk<PointType, LabelType>::intersects(const OtherSegment& other) const {
905 if (const auto point = getIfPoint()) {
906 // A radius-zero disk is its centre; the in-circle formulation below
907 // needs three non-collinear boundary points to be meaningful, and on
908 // three equal ones every determinant vanishes and both of its tests
909 // come out true.
910 return other.contains(*point);
911 }
912 if (contains(other.min()) || contains(other.max())) {
913 return true;
914 }
915
916 // Both endpoints lie strictly outside the closed disk, so the segment meets
917 // the disk exactly when the perpendicular foot from the centre falls on the
918 // segment *and* reaches inside it. This is decided exactly and without any
919 // division (no centre/radius needed): writing power(p) = |p-center|^2 - r^2
920 // for the in-circle determinant inCircleDeterminant(a,b,c,p) = -A*power(p),
921 // with A = 2*signedArea(a,b,c), the quadratic power along the segment is
922 // pinned by its endpoint values, so the centre never appears.
923 //
924 // A = orientation determinant of the three boundary points
925 // J0 = inCircleDeterminant(a,b,c, min), J1 = ... (a,b,c, max)
926 // L = |max - min|^2, M = L * A
927 // Foot on the segment (t* in [0,1]): |(J0 - J1)*A| <= M*A (M*A >= 0).
928 // Foot reaches the disk (f(t*) <= 0): (J0 + J1 + M)^2 >= 4*J0*J1.
929 // Reordering a,b,c flips A and every J together, leaving both tests intact.
930 using W = detail::promoted_number_t<
931 decltype(inCircleDeterminant(a(), b(), c(), other.min()))>;
932
933 const W det = static_cast<W>(orientationDeterminant(a(), b(), c()));
934 const W j0 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other.min()));
935 const W j1 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other.max()));
936 const W squared_length = other.min().template squaredDistance<W>(other.max());
937 if (squared_length == W{}) {
938 return false; // Degenerate (point) segment whose sole point is outside.
939 }
940 const W m = squared_length * det;
941
942 const W projection = (j0 - j1) * det; // (J0 - J1) * A
943 const W half_span = m * det; // M * A = L * A^2 >= 0
944 const W discriminant_base = j0 + j1 + m; // J0 + J1 + M
945
946 const bool foot_on_segment = projection >= -half_span && projection <= half_span;
947 const bool reaches_disk = discriminant_base * discriminant_base >= W{4} * j0 * j1;
948
949 return foot_on_segment && reaches_disk;
950}
951
952template <class PointType, class LabelType>
953template<DiskConcept OtherDisk>
954constexpr bool Disk<PointType, LabelType>::intersects(const OtherDisk& other) const {
955 using R = std::conditional_t<
956 std::is_floating_point_v<NumberType> ||
957 std::is_floating_point_v<typename OtherDisk::NumberType>,
958 long double,
960
961 const R d2 = center<R>().template squaredDistance<R>(other.template center<R>());
962 const R r1_sq = squaredRadius<R>();
963 const R r2_sq = other.template squaredRadius<R>();
964
965 const R A = d2 - r1_sq - r2_sq;
966 return A <= R{} || A * A <= R{4} * r1_sq * r2_sq;
967}
968
969template <class PointType, class LabelType>
970template <PointConcept OtherPoint>
972 return std::visit(
973 [this](const auto& value) {
974 return this->intersects(value);
975 },
976 other.variant());
977}
978
979
980// ---------------------------------------------------------------------------
981// Polygon
982
983template <class PointType, class LabelType>
984template<PointConcept OtherPoint>
985constexpr bool Polygon<PointType, LabelType>::intersects(const OtherPoint& other) const {
986 return contains(other);
987}
988
989template <class PointType, class LabelType>
990template<SegmentConcept OtherSegment>
991constexpr bool Polygon<PointType, LabelType>::intersects(const OtherSegment& other) const {
992 // Either an endpoint lies in the closed polygon, or the segment crosses
993 // a boundary edge.
994 if (contains(other.min()) || contains(other.max())) {
995 return true;
996 }
997 for (const auto& edge : edgesView()) {
998 if (edge.intersects(other)) {
999 return true;
1000 }
1001 }
1002 return false;
1003}
1004
1005template <class PointType, class LabelType>
1006template<OrientedSegmentConcept OtherOrientedSegment>
1007constexpr bool Polygon<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
1009}
1010
1011template <class PointType, class LabelType>
1012template<LineConcept OtherLine>
1013constexpr bool Polygon<PointType, LabelType>::intersects(const OtherLine& other) const {
1014 if (other.isDegenerate()) {
1015 return contains(other.min());
1016 }
1017 // The line meets the closed polygon unless every vertex is strictly on one
1018 // side: the boundary is connected, so vertices straddling the line force an
1019 // edge crossing (and a vertex on the line is itself an intersection).
1020 bool positive = false, negative = false;
1021 for (const auto& vertex : vertices()) {
1022 const auto o = orientationSign(other.min(), other.max(), vertex);
1023 if (o == 0) {
1024 return true;
1025 }
1026 negative = negative || o < 0;
1027 positive = positive || o > 0;
1028 if (positive && negative) {
1029 return true;
1030 }
1031 }
1032 return false;
1033}
1034
1035template <class PointType, class LabelType>
1036template<OrientedLineConcept OtherOrientedLine>
1037constexpr bool Polygon<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
1038 return intersects(static_cast<Line<typename OtherOrientedLine::PointType>>(other));
1039}
1040
1041template <class PointType, class LabelType>
1042template<RayConcept OtherRay>
1043constexpr bool Polygon<PointType, LabelType>::intersects(const OtherRay& other) const {
1044 if (contains(other.source())) {
1045 return true;
1046 }
1047 for (const auto& edge : edgesView()) {
1048 if (edge.intersects(other)) {
1049 return true;
1050 }
1051 }
1052 return false;
1053}
1054
1055template <class PointType, class LabelType>
1056template<HalfplaneConcept OtherHalfplane>
1057constexpr bool Polygon<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
1058 if (other.isDegenerate()) {
1059 return contains(other.source());
1060 }
1061 // The half-plane is convex, so it meets the polygon iff some vertex lies in
1062 // it; if every vertex is strictly outside, the whole polygon is too.
1063 for (const auto& vertex : vertices()) {
1064 if (other.contains(vertex)) {
1065 return true;
1066 }
1067 }
1068 return false;
1069}
1070
1071// Two filled simple polygons meet iff a vertex of one lies in the other or a
1072// pair of their edges cross. The region overloads below all share this shape.
1073template <class PointType, class LabelType>
1074template<RectangleConcept OtherRectangle>
1075constexpr bool Polygon<PointType, LabelType>::intersects(const OtherRectangle& other) const {
1076 if (other.empty()) {
1077 // The empty set meets nothing and disconnects nothing.
1078 return false;
1079 }
1080 if (size() == 0) {
1081 return false;
1082 }
1083 if (!bbox().intersects(other.bbox())) {
1084 return false;
1085 }
1086 if (bbox().separates(other.bbox()) || other.bbox().separates(bbox())) {
1087 return true;
1088 }
1089 if (other.contains((*this)[0]) || contains(other[0])) {
1090 return true;
1091 }
1092 for (const auto& edge : edgesView()) {
1093 if (other.intersects(edge)) {
1094 return true;
1095 }
1096 }
1097 return false;
1098}
1099
1100template <class PointType, class LabelType>
1101template<TriangleConcept OtherTriangle>
1102constexpr bool Polygon<PointType, LabelType>::intersects(const OtherTriangle& other) const {
1103 if (size() == 0) {
1104 return false;
1105 }
1106 if (!bbox().intersects(other.bbox())) {
1107 return false;
1108 }
1109 if (bbox().separates(other.bbox()) || other.bbox().separates(bbox())) {
1110 return true;
1111 }
1112 if (other.contains((*this)[0]) || contains(other[0])) {
1113 return true;
1114 }
1115 for (const auto& edge : edgesView()) {
1116 if (other.intersects(edge)) {
1117 return true;
1118 }
1119 }
1120 return false;
1121}
1122
1123template <class PointType, class LabelType>
1124template<ConvexConcept OtherConvex>
1125constexpr bool Polygon<PointType, LabelType>::intersects(const OtherConvex& other) const {
1126 if (size() == 0 || other.size() == 0) {
1127 return false;
1128 }
1129 if (!bbox().intersects(other.bbox())) {
1130 return false;
1131 }
1132 if (bbox().separates(other.bbox()) || other.bbox().separates(this->bbox())) {
1133 return true;
1134 }
1135 if (other.contains((*this)[0]) || contains(other[0])) {
1136 return true;
1137 }
1138 for (const auto& edge : edgesView()) {
1139 if (other.intersects(edge)) {
1140 return true;
1141 }
1142 }
1143 return false;
1144}
1145
1146template <class PointType, class LabelType>
1147template<PolygonConcept OtherPolygon>
1148constexpr bool Polygon<PointType, LabelType>::intersects(const OtherPolygon& other) const {
1149 if (size() == 0 || other.size() == 0) {
1150 return false;
1151 }
1152 if (!bbox().intersects(other.bbox())) {
1153 return false;
1154 }
1155 if (bbox().separates(other.bbox()) || other.bbox().separates(bbox())) {
1156 return true;
1157 }
1158
1159 // The boundaries touching or crossing settles the closed intersection.
1160 if (boundariesIntersect(other)) {
1161 return true;
1162 }
1163
1164 // Disjoint boundaries: the polygons are either separate or one lies wholly
1165 // inside the other, so a single point-in-polygon test each way settles it
1166 // (every vertex of the inner polygon is contained in the outer one).
1167 return contains(other.get(0)) || other.contains(get(0));
1168}
1169
1170template <class PointType, class LabelType>
1171template<PointConcept OtherPoint>
1173 return std::visit(
1174 [this](const auto& value) {
1175 return this->intersects(value);
1176 },
1177 other.variant());
1178}
1179
1180template <class PointType, class LabelType>
1181template<PointConcept OtherPoint>
1182constexpr bool Disk<PointType, LabelType>::intersects(const OtherPoint& point) const {
1183 return contains(point);
1184}
1185
1186template <class PointType, class LabelType>
1187template<OrientedSegmentConcept OtherOrientedSegment>
1188constexpr bool Disk<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
1189 return intersects(other.asSegment());
1190}
1191
1192template <class PointType, class LabelType>
1193template<LineConcept OtherLine>
1194constexpr bool Disk<PointType, LabelType>::intersects(const OtherLine& other) const {
1195 if (const auto point = getIfPoint()) {
1196 // A radius-zero disk is its centre, which the in-circle formulation
1197 // below cannot see (see intersects(Segment)).
1198 return other.contains(*point);
1199 }
1200 // A line has no endpoints, so there is no containment shortcut: it meets the
1201 // closed disk exactly when the centre lies within one radius of the line,
1202 // i.e. when the power quadratic along the line has real roots (discriminant
1203 // >= 0). Same exact, division-free in-circle formulation as
1204 // intersects(Segment), evaluated on the two points that define the line and
1205 // without the [0,1] parameter restriction.
1206 //
1207 // A = orientation determinant of the three boundary points
1208 // J0 = inCircleDeterminant(a,b,c, other[0]), J1 = ... (a,b,c, other[1])
1209 // L = |other[1] - other[0]|^2, M = L * A
1210 // Line reaches the closed disk: (J0 + J1 + M)^2 >= 4*J0*J1.
1211 using W = detail::promoted_number_t<
1212 decltype(inCircleDeterminant(a(), b(), c(), other[0]))>;
1213
1214 const W det = static_cast<W>(orientationDeterminant(a(), b(), c()));
1215 const W j0 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other[0]));
1216 const W j1 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other[1]));
1217 const W squared_length = other[0].template squaredDistance<W>(other[1]);
1218 if (squared_length == W{}) {
1219 return false; // Degenerate line.
1220 }
1221 const W discriminant_base = j0 + j1 + squared_length * det; // J0 + J1 + M
1222
1223 return discriminant_base * discriminant_base >= W{4} * j0 * j1;
1224}
1225
1226template <class PointType, class LabelType>
1227template<OrientedLineConcept OtherOrientedLine>
1228constexpr bool Disk<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
1229 return intersects(other.asLine());
1230}
1231
1232template <class PointType, class LabelType>
1233template<RayConcept OtherRay>
1234constexpr bool Disk<PointType, LabelType>::intersects(const OtherRay& other) const {
1235 if (const auto center = getIfPoint()) {
1236 // A radius-zero disk is its center; the in-circle formulation below
1237 // needs three non-collinear boundary points to be meaningful.
1238 return other.contains(*center);
1239 }
1240 // The source in the closed disk settles it. Otherwise the ray meets the
1241 // closed disk exactly when its supporting line does (discriminant >= 0) and
1242 // the contact lies ahead of the source (the perpendicular foot has positive
1243 // parameter). Same division-free in-circle formulation as
1244 // intersects(Segment); the source is parameter 0 and the target parameter 1.
1245 if (contains(other.source())) {
1246 return true;
1247 }
1248
1249 // A = orientation determinant of the three boundary points
1250 // J0 = inCircleDeterminant(a,b,c, source), J1 = ... (a,b,c, target)
1251 // L = |target - source|^2, M = L * A
1252 // Supporting line reaches the closed disk: (J0 + J1 + M)^2 >= 4*J0*J1.
1253 // Contact ahead of the source (foot parameter t* > 0): (J0 - J1)*A < M*A.
1254 using W = detail::promoted_number_t<
1255 decltype(inCircleDeterminant(a(), b(), c(), other.source()))>;
1256
1257 const W det = static_cast<W>(orientationDeterminant(a(), b(), c()));
1258 const W j0 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other.source()));
1259 const W j1 = static_cast<W>(inCircleDeterminant(a(), b(), c(), other.target()));
1260 const W squared_length = other.source().template squaredDistance<W>(other.target());
1261 const W m = squared_length * det;
1262
1263 const W projection = (j0 - j1) * det; // (J0 - J1) * A
1264 const W half_span = m * det; // M * A = L * A^2 >= 0
1265 const W discriminant_base = j0 + j1 + m; // J0 + J1 + M
1266
1267 const bool reaches_disk = discriminant_base * discriminant_base >= W{4} * j0 * j1;
1268 const bool contact_ahead = projection < half_span; // foot parameter t* > 0
1269
1270 return reaches_disk && contact_ahead;
1271}
1272
1273template <class PointType, class LabelType>
1274template<HalfplaneConcept OtherHalfplane>
1275constexpr bool Disk<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
1276 return other.contains((*this)[0]) || intersects(other.asLine());
1277}
1278
1279template <class PointType, class LabelType>
1280template<RectangleConcept OtherRectangle>
1281constexpr bool Disk<PointType, LabelType>::intersects(const OtherRectangle& other) const {
1282 if (other.empty()) {
1283 // The empty set meets nothing and disconnects nothing.
1284 return false;
1285 }
1286 // A rectangle edge meeting the closed disk covers every case except the disk
1287 // lying entirely inside the rectangle: an edge meets the disk whenever it
1288 // crosses it or has an endpoint in it, so a rectangle corner inside the disk
1289 // is already caught by its incident edges. The remaining case is detected by
1290 // a disk boundary point lying inside the rectangle.
1291 for (const auto& edge : other.edges()) {
1292 if (intersects(edge)) {
1293 return true;
1294 }
1295 }
1296 return other.contains((*this)[0]);
1297}
1298
1299template <class PointType, class LabelType>
1300template<TriangleConcept OtherTriangle>
1301constexpr bool Disk<PointType, LabelType>::intersects(const OtherTriangle& other) const {
1302 // A triangle edge meeting the closed disk covers every case except the disk
1303 // lying entirely inside the triangle: an edge meets the disk whenever it
1304 // crosses it or has an endpoint in it, so a triangle vertex inside the disk
1305 // is already caught by its incident edges. The remaining case is detected by
1306 // a disk boundary point lying inside the triangle.
1307 for (const auto& edge : other.edges()) {
1308 if (intersects(edge)) {
1309 return true;
1310 }
1311 }
1312 return other.contains((*this)[0]);
1313}
1314
1315template <class PointType, class LabelType>
1316template<PointConcept OtherPoint>
1318 return std::visit(
1319 [this](const auto& value) {
1320 return this->intersects(value);
1321 },
1322 other.variant());
1323}
1324
1325
1326template <class PointType, class LabelType>
1327template<DiskConcept OtherDisk>
1328constexpr bool Polygon<PointType, LabelType>::intersects(const OtherDisk& other) const {
1329 // If the disk meets the polygon without crossing its boundary, the disk lies
1330 // wholly inside the polygon, so a boundary point of the disk is contained.
1331 // Every other configuration -- a boundary crossing, or the polygon sitting
1332 // inside the disk -- is witnessed by some edge meeting the closed disk.
1333 if (contains(other.a())) {
1334 return true;
1335 }
1336 for (const auto& edge : edgesView()) {
1337 if (edge.intersects(other)) {
1338 return true;
1339 }
1340 }
1341 return false;
1342}
1343
1350
1351template <class PointType, class LabelType, class Storage>
1352template<PointConcept OtherPoint>
1353constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherPoint& other) const {
1354 return contains(other);
1355}
1356
1357template <class PointType, class LabelType, class Storage>
1358template<SegmentConcept OtherSegment>
1359constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherSegment& other) const {
1360 if (points_.empty()) {
1361 return false;
1362 }
1363 if (points_.size() == 1) {
1364 return other.contains((*this)[0]);
1365 }
1366 const auto window = edgeWindow(other.min().x(), other.max().x());
1367 if (!window) {
1368 return false;
1369 }
1370 for (std::size_t i = window->first; i <= window->second; ++i) {
1371 if (this->template boundaryAt<false>(i).intersects(other)) {
1372 return true;
1373 }
1374 }
1375 return false;
1376}
1377
1378template <class PointType, class LabelType, class Storage>
1379template<OrientedSegmentConcept OtherOrientedSegment>
1380constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherOrientedSegment& other) const {
1382}
1383
1384template <class PointType, class LabelType, class Storage>
1385template<LineConcept OtherLine>
1386constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherLine& other) const {
1387 if (empty()) {
1388 return false;
1389 }
1390 if (size() == 1) {
1391 return other.contains((*this)[0]);
1392 }
1393 for (std::size_t i = 0; i + 1 < size(); ++i) {
1394 if (this->template boundaryAt<false>(i).intersects(other)) {
1395 return true;
1396 }
1397 }
1398 return false;
1399}
1400
1401template <class PointType, class LabelType, class Storage>
1402template<OrientedLineConcept OtherOrientedLine>
1403constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherOrientedLine& other) const {
1404 if (empty()) {
1405 return false;
1406 }
1407 if (size() == 1) {
1408 return other.contains((*this)[0]);
1409 }
1410 for (std::size_t i = 0; i + 1 < size(); ++i) {
1411 if (this->template boundaryAt<false>(i).intersects(other)) {
1412 return true;
1413 }
1414 }
1415 return false;
1416}
1417
1418template <class PointType, class LabelType, class Storage>
1419template<RayConcept OtherRay>
1420constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherRay& other) const {
1421 if (empty()) {
1422 return false;
1423 }
1424 if (size() == 1) {
1425 return other.contains((*this)[0]);
1426 }
1427 for (std::size_t i = 0; i + 1 < size(); ++i) {
1428 if (this->template boundaryAt<false>(i).intersects(other)) {
1429 return true;
1430 }
1431 }
1432 return false;
1433}
1434
1435template <class PointType, class LabelType, class Storage>
1436template<HalfplaneConcept OtherHalfplane>
1437constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherHalfplane& other) const {
1438 if (empty()) {
1439 return false;
1440 }
1441 if (size() == 1) {
1442 return other.contains((*this)[0]);
1443 }
1444 for (std::size_t i = 0; i + 1 < size(); ++i) {
1445 if (this->template boundaryAt<false>(i).intersects(other)) {
1446 return true;
1447 }
1448 }
1449 return false;
1450}
1451
1452template <class PointType, class LabelType, class Storage>
1453template<RectangleConcept OtherRectangle>
1454constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherRectangle& other) const {
1455 if (other.empty()) {
1456 // The empty set meets nothing and disconnects nothing.
1457 return false;
1458 }
1459 if (empty()) {
1460 return false;
1461 }
1462 if (size() == 1) {
1463 return other.contains((*this)[0]);
1464 }
1465 // A rectangle's x-extent is available directly, so only the chain edges in
1466 // that window can meet it.
1467 const auto window = edgeWindow(other.min().x(), other.max().x());
1468 if (!window) {
1469 return false;
1470 }
1471 for (std::size_t i = window->first; i <= window->second; ++i) {
1472 if (this->template boundaryAt<false>(i).intersects(other)) {
1473 return true;
1474 }
1475 }
1476 return false;
1477}
1478
1479template <class PointType, class LabelType, class Storage>
1480template<TriangleConcept OtherTriangle>
1481constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherTriangle& other) const {
1482 if (empty()) {
1483 return false;
1484 }
1485 if (size() == 1) {
1486 return other.contains((*this)[0]);
1487 }
1488 for (std::size_t i = 0; i + 1 < size(); ++i) {
1489 if (this->template boundaryAt<false>(i).intersects(other)) {
1490 return true;
1491 }
1492 }
1493 return false;
1494}
1495
1496template <class PointType, class LabelType, class Storage>
1497template<ConvexConcept OtherConvex>
1498constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherConvex& other) const {
1499 if (empty()) {
1500 return false;
1501 }
1502 if (size() == 1) {
1503 return other.contains((*this)[0]);
1504 }
1505 for (std::size_t i = 0; i + 1 < size(); ++i) {
1506 if (this->template boundaryAt<false>(i).intersects(other)) {
1507 return true;
1508 }
1509 }
1510 return false;
1511}
1512
1513template <class PointType, class LabelType, class Storage>
1514template<DiskConcept OtherDisk>
1515constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherDisk& other) const {
1516 if (empty()) {
1517 return false;
1518 }
1519 if (size() == 1) {
1520 return other.contains((*this)[0]);
1521 }
1522 for (std::size_t i = 0; i + 1 < size(); ++i) {
1523 if (this->template boundaryAt<false>(i).intersects(other)) {
1524 return true;
1525 }
1526 }
1527 return false;
1528}
1529
1530template <class PointType, class LabelType, class Storage>
1531template<PointConcept OtherPoint>
1533 return std::visit(
1534 [this](const auto& value) {
1535 return this->intersects(value);
1536 },
1537 other.variant());
1538}
1539
1540template <class PointType, class LabelType, class Storage>
1541template<MonotoneChainConcept OtherChain>
1542constexpr bool MonotoneChain<PointType, LabelType, Storage>::intersects(const OtherChain& other) const {
1543 if (empty() || other.empty()) {
1544 return false;
1545 }
1546 if (size() == 1) {
1547 return other.contains((*this)[0]);
1548 }
1549 if (other.size() == 1) {
1550 return contains(other[0]);
1551 }
1552 if ((*this)[size() - 1].x() < other[0].x() || other[other.size() - 1].x() < (*this)[0].x()) {
1553 return false;
1554 }
1555 // Merge sweep: both edge sequences are sorted by x-interval, so advancing
1556 // the edge with the lexicographically smaller right endpoint visits every
1557 // pair whose x-ranges overlap. On a tie both advance: the skipped pairs
1558 // could only meet at that shared right endpoint, which belongs to the pair
1559 // just tested, so nothing is missed.
1560 const std::size_t iEnd = size() - 1;
1561 const std::size_t jEnd = other.size() - 1;
1562 // Seed past the leading edges left of the shared x-window: an edge whose
1563 // x-range ends before max(minX) cannot overlap the other chain. indexAtX
1564 // locates the first candidate edge in O(log n) instead of advancing the
1565 // sweep one edge at a time; a disengaged result means the chains' x-ranges
1566 // are disjoint, so there is no overlapping pair to test.
1567 using XType = std::common_type_t<NumberType, typename OtherChain::PointType::NumberType>;
1568 const XType xlo = std::max<XType>((*this)[0].x(), other[0].x());
1569 const auto iSeed = indexAtX(xlo);
1570 const auto jSeed = other.indexAtX(xlo);
1571 // Back up one edge: the edge whose right endpoint sits exactly on xlo can
1572 // still meet the other chain there (e.g. a vertical edge at xlo), yet
1573 // indexAtX returns the vertex at xlo, i.e. the following edge.
1574 std::size_t i = (iSeed && jSeed) ? (*iSeed > 0 ? *iSeed - 1 : 0) : iEnd;
1575 std::size_t j = (iSeed && jSeed) ? (*jSeed > 0 ? *jSeed - 1 : 0) : jEnd;
1576 while (i < iEnd && j < jEnd) {
1577 const Segment<PointType> mine((*this)[i], (*this)[i + 1]);
1578 const Segment<typename OtherChain::PointType> theirs(other[j], other[j + 1]);
1579 if (!(mine.max().x() < theirs.min().x() || theirs.max().x() < mine.min().x()) &&
1580 mine.intersects(theirs)) {
1581 return true;
1582 }
1583 const auto order = mine.max() <=> theirs.max();
1584 if (order <= 0) {
1585 ++i;
1586 }
1587 if (order >= 0) {
1588 ++j;
1589 }
1590 }
1591 return false;
1592}
1593
1594template <class PointType, class LabelType>
1595template<MonotoneChainConcept OtherChain>
1596constexpr bool Polygon<PointType, LabelType>::intersects(const OtherChain& other) const {
1597 if (other.empty()) {
1598 return false;
1599 }
1600 if (other.size() == 1) {
1601 return intersects(other[0]);
1602 }
1603 for (std::size_t i = 0; i + 1 < other.size(); ++i) {
1604 if (intersects(Segment<typename OtherChain::PointType>(other[i], other[i + 1]))) {
1605 return true;
1606 }
1607 }
1608 return false;
1609}
1610
1617
1618template <class PointType, class LabelType>
1619template<PointConcept OtherPoint>
1620constexpr bool Polyline<PointType, LabelType>::intersects(const OtherPoint& other) const {
1621 return contains(other);
1622}
1623
1624template <class PointType, class LabelType>
1625template<SegmentConcept OtherSegment>
1626constexpr bool Polyline<PointType, LabelType>::intersects(const OtherSegment& other) const {
1627 if (empty()) {
1628 return false;
1629 }
1630 if (size() == 1) {
1631 return other.contains((*this)[0]);
1632 }
1633 for (const auto& edge : edgesView()) {
1634 if (edge.intersects(other)) {
1635 return true;
1636 }
1637 }
1638 return false;
1639}
1640
1641template <class PointType, class LabelType>
1642template<OrientedSegmentConcept OtherOrientedSegment>
1643constexpr bool Polyline<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
1645}
1646
1647namespace detail {
1648
1649// Shared body of Polyline::intersects against a shape with a
1650// `contains(point)` test and a segment intersection test: the polyline meets
1651// it iff some edge does (or its single vertex lies inside it).
1652template <class PolylineType, class OtherShape>
1653constexpr bool polylineIntersects(const PolylineType& polyline, const OtherShape& other) {
1654 if (polyline.empty()) {
1655 return false;
1656 }
1657 if (polyline.size() == 1) {
1658 return other.contains(polyline[0]);
1659 }
1660 for (const auto& edge : polyline.edgesView()) {
1661 if (edge.intersects(other)) {
1662 return true;
1663 }
1664 }
1665 return false;
1666}
1667
1668} // namespace detail
1669
1670template <class PointType, class LabelType>
1671template<LineConcept OtherLine>
1672constexpr bool Polyline<PointType, LabelType>::intersects(const OtherLine& other) const {
1673 return detail::polylineIntersects(*this, other);
1674}
1675
1676template <class PointType, class LabelType>
1677template<OrientedLineConcept OtherOrientedLine>
1678constexpr bool Polyline<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
1679 return detail::polylineIntersects(*this, other);
1680}
1681
1682template <class PointType, class LabelType>
1683template<RayConcept OtherRay>
1684constexpr bool Polyline<PointType, LabelType>::intersects(const OtherRay& other) const {
1685 return detail::polylineIntersects(*this, other);
1686}
1687
1688template <class PointType, class LabelType>
1689template<HalfplaneConcept OtherHalfplane>
1690constexpr bool Polyline<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
1691 return detail::polylineIntersects(*this, other);
1692}
1693
1694template <class PointType, class LabelType>
1695template<RectangleConcept OtherRectangle>
1696constexpr bool Polyline<PointType, LabelType>::intersects(const OtherRectangle& other) const {
1697 if (other.empty()) {
1698 // The empty set meets nothing and disconnects nothing.
1699 return false;
1700 }
1701 if (size() > 1 && !bbox().intersects(other)) {
1702 return false;
1703 }
1704 return detail::polylineIntersects(*this, other);
1705}
1706
1707template <class PointType, class LabelType>
1708template<TriangleConcept OtherTriangle>
1709constexpr bool Polyline<PointType, LabelType>::intersects(const OtherTriangle& other) const {
1710 return detail::polylineIntersects(*this, other);
1711}
1712
1713template <class PointType, class LabelType>
1714template<ConvexConcept OtherConvex>
1715constexpr bool Polyline<PointType, LabelType>::intersects(const OtherConvex& other) const {
1716 return detail::polylineIntersects(*this, other);
1717}
1718
1719template <class PointType, class LabelType>
1720template<DiskConcept OtherDisk>
1721constexpr bool Polyline<PointType, LabelType>::intersects(const OtherDisk& other) const {
1722 return detail::polylineIntersects(*this, other);
1723}
1724
1725template <class PointType, class LabelType>
1726template<MonotoneChainConcept OtherChain>
1727constexpr bool Polyline<PointType, LabelType>::intersects(const OtherChain& other) const {
1728 if (empty() || other.empty()) {
1729 return false;
1730 }
1731 if (size() == 1) {
1732 return other.contains((*this)[0]);
1733 }
1734 if (other.size() == 1) {
1735 return contains(other[0]);
1736 }
1737 if (!bbox().intersects(other.bbox())) {
1738 return false;
1739 }
1740 // The chain's own segment test prunes by x-range, so fold it over the
1741 // polyline edges.
1742 for (const auto& edge : edgesView()) {
1743 if (other.intersects(edge)) {
1744 return true;
1745 }
1746 }
1747 return false;
1748}
1749
1750template <class PointType, class LabelType>
1751template<PolylineConcept OtherPolyline>
1752constexpr bool Polyline<PointType, LabelType>::intersects(const OtherPolyline& other) const {
1753 if (empty() || other.empty()) {
1754 return false;
1755 }
1756 if (size() == 1) {
1757 return other.contains((*this)[0]);
1758 }
1759 if (other.size() == 1) {
1760 return contains(other[0]);
1761 }
1762 if (!bbox().intersects(other.bbox())) {
1763 return false;
1764 }
1765 for (const auto& mine : edgesView()) {
1766 for (const auto& theirs : other.edgesView()) {
1767 if (mine.intersects(theirs)) {
1768 return true;
1769 }
1770 }
1771 }
1772 return false;
1773}
1774
1775template <class PointType, class LabelType>
1776template<PointConcept OtherPoint>
1778 return std::visit(
1779 [this](const auto& value) {
1780 return this->intersects(value);
1781 },
1782 other.variant());
1783}
1784
1785template <class PointType, class LabelType>
1786template<PolylineConcept OtherPolyline>
1787constexpr bool Polygon<PointType, LabelType>::intersects(const OtherPolyline& other) const {
1788 if (other.empty()) {
1789 return false;
1790 }
1791 if (other.size() == 1) {
1792 return intersects(other[0]);
1793 }
1794 for (std::size_t i = 0; i + 1 < other.size(); ++i) {
1795 if (intersects(Segment<typename OtherPolyline::PointType>(other[i], other[i + 1]))) {
1796 return true;
1797 }
1798 }
1799 return false;
1800}
1801
1802
1803// ---------------------------------------------------------------------------
1804// HalfplaneIntersection
1805
1806template <class PointType, class LabelType>
1807template <PointConcept OtherPoint>
1808constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherPoint& other) const {
1809 return contains(other);
1810}
1811
1812template <class PointType, class LabelType>
1813template <SegmentConcept OtherSegment>
1814constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherSegment& other) const {
1815 if (empty()) {
1816 return false;
1817 }
1818 if (halfplanes_.empty()) {
1819 return true;
1820 }
1821 if (other.isDegenerate()) {
1822 return contains(other.min());
1823 }
1824 // Clip the supporting line and intersect the parameter interval with
1825 // [0, 1]; the interval endpoints compare against the segment endpoints
1826 // through plain point-side tests.
1827 const Halfplane<typename OtherSegment::PointType> along(other.min(), other.max());
1828 const auto clip = clipLine(along);
1829 if (clip.empty) {
1830 return false;
1831 }
1832 if (clip.entry >= 0 && constraintSide(static_cast<std::size_t>(clip.entry), other.max()) < 0) {
1833 return false; // the line enters the region only after the segment ends
1834 }
1835 if (clip.exit >= 0 && constraintSide(static_cast<std::size_t>(clip.exit), other.min()) < 0) {
1836 return false; // the line leaves the region before the segment starts
1837 }
1838 return true;
1839}
1840
1841template <class PointType, class LabelType>
1842template <OrientedSegmentConcept OtherOrientedSegment>
1843constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
1845}
1846
1847template <class PointType, class LabelType>
1848template <LineConcept OtherLine>
1849constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherLine& other) const {
1850 if (empty()) {
1851 return false;
1852 }
1853 if (halfplanes_.empty()) {
1854 return true;
1855 }
1856 const Halfplane<typename OtherLine::PointType> along(other[0], other[1]);
1857 return !clipLine(along).empty;
1858}
1859
1860template <class PointType, class LabelType>
1861template <OrientedLineConcept OtherOrientedLine>
1862constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
1863 return intersects(other.asLine());
1864}
1865
1866template <class PointType, class LabelType>
1867template <RayConcept OtherRay>
1868constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherRay& other) const {
1869 if (empty()) {
1870 return false;
1871 }
1872 if (halfplanes_.empty()) {
1873 return true;
1874 }
1875 const Halfplane<typename OtherRay::PointType> along(other.source(), other.target());
1876 const auto clip = clipLine(along);
1877 if (clip.empty) {
1878 return false;
1879 }
1880 // The interval must reach forward parameters: the line may not leave the
1881 // region before the ray's source.
1882 return clip.exit < 0 || constraintSide(static_cast<std::size_t>(clip.exit), other.source()) >= 0;
1883}
1884
1885template <class PointType, class LabelType>
1886template <HalfplaneConcept OtherHalfplane>
1887constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
1888 if (empty()) {
1889 return false;
1890 }
1891 if (halfplanes_.empty()) {
1892 return true;
1893 }
1894 // Nonempty intersection means the infimum of the half-plane's normal
1895 // functional over the region does not exceed its boundary value.
1896 return supStatus(other.opposite()) != SupStatus::below;
1897}
1898
1899template <class PointType, class LabelType>
1900template <RectangleConcept OtherRectangle>
1901constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherRectangle& other) const {
1902 if (other.empty()) {
1903 // The empty set meets nothing and disconnects nothing.
1904 return false;
1905 }
1906 // The intersection with a convex region is itself a half-plane
1907 // intersection; the two shapes meet exactly when it is nonempty.
1908 return !this->template intersection<NumberType>(other).empty();
1909}
1910
1911template <class PointType, class LabelType>
1912template <TriangleConcept OtherTriangle>
1913constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherTriangle& other) const {
1914 return !this->template intersection<NumberType>(other).empty();
1915}
1916
1917template <class PointType, class LabelType>
1918template <ConvexConcept OtherConvex>
1919constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherConvex& other) const {
1920 if (other.size() == 0) {
1921 return false;
1922 }
1923 return !this->template intersection<NumberType>(other).empty();
1924}
1925
1926template <class PointType, class LabelType>
1927template <DiskConcept OtherDisk>
1928constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherDisk& other) const {
1929 if (empty()) {
1930 return false;
1931 }
1932 if (halfplanes_.empty()) {
1933 return true;
1934 }
1935 // The disk is bounded, so only the part of the region near it matters:
1936 // clip to a box enclosing the disk and test the resulting bounded region.
1937 using E = detail::region_exact_number_t<NumberType>;
1938 const auto clipped = detail::regionClippedToBox(*this, other.bbox());
1939 if (clipped.empty()) {
1940 return false;
1941 }
1942 return clipped.template asConvex<E>().intersects(other);
1943}
1944
1945template <class PointType, class LabelType>
1946template <MonotoneChainConcept OtherChain>
1947constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherChain& other) const {
1948 if (empty() || other.size() == 0) {
1949 return false;
1950 }
1951 if (other.size() == 1) {
1952 return contains(other[0]);
1953 }
1954 for (const auto& edge : other.edgesView()) {
1955 if (intersects(edge)) {
1956 return true;
1957 }
1958 }
1959 return false;
1960}
1961
1962template <class PointType, class LabelType>
1963template <PolylineConcept OtherPolyline>
1964constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherPolyline& other) const {
1965 if (empty() || other.size() == 0) {
1966 return false;
1967 }
1968 if (other.size() == 1) {
1969 return contains(other[0]);
1970 }
1971 for (const auto& edge : other.edgesView()) {
1972 if (intersects(edge)) {
1973 return true;
1974 }
1975 }
1976 return false;
1977}
1978
1979template <class PointType, class LabelType>
1980template <PolygonConcept OtherPolygon>
1981constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherPolygon& other) const {
1982 if (empty() || other.size() == 0) {
1983 return false;
1984 }
1985 // The shapes meet when the region reaches the polygon's boundary or holds
1986 // an interior witness of the polygon (the region is fully inside it).
1987 for (std::size_t i = 0; i < other.size(); ++i) {
1988 if (contains(other[i])) {
1989 return true;
1990 }
1991 }
1992 for (const auto& edge : other.edgesView()) {
1993 if (intersects(edge)) {
1994 return true;
1995 }
1996 }
1997 using E = detail::region_exact_number_t<NumberType>;
1998 return other.contains(pointInside<E>());
1999}
2000
2001template <class PointType, class LabelType>
2002template <HalfplaneIntersectionConcept OtherRegion>
2003constexpr bool HalfplaneIntersection<PointType, LabelType>::intersects(const OtherRegion& other) const {
2004 // The intersection of two half-plane intersections is itself one; the
2005 // regions meet exactly when it is nonempty.
2006 if (empty() || other.empty()) {
2007 return false;
2008 }
2009 return !this->template intersection<NumberType>(other).empty();
2010}
2011
2012template <class PointType, class LabelType>
2013template <PointConcept OtherPoint>
2015 return std::visit(
2016 [this](const auto& value) {
2017 return this->intersects(value);
2018 },
2019 other.variant());
2020}
2021
2022
2023// ---------------------------------------------------------------------------
2024// PolygonWithHoles
2025
2026template <class PointType, class LabelType>
2027template <SegmentConcept OtherSegment>
2028constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherSegment& other) const {
2029 if (!outer_.intersects(other)) {
2030 return false;
2031 }
2032 if (holes_.empty()) {
2033 return true;
2034 }
2035 // Every point of every ring belongs to the region: holes remove only their
2036 // interiors, and a hole interior never reaches the outer boundary. So a
2037 // segment touching any ring edge already meets the region.
2038 if (anyBoundaryEdge([&other](const auto& edge) { return edge.intersects(other); })) {
2039 return true;
2040 }
2041 // The segment misses ∂A altogether, so — being connected — it lies wholly
2042 // in the open region or wholly outside the closed one, and either endpoint
2043 // says which.
2044 return contains(other.min());
2045}
2046
2047template <class PointType, class LabelType>
2048template <OrientedSegmentConcept OtherOrientedSegment>
2049constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherOrientedSegment& other) const {
2050 return intersects(other.asSegment());
2051}
2052
2053// An unbounded connected operand that reaches the bounded outer polygon has to
2054// leave it again, so it meets ∂outer — and every point of ∂outer is in the
2055// region, hole interiors never reaching it. The holes therefore cost nothing
2056// here; only a collapsed operand, which is a point that a hole can swallow,
2057// needs the region's own point test.
2058template <class PointType, class LabelType>
2059template <LineConcept OtherLine>
2060constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherLine& other) const {
2061 if (other.isDegenerate()) {
2062 return contains(other.min());
2063 }
2064 return outer_.intersects(other);
2065}
2066
2067template <class PointType, class LabelType>
2068template <OrientedLineConcept OtherOrientedLine>
2069constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherOrientedLine& other) const {
2070 return intersects(other.asLine());
2071}
2072
2073template <class PointType, class LabelType>
2074template <RayConcept OtherRay>
2075constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherRay& other) const {
2076 if (other.isDegenerate()) {
2077 return contains(other.source());
2078 }
2079 return outer_.intersects(other);
2080}
2081
2082template <class PointType, class LabelType>
2083template <HalfplaneConcept OtherHalfplane>
2084constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherHalfplane& other) const {
2085 if (other.isDegenerate()) {
2086 return contains(other.source());
2087 }
2088 return outer_.intersects(other);
2089}
2090
2091// The bounded operands with area follow the segment overload exactly: every
2092// point of every ring belongs to the region, so touching a ring settles it, and
2093// an operand missing ∂A altogether is — being connected — wholly in A° or
2094// wholly outside A, which one of its own vertices reports.
2095template <class PointType, class LabelType>
2096template <class OtherArea>
2097constexpr bool PolygonWithHoles<PointType, LabelType>::areaIntersects(const OtherArea& other) const {
2098 if (!other.intersects(outer_)) {
2099 return false;
2100 }
2101 if (holes_.empty()) {
2102 return true;
2103 }
2104 if (anyBoundaryEdge([&other](const auto& edge) { return edge.intersects(other); })) {
2105 return true;
2106 }
2107 const auto edges = other.edges();
2108 return !edges.empty() && contains(edges.front().min());
2109}
2110
2111template <class PointType, class LabelType>
2112template <RectangleConcept OtherRectangle>
2113constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherRectangle& other) const {
2114 if (other.empty()) {
2115 // The empty set meets nothing and disconnects nothing.
2116 return false;
2117 }
2118 return areaIntersects(other);
2119}
2120
2121template <class PointType, class LabelType>
2122template <TriangleConcept OtherTriangle>
2123constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherTriangle& other) const {
2124 return areaIntersects(other);
2125}
2126
2127template <class PointType, class LabelType>
2128template <ConvexConcept OtherConvex>
2129constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherConvex& other) const {
2130 return areaIntersects(other);
2131}
2132
2133template <class PointType, class LabelType>
2134template <PolygonConcept OtherPolygon>
2135constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherPolygon& other) const {
2136 return areaIntersects(other);
2137}
2138
2139template <class PointType, class LabelType>
2140template <PolygonWithHolesConcept OtherRegion>
2141constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherRegion& other) const {
2142 return areaIntersects(other);
2143}
2144
2145// A chain is the union of its edges, so it meets the region exactly when some
2146// edge does (see @ref chainRelation).
2147template <class PointType, class LabelType>
2148template <MonotoneChainConcept OtherChain>
2149constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherChain& other) const {
2150 return chainRelation(other, false, [this](const auto& edge) { return this->intersects(edge); });
2151}
2152
2153template <class PointType, class LabelType>
2154template <PolylineConcept OtherPolyline>
2155constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherPolyline& other) const {
2156 return chainRelation(other, false, [this](const auto& edge) { return this->intersects(edge); });
2157}
2158
2159// The disk follows the area operands exactly: every point of every ring belongs
2160// to the region, so touching a ring settles it, and a disk missing ∂A altogether
2161// is — being connected — wholly in A° or wholly outside A, which any one of its
2162// own points reports.
2163template <class PointType, class LabelType>
2164template <DiskConcept OtherDisk>
2165constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherDisk& other) const {
2166 if (other.isDegenerate()) {
2167 // Radius zero, or undefined; either way a() carries the answer, and the
2168 // point overload is cheaper than the scan below.
2169 return intersects(other.a());
2170 }
2171 if (!other.intersects(outer_)) {
2172 return false;
2173 }
2174 if (holes_.empty()) {
2175 return true;
2176 }
2177 if (anyBoundaryEdge([&other](const auto& edge) { return other.intersects(edge); })) {
2178 return true;
2179 }
2180 return contains(other.a());
2181}
2182
2183// Same argument once more, and it survives an unbounded operand: a half-plane
2184// intersection is connected, so one that misses ∂A lies wholly in A° — which a
2185// bounded region rules out for anything unbounded — or wholly outside A.
2186template <class PointType, class LabelType>
2187template <HalfplaneIntersectionConcept OtherIntersection>
2188constexpr bool PolygonWithHoles<PointType, LabelType>::intersects(const OtherIntersection& other) const {
2189 if (other.empty()) {
2190 return false;
2191 }
2192 if (!other.intersects(outer_)) {
2193 return false;
2194 }
2195 if (holes_.empty()) {
2196 return true;
2197 }
2198 if (anyBoundaryEdge([&other](const auto& edge) { return other.intersects(edge); })) {
2199 return true;
2200 }
2201 using E = detail::region_exact_number_t<typename OtherIntersection::NumberType>;
2202 return contains(other.template pointInside<E>());
2203}
2204
2205
2206// ---------------------------------------------------------------------------
2207// Reverse direction: intersects is symmetric, so the lower-ranked shapes'
2208// generic rank-guarded forwarders already dispatch these here — no per-shape
2209// definitions are needed.
2210
2211// ---------------------------------------------------------------------------
2212// Runtime Shape argument: unwrap the stored alternative and re-dispatch. Every
2213// alternative has a per-shape overload above, so no fallback is needed.
2214
2215template <class PointType, class LabelType>
2216template <PointConcept OtherPoint>
2218 return std::visit(
2219 [this](const auto& value) {
2220 return this->intersects(value);
2221 },
2222 other.variant());
2223}
2224
2225
2226// ---------------------------------------------------------------------------
2227// PolygonSet
2228//
2229// `A ∩ x ≠ ∅` iff some `Aᵢ ∩ x ≠ ∅`: the union of the components is the set, and
2230// that is the whole argument, for every operand and with no exception.
2231
2232template <class PointType, class LabelType>
2233template <detail::SetOperandConcept OtherShape>
2234bool PolygonSet<PointType, LabelType>::intersects(const OtherShape& other) const {
2235 return anyComponent([&](const ComponentType& component) { return component.intersects(other); });
2236}
2237
2238template <class PointType, class LabelType>
2239template <PolygonSetConcept OtherSet>
2240bool PolygonSet<PointType, LabelType>::intersects(const OtherSet& other) const {
2241 for (const auto& component : other) {
2242 if (intersects(component)) {
2243 return true;
2244 }
2245 }
2246 return false;
2247}
2248
2249template <class PointType, class LabelType>
2250template <PointConcept OtherPoint>
2252 return std::visit([this](const auto& value) { return this->intersects(value); },
2253 other.variant());
2254}
2255
2256} // namespace pgl
Exact rational number class template.
Definition rational.hpp:106
Implementations of the 'interiorsIntersect' predicate.
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
constexpr auto inCircleDeterminant(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c, const Point< DNumber, DLabel > &d)
Returns the signed in-circle determinant of a query point.
Definition orientation.hpp:857
constexpr bool is_Rational_v
Definition rational.hpp:37
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
constexpr std::partial_ordering orientationSign(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Classifies the orientation of three points.
Definition orientation.hpp:544
constexpr std::partial_ordering crossSign(const Point< UNumber, ULabel > &u, const Point< VNumber, VLabel > &v)
Classifies the turn from one vector to another.
Definition orientation.hpp:583
constexpr auto orientationDeterminant(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Returns the signed orientation determinant of three points.
Definition orientation.hpp:518
Exact low-level orientation and incircle predicates.
Small dispatch traits and geometry helpers reused by the implementations.
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the convex polygon.
Definition bounding.hpp:374
constexpr bool intersects(const OtherSegment &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:716
constexpr auto edgesView() const
Returns a lazy view over the edges, materializing each Segment on the fly instead of allocating a vec...
Definition convex.hpp:575
constexpr bool separates(const EmptyShape< EmptyPoint > &) const
Tests whether removing this shape disconnects the other shape (B∖A is disconnected).
Definition convex.hpp:1345
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1135
size_t size() const
Returns the number of vertices in the convex polygon.
Definition convex.hpp:840
PointType_ PointType
Definition convex.hpp:171
constexpr ResultNumber squaredRadius() const
Returns the squared radius in an explicitly chosen result type.
Definition disk.hpp:402
constexpr Point< ResultNumber, PointLabelType > center() const
Returns the center (circumcenter of the three boundary points) in an explicitly chosen coordinate typ...
Definition disk.hpp:284
constexpr bool intersects(const OtherSegment &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:904
constexpr std::optional< PointType > getIfPoint() const
Returns the point the disk collapses to, if it does.
Definition disk.hpp:372
constexpr const PointType & c() const
Returns the third boundary point in canonical order.
Definition disk.hpp:244
constexpr bool contains(const OtherPoint &other) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1015
detail::floating_result_t< ResultNumber > squaredDistance(const OtherPoint &point) const
Returns the squared Euclidean distance from this disk to a point.
Definition distance.hpp:1194
constexpr const PointType & a() const
Returns the first boundary point (lexicographically smallest).
Definition disk.hpp:228
constexpr const PointType & b() const
Returns the second boundary point in canonical order.
Definition disk.hpp:235
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:2511
constexpr bool empty() const
Returns whether the region is the empty set.
Definition halfplaneintersection.hpp:649
constexpr EmptyShape< EmptyPoint > intersection(const EmptyShape< EmptyPoint > &) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition halfplaneintersection.hpp:1762
constexpr Convex< Point< ResultNumber, typename PointType::LabelType > > asConvex() const
Returns the region as a convex polygon.
Definition halfplaneintersection.hpp:955
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1808
constexpr Point< ResultNumber > pointInside() const
Returns a representative point of the region: a point of its interior when the region is full-dimensi...
Definition measures.hpp:1350
constexpr std::variant< Segment< Point< ResultNumber, typename PointType::LabelType > >, Ray< Point< ResultNumber, typename PointType::LabelType > >, Line< Point< ResultNumber, typename PointType::LabelType > > > edge(std::size_t i) const
Returns the boundary contribution of half-plane i as a typed one-dimensional shape.
Definition halfplaneintersection.hpp:919
Closed half-plane defined by an oriented boundary line.
Definition halfplane.hpp:51
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:622
constexpr const PointType & target() const
Returns the target boundary point.
Definition halfplane.hpp:193
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:872
constexpr Line< PointType > asLine() const
Returns the boundary line without orientation.
Definition halfplane.hpp:318
constexpr const PointType & source() const
Returns the source boundary point.
Definition halfplane.hpp:181
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:952
Unoriented infinite line.
Definition line.hpp:52
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:428
constexpr const PointType & max() const
Returns the largest stored defining point.
Definition line.hpp:189
constexpr bool parallel(const OtherLine &other) const
Returns whether another line is parallel to this line.
Definition predicates.hpp:508
constexpr const PointType & min() const
Returns the smallest stored defining point.
Definition line.hpp:180
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:312
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:451
constexpr std::optional< std::size_t > indexAtX(const OtherNumber &x) const
Locates the vertex or edge of the chain at a given x-coordinate.
Definition atxy.hpp:346
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1353
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1822
constexpr std::size_t size() const
Returns the number of vertices in the chain.
Definition monotonechain.hpp:393
constexpr bool empty() const
Checks whether the chain has no vertex.
Definition monotonechain.hpp:400
constexpr Line< PointType > asLine() const
Returns the line without orientation.
Definition orientedline.hpp:321
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:364
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:278
Two-dimensional point with optional label payload.
Definition point.hpp:129
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:25
constexpr bool contains(const OtherPoint &other) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:25
bool intersects(const OtherShape &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:2234
constexpr const ComponentType & component(std::size_t index) const
Accesses a component by index.
Definition polygonset.hpp:271
PolygonWithHoles< PointType > ComponentType
Definition polygonset.hpp:169
Point< ResultNumber > pointInside() const
Returns a point strictly inside the region.
Definition triangulation.hpp:6989
constexpr bool intersects(const OtherPoint &point) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition polygonwithholes.hpp:1649
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:2945
constexpr bool intersects(const OtherChain &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1596
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the polygon.
Definition bounding.hpp:449
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1296
constexpr auto edgesView() const
Returns a lazy view over the edges, materializing each Segment on the fly instead of allocating a vec...
Definition polygon.hpp:782
constexpr bool boundariesIntersect(const OtherPolygon &other) const
Tests whether the two polygon boundaries share at least one point (∂A ∩ ∂B ≠ ∅).
Definition interiorsintersect.hpp:1197
constexpr std::size_t size() const
Returns the number of vertices in the polygon.
Definition polygon.hpp:259
constexpr std::vector< PointType > vertices() const
Returns the vertices of the polygon (translation applied).
Definition polygon.hpp:587
constexpr PointType get(std::ptrdiff_t index) const
Cyclic access: same as operator[] but index is taken modulo size(); negative indices wrap from the en...
Definition polygon.hpp:169
constexpr bool separates(const OtherPoint &other) const
Tests whether removing this shape disconnects the other shape (B∖A is disconnected).
Definition separates.hpp:1864
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the polyline.
Definition bounding.hpp:515
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:2134
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:1620
constexpr bool empty() const
Checks whether the polyline has no vertex.
Definition polyline.hpp:395
constexpr std::size_t size() const
Returns the number of vertices in the polyline.
Definition polyline.hpp:388
constexpr auto edgesView() const
Returns a lazy view over the edges, materializing each Segment on the fly instead of allocating a vec...
Definition polyline.hpp:627
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:727
constexpr const PointType & target() const
Returns the second stored point defining the direction.
Definition ray.hpp:193
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:409
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:625
constexpr Line< PointType > asLine() const
Returns the supporting line without orientation.
Definition ray.hpp:319
constexpr const PointType & source() const
Returns the source point of the ray.
Definition ray.hpp:181
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:502
constexpr const PointType & min() const
Returns the minimum corner (min x, min y).
Definition rectangle.hpp:347
constexpr bool empty() const
Returns whether the rectangle is the empty set of points.
Definition rectangle.hpp:290
constexpr std::array< Segment< PointType >, 4 > edges() const
Returns the four edges as unordered segments.
Definition bounding.hpp:199
constexpr std::array< PointType, 4 > vertices() const
Returns the four vertices in counterclockwise order.
Definition bounding.hpp:188
constexpr const PointType & max() const
Returns the maximum corner (max x, max y).
Definition rectangle.hpp:359
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:728
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:119
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:48
constexpr bool containsCollinear(const OtherPoint &point) const
Returns whether the segment contains the given point that is collinear with the segment.
Definition predicates.hpp:161
constexpr const PointType & max() const
Returns the largest stored endpoint.
Definition segment.hpp:199
constexpr const PointType & min() const
Returns the smallest stored endpoint.
Definition segment.hpp:190
Runtime variant wrapper over the supported primitive shapes.
Definition shape.hpp:160
constexpr const Variant & variant() const
Returns the underlying variant.
Definition shape.hpp:264
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:223
constexpr const PointType & b() const
Returns the second vertex.
Definition triangle.hpp:217
constexpr const PointType & a() const
Returns the first vertex.
Definition triangle.hpp:208
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:134
constexpr Rectangle< PointType > bbox() const
Returns the axis-aligned bounding box of the vertices.
Definition bounding.hpp:224
constexpr std::array< Segment< PointType >, 3 > edges() const
Returns the three unoriented boundary edges.
Definition bounding.hpp:240
constexpr const PointType & c() const
Returns the third vertex.
Definition triangle.hpp:226