Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
intersection.hpp
Go to the documentation of this file.
1#pragma once
2
4
9
10#include <algorithm>
11#include <cassert>
12#include <map>
13#include <set>
14#include <type_traits>
15
16
17namespace pgl {
18
19namespace detail {
20
40template <class ResultNumber, class ResultLabel, PointConcept APoint, PointConcept BPoint>
41constexpr Point<ResultNumber, ResultLabel> carrierCrossing(
42 const APoint& a1, const APoint& a2, const BPoint& b1, const BPoint& b2) {
43 using InputNumber = std::common_type_t<typename APoint::NumberType,
44 typename BPoint::NumberType>;
45 using Coordinate =
46 std::conditional_t<std::floating_point<InputNumber>, ResultNumber,
47 promoted_number_t<promoted_number_t<InputNumber>>>;
48
49 const auto wide = [](const auto& value) -> decltype(auto) {
50 return detail::asNumber<Coordinate>(value);
51 };
52 const Coordinate rx = wide(a2.x()) - wide(a1.x());
53 const Coordinate ry = wide(a2.y()) - wide(a1.y());
54 const Coordinate sx = wide(b2.x()) - wide(b1.x());
55 const Coordinate sy = wide(b2.y()) - wide(b1.y());
56 const Coordinate ox = wide(b1.x()) - wide(a1.x());
57 const Coordinate oy = wide(b1.y()) - wide(a1.y());
58
59 const Coordinate determinant = rx * sy - ry * sx;
60 const Coordinate along = ox * sy - oy * sx;
61
62 // An integral result divides in the wide type, where the division is exact
63 // for a crossing that lands on the grid; every other result type divides in
64 // itself, which is where its own exactness lives.
65 const auto ratio = [&determinant](const Coordinate& numerator) {
66 if constexpr (std::integral<ResultNumber>) {
67 return static_cast<ResultNumber>(numerator / determinant);
68 } else {
69 return detail::asNumber<ResultNumber>(numerator) /
70 detail::asNumber<ResultNumber>(determinant);
71 }
72 };
73
75 detail::asNumber<ResultNumber>(a1.x()) + ratio(along * rx),
76 detail::asNumber<ResultNumber>(a1.y()) + ratio(along * ry));
77}
78
79} // namespace detail
80
81// -----------------------------------------------------------------------------
82// Point
83
84template <class Number, class Label>
85template <class ResultNumber, PointConcept OtherPoint>
86constexpr std::optional<Point<ResultNumber, Label>>
87Point<Number, Label>::intersection(const OtherPoint& other) const {
88 if (contains(other)) {
89 return Point<ResultNumber, Label>(*this);
90 }
91 return {};
92}
93
94// -----------------------------------------------------------------------------
95// Segment
96
97template <class PointType, class LabelType>
98template <class ResultNumber, PointConcept OtherPoint>
99constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
100Segment<PointType, LabelType>::intersection(const OtherPoint& other) const {
101 if (contains(other)) {
103 }
104 return {};
105}
106
107template <class PointType, class LabelType>
108template <class ResultNumber, SegmentConcept OtherSegment>
109constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
110Segment<PointType, LabelType>::intersection(const OtherSegment& other) const {
112 using ResultSegment = Segment<ResultPoint>;
113
114 if (!boundingBoxesOverlap(other)) {
115 return {};
116 }
117
118 const auto d1 = orientationSign(min(), max(), other.min());
119 const auto d2 = orientationSign(min(), max(), other.max());
120
121 if (d1 == 0 && d2 == 0) {
122 // Both segments are collinear: compare endpoints in ResultPoint so the
123 // ternary and ordering stay well-typed when PointType and the other
124 // segment's point type
125 // differ (e.g. Convex chord clipped from int into Rational).
126 const ResultPoint a_min(min());
127 const ResultPoint a_max(max());
128 const ResultPoint b_min(other.min());
129 const ResultPoint b_max(other.max());
130 const ResultPoint pminmax = a_max < b_max ? a_max : b_max;
131 const ResultPoint pmaxmin = a_min < b_min ? b_min : a_min;
132
133 if (pminmax < pmaxmin) {
134 return {};
135 }
136 if (pminmax == pmaxmin) {
137 return pminmax;
138 }
139
140 return ResultSegment(pminmax, pmaxmin);
141 }
142
143 if (d1 == 0 && containsCollinear(other.min())) {
144 return Point<ResultNumber>(other.min());
145 }
146 if (d2 == 0 && containsCollinear(other.max())) {
147 return Point<ResultNumber>(other.max());
148 }
149
150 const auto d3 = orientationSign(other.min(), other.max(), min());
151 if (d3 == 0 && other.containsCollinear(min())) {
152 return Point<ResultNumber>(min());
153 }
154
155 const auto d4 = orientationSign(other.min(), other.max(), max());
156 if (d4 == 0 && other.containsCollinear(max())) {
157 return Point<ResultNumber>(max());
158 }
159
160 if (d1 == 0 || d2 == 0 || d3 == 0 || d4 == 0) {
161 return {};
162 }
163
164 if (d1 != d2 && d3 != d4) {
165 // The four orientations above have established that the segments cross
166 // properly, so their carriers are not parallel.
167 return detail::carrierCrossing<ResultNumber, typename PointType::LabelType>(
168 min(), max(), other.min(), other.max());
169 }
170
171 return {};
172}
173
174// -----------------------------------------------------------------------------
175// OrientedSegment
176
177template <class PointType, class LabelType>
178template <class ResultNumber, PointConcept OtherPoint>
179constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
181 return static_cast<Segment<PointType>>(*this).template intersection<ResultNumber>(other);
182}
183
184template <class PointType, class LabelType>
185template <class ResultNumber, SegmentConcept OtherSegment>
186constexpr auto OrientedSegment<PointType, LabelType>::intersection(const OtherSegment& other) const {
187 return static_cast<Segment<PointType>>(*this).template intersection<ResultNumber>(other);
188}
189
190template <class PointType, class LabelType>
191template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
192constexpr auto OrientedSegment<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
193 return static_cast<Segment<PointType>>(*this).template intersection<ResultNumber>(
195}
196
197// -----------------------------------------------------------------------------
198// Line
199
200template <class PointType, class LabelType>
201template <class ResultNumber, PointConcept OtherPoint>
202constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
203Line<PointType, LabelType>::intersection(const OtherPoint& other) const {
204 if (contains(other)) {
206 }
207 return {};
208}
209
210template <class PointType, class LabelType>
211template <class ResultNumber, LineConcept OtherLine>
212constexpr std::optional<
213 std::variant<
216Line<PointType, LabelType>::intersection(const OtherLine& other) const {
218 using ResultLine = Line<ResultPoint>;
219
220 if (isDegenerate()) {
221 if (other.contains(min())) {
222 return ResultPoint(min());
223 }
224 return {};
225 }
226
227 if (other.isDegenerate()) {
228 if (contains(other.min())) {
229 return ResultPoint(other.min());
230 }
231 return {};
232 }
233
234 if (contains(other)) {
235 return ResultLine(ResultPoint(min()), ResultPoint(max()));
236 }
237
238 if (parallel(other)) {
239 return {};
240 }
241
242 // Neither line is degenerate and they are not parallel, so they cross once.
243 return detail::carrierCrossing<ResultNumber, typename PointType::LabelType>(
244 min(), max(), other.min(), other.max());
245}
246
247template <class PointType, class LabelType>
248template <class ResultNumber, SegmentConcept OtherSegment>
249constexpr auto Line<PointType, LabelType>::intersection(const OtherSegment& other) const {
251 using ResultSegment = Segment<ResultPoint>;
252 using ResultType = std::optional<std::variant<ResultPoint, ResultSegment>>;
253
254 const auto line_intersection = intersection<ResultNumber>(Line<typename OtherSegment::PointType>(other.min(), other.max()));
255 if (!line_intersection) {
256 return ResultType{};
257 }
258 if (std::holds_alternative<ResultPoint>(*line_intersection)) {
259 const auto& point = std::get<ResultPoint>(*line_intersection);
260 if (other.contains(point)) {
261 return ResultType(point);
262 }
263 return ResultType{};
264 }
265 return ResultType(ResultSegment(ResultPoint(other.min()), ResultPoint(other.max())));
266}
267
268template <class PointType, class LabelType>
269template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
270constexpr auto Line<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
272}
273
274// -----------------------------------------------------------------------------
275// OrientedLine
276
277template <class PointType, class LabelType>
278template <class ResultNumber, PointConcept OtherPoint>
279constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
281 return this->asLine().template intersection<ResultNumber>(other);
282}
283
284template <class PointType, class LabelType>
285template <class ResultNumber, LineConcept OtherLine>
286constexpr std::optional<
287 std::variant<
291 return this->asLine().template intersection<ResultNumber>(other);
292}
293
294template <class PointType, class LabelType>
295template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
296constexpr std::optional<
297 std::variant<
300OrientedLine<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
301 return this->asLine().template intersection<ResultNumber>(
302 other.asLine());
303}
304
305template <class PointType, class LabelType>
306template <class ResultNumber, SegmentConcept OtherSegment>
307constexpr auto OrientedLine<PointType, LabelType>::intersection(const OtherSegment& other) const {
308 return this->asLine().template intersection<ResultNumber>(other);
309}
310
311template <class PointType, class LabelType>
312template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
313constexpr auto OrientedLine<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
314 return this->asLine().template intersection<ResultNumber>(other);
315}
316
317// -----------------------------------------------------------------------------
318// Ray
319
320template <class PointType, class LabelType>
321template <class ResultNumber, PointConcept OtherPoint>
322constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
323Ray<PointType, LabelType>::intersection(const OtherPoint& other) const {
324 if (contains(other)) {
326 }
327 return {};
328}
329
330template <class PointType, class LabelType>
331template <class ResultNumber, LineConcept OtherLine>
332constexpr std::optional<
333 std::variant<
336Ray<PointType, LabelType>::intersection(const OtherLine& other) const {
338 if (isDegenerate()) {
339 if (other.contains(source())) {
340 return ResultPoint(source());
341 }
342 return {};
343 }
344
345 const auto line_intersection = this->asLine().template intersection<ResultNumber>(other);
346 if (!line_intersection) {
347 return {};
348 }
349
350 if (std::holds_alternative<Line<ResultPoint>>(*line_intersection)) {
351 return Ray<ResultPoint>(ResultPoint(source()), ResultPoint(target()));
352 }
353
354 const auto& point = std::get<ResultPoint>(*line_intersection);
355 if (contains(point)) {
356 return point;
357 }
358
359 return {};
360}
361
362template <class PointType, class LabelType>
363template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
364constexpr std::optional<
365 std::variant<
368Ray<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
369 return intersection<ResultNumber>(other.asLine());
370}
371
372template <class PointType, class LabelType>
373template <class ResultNumber, SegmentConcept OtherSegment>
374constexpr std::optional<
375 std::variant<
378Ray<PointType, LabelType>::intersection(const OtherSegment& other) const {
380 using ResultSegment = Segment<ResultPoint>;
381 if (isDegenerate()) {
382 if (other.contains(source())) {
383 return ResultPoint(source());
384 }
385 return {};
386 }
387
388 const auto line_intersection =
389 this->asLine().template intersection<ResultNumber>(
390 Line<typename OtherSegment::PointType>(other.min(), other.max()));
391 if (!line_intersection) {
392 return {};
393 }
394
395 if (std::holds_alternative<ResultPoint>(*line_intersection)) {
396 const auto& point = std::get<ResultPoint>(*line_intersection);
397 if (contains(point) && other.contains(point)) {
398 return point;
399 }
400 return {};
401 }
402
403 const ResultPoint ray_source(source());
404 const bool min_on_ray = contains(other.min());
405 const bool max_on_ray = contains(other.max());
406
407 if (min_on_ray && max_on_ray) {
408 return ResultSegment(ResultPoint(other.min()), ResultPoint(other.max()));
409 }
410
411 if (min_on_ray) {
412 const auto point = ResultPoint(other.min());
413 if (other.contains(ray_source) && point != ray_source) {
414 return ResultSegment(point, ray_source);
415 }
416 return point;
417 }
418
419 if (max_on_ray) {
420 const auto point = ResultPoint(other.max());
421 if (other.contains(ray_source) && point != ray_source) {
422 return ResultSegment(point, ray_source);
423 }
424 return point;
425 }
426
427 if (other.contains(ray_source)) {
428 return ray_source;
429 }
430
431 return {};
432}
433
434template <class PointType, class LabelType>
435template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
436constexpr std::optional<
437 std::variant<
440Ray<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
442}
443
444template <class PointType, class LabelType>
445template <class ResultNumber, RayConcept OtherRay>
446constexpr std::optional<
447 std::variant<
451Ray<PointType, LabelType>::intersection(const OtherRay& other) const {
453 using ResultSegment = Segment<ResultPoint>;
454 using ResultRay = Ray<ResultPoint>;
455 if (isDegenerate()) {
456 if (other.contains(source())) {
457 return ResultPoint(source());
458 }
459 return {};
460 }
461
462 if (other.isDegenerate()) {
463 if (contains(other.source())) {
464 return ResultPoint(other.source());
465 }
466 return {};
467 }
468
469 const auto line_intersection =
470 this->asLine().template intersection<ResultNumber>(
471 other.asLine());
472 if (!line_intersection) {
473 return {};
474 }
475
476 if (std::holds_alternative<ResultPoint>(*line_intersection)) {
477 const auto& point = std::get<ResultPoint>(*line_intersection);
478 if (contains(point) && other.contains(point)) {
479 return point;
480 }
481 return {};
482 }
483
484 if (source() == other.source()) {
485 if (contains(other.target())) {
486 return ResultRay(ResultPoint(source()), ResultPoint(target()));
487 }
488 return ResultPoint(source());
489 }
490
491 const bool this_contains_other_source = contains(other.source());
492 const bool other_contains_this_source = other.contains(source());
493
494 if (this_contains_other_source && other_contains_this_source) {
495 return ResultSegment(ResultPoint(source()), ResultPoint(other.source()));
496 }
497
498 if (this_contains_other_source) {
499 return ResultRay(ResultPoint(other.source()), ResultPoint(other.target()));
500 }
501
502 if (other_contains_this_source) {
503 return ResultRay(ResultPoint(source()), ResultPoint(target()));
504 }
505
506 return {};
507}
508
509// -----------------------------------------------------------------------------
510// Halfplane
511
512namespace detail {
513
514template <class ResultPoint>
515constexpr ResultPoint extendRayAlongLine(const ResultPoint& intersection, const ResultPoint& first, const ResultPoint& second) {
516 return intersection + (second - first);
517}
518
519} // namespace detail
520
521template <class PointType, class LabelType>
522template <class ResultNumber, PointConcept OtherPoint>
523constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
524Halfplane<PointType, LabelType>::intersection(const OtherPoint& other) const {
525 if (contains(other)) {
527 }
528 return {};
529}
530
531template <class PointType, class LabelType>
532template <class ResultNumber, LineConcept OtherLine>
533constexpr std::optional<
534 std::variant<
540 using ResultLine = Line<ResultPoint>;
541 using ResultRay = Ray<ResultPoint>;
542
543 if (isDegenerate()) {
544 if (other.contains(source())) {
545 return ResultPoint(source());
546 }
547 return {};
548 }
549
550 if (other.isDegenerate()) {
551 if (contains(other.min())) {
552 return ResultPoint(other.min());
553 }
554 return {};
555 }
556
557 const auto boundary_intersection =
558 this->asLine().template intersection<ResultNumber>(other);
559
560 if (!boundary_intersection) {
561 if (contains(other.min())) {
562 return ResultLine(ResultPoint(other.min()), ResultPoint(other.max()));
563 }
564 return {};
565 }
566
567 if (std::holds_alternative<ResultPoint>(*boundary_intersection)) {
568 const auto& point = std::get<ResultPoint>(*boundary_intersection);
569 const ResultPoint first(other.min());
570 const ResultPoint second(other.max());
571 const auto direction_side =
572 orientationDeterminant(source(), target(), other.max()) -
573 orientationDeterminant(source(), target(), other.min());
574 const auto zero = decltype(direction_side){};
575
576 if (zero < direction_side) {
577 return ResultRay(point, detail::extendRayAlongLine(point, first, second));
578 }
579
580 return ResultRay(point, detail::extendRayAlongLine(point, second, first));
581 }
582
583 return ResultLine(ResultPoint(other.min()), ResultPoint(other.max()));
584}
585
586template <class PointType, class LabelType>
587template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
588constexpr std::optional<
589 std::variant<
593Halfplane<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
594 return intersection<ResultNumber>(other.asLine());
595}
596
597template <class PointType, class LabelType>
598template <class ResultNumber, SegmentConcept OtherSegment>
599constexpr std::optional<
600 std::variant<
603Halfplane<PointType, LabelType>::intersection(const OtherSegment& other) const {
605 using ResultSegment = Segment<ResultPoint>;
606 using ResultLine = Line<ResultPoint>;
607 using ResultRay = Ray<ResultPoint>;
608
609 if (isDegenerate()) {
610 if (other.contains(source())) {
611 return ResultPoint(source());
612 }
613 return {};
614 }
615
616 if (other.isDegenerate()) {
617 if (contains(other.min())) {
618 return ResultPoint(other.min());
619 }
620 return {};
621 }
622
623 const auto supporting_intersection =
625 if (!supporting_intersection) {
626 return {};
627 }
628
629 if (std::holds_alternative<ResultPoint>(*supporting_intersection)) {
630 const auto& point = std::get<ResultPoint>(*supporting_intersection);
631 if (other.contains(point)) {
632 return point;
633 }
634 return {};
635 }
636
637 if (std::holds_alternative<ResultLine>(*supporting_intersection)) {
638 return ResultSegment(ResultPoint(other.min()), ResultPoint(other.max()));
639 }
640
641 return std::get<ResultRay>(*supporting_intersection).template intersection<ResultNumber>(other);
642}
643
644template <class PointType, class LabelType>
645template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
646constexpr std::optional<
647 std::variant<
650Halfplane<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
652}
653
654template <class PointType, class LabelType>
655template <class ResultNumber, RayConcept OtherRay>
656constexpr std::optional<
657 std::variant<
663 using ResultRay = Ray<ResultPoint>;
664
665 if (isDegenerate()) {
666 if (other.contains(source())) {
667 return ResultPoint(source());
668 }
669 return {};
670 }
671
672 if (other.isDegenerate()) {
673 if (contains(other.source())) {
674 return ResultPoint(other.source());
675 }
676 return {};
677 }
678
679 const auto supporting_intersection =
680 intersection<ResultNumber>(other.asLine());
681 if (!supporting_intersection) {
682 return {};
683 }
684
685 if (std::holds_alternative<ResultPoint>(*supporting_intersection)) {
686 const auto& point = std::get<ResultPoint>(*supporting_intersection);
687 if (other.contains(point)) {
688 return point;
689 }
690 return {};
691 }
692
693 if (std::holds_alternative<Line<ResultPoint>>(*supporting_intersection)) {
694 return ResultRay(ResultPoint(other.source()), ResultPoint(other.target()));
695 }
696
697 return std::get<ResultRay>(*supporting_intersection).template intersection<ResultNumber>(other);
698}
699
700template <class PointType, class LabelType>
701template <class ResultNumber, HalfplaneConcept OtherHalfplane>
703Halfplane<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
704 // Exact and division-free: the region simply stores the two half-planes,
705 // discarding one when it is redundant and flagging emptiness when they
706 // contradict each other.
708 result.insert(other);
709 return result;
710}
711
712// -----------------------------------------------------------------------------
713// Rectangle
714
715namespace detail {
716
717template <class PointType, std::size_t Capacity>
718struct UniqueIntersectionPoints {
719 constexpr void add(const PointType& point) {
720 for (std::size_t index = 0; index < size; ++index) {
721 if (points[index] == point) {
722 return;
723 }
724 }
725
726 points[size] = point;
727 ++size;
728 }
729
730 constexpr const PointType& operator[](std::size_t index) const {
731 return points[index];
732 }
733
734 constexpr PointType minPoint() const {
735 PointType result = points[0];
736 for (std::size_t index = 1; index < size; ++index) {
737 if (points[index] < result) {
738 result = points[index];
739 }
740 }
741 return result;
742 }
743
744 constexpr PointType maxPoint() const {
745 PointType result = points[0];
746 for (std::size_t index = 1; index < size; ++index) {
747 if (result < points[index]) {
748 result = points[index];
749 }
750 }
751 return result;
752 }
753
754 std::array<PointType, Capacity> points{};
755 std::size_t size = 0;
756};
757
758template <class SegmentType, class PointSet>
759constexpr void addSegmentEndpoints(const SegmentType& segment, PointSet& points) {
760 points.add(segment.min());
761 points.add(segment.max());
762}
763
764template <class PointSet>
765constexpr auto pointsToSegmentIntersection(const PointSet& points) {
766 using PointType = std::remove_cvref_t<decltype(points[0])>;
767 using SegmentType = Segment<PointType>;
768 using ResultType = std::optional<std::variant<PointType, SegmentType>>;
769
770 if (points.size == 0) {
771 return ResultType{};
772 }
773
774 if (points.size == 1) {
775 return ResultType(points[0]);
776 }
777
778 const auto first = points.minPoint();
779 const auto second = points.maxPoint();
780 if (first == second) {
781 return ResultType(first);
782 }
783
784 return ResultType(SegmentType(first, second));
785}
786
787template <class ResultNumber, class LabelType, class RectangleType, class LineType>
788constexpr auto rectangleLineIntersection(const RectangleType& rectangle, const LineType& line) {
789 using ResultPoint = Point<ResultNumber, LabelType>;
790 UniqueIntersectionPoints<ResultPoint, 8> points;
791
792 if (line.isDegenerate()) {
793 if (rectangle.contains(line.min())) {
794 points.add(ResultPoint(line.min()));
795 }
796 return pointsToSegmentIntersection(points);
797 }
798
799 const auto rectangle_edges = rectangle.edges();
800 for (const auto& edge : rectangle_edges) {
801 const Line<typename RectangleType::PointType> edge_line(edge.min(), edge.max());
802 const auto intersection = edge_line.template intersection<ResultNumber>(line);
803 if (!intersection) {
804 continue;
805 }
806
807 if (std::holds_alternative<ResultPoint>(*intersection)) {
808 const auto& point = std::get<ResultPoint>(*intersection);
809 if (edge.contains(point)) {
810 points.add(point);
811 }
812 } else {
813 const auto& overlap = std::get<Line<ResultPoint>>(*intersection);
814 static_cast<void>(overlap);
815 addSegmentEndpoints(Segment<ResultPoint>(ResultPoint(edge.min()), ResultPoint(edge.max())), points);
816 }
817 }
818
819 return pointsToSegmentIntersection(points);
820}
821
822template <class ResultNumber, class LabelType, class RectangleType, class SegmentType>
823constexpr auto rectangleSegmentIntersection(const RectangleType& rectangle, const SegmentType& segment) {
824 using ResultPoint = Point<ResultNumber, LabelType>;
825 UniqueIntersectionPoints<ResultPoint, 10> points;
826
827 if (rectangle.contains(segment.min())) {
828 points.add(ResultPoint(segment.min()));
829 }
830 if (rectangle.contains(segment.max())) {
831 points.add(ResultPoint(segment.max()));
832 }
833
834 const auto rectangle_edges = rectangle.edges();
835 for (const auto& edge : rectangle_edges) {
836 const auto intersection = edge.template intersection<ResultNumber>(segment);
837 if (!intersection) {
838 continue;
839 }
840
841 if (std::holds_alternative<ResultPoint>(*intersection)) {
842 points.add(std::get<ResultPoint>(*intersection));
843 } else {
844 addSegmentEndpoints(std::get<Segment<ResultPoint>>(*intersection), points);
845 }
846 }
847
848 return pointsToSegmentIntersection(points);
849}
850
851template <class ResultNumber, class LabelType, class RectangleType, class RayType>
852constexpr auto rectangleRayIntersection(const RectangleType& rectangle, const RayType& ray) {
853 using ResultPoint = Point<ResultNumber, LabelType>;
854 UniqueIntersectionPoints<ResultPoint, 10> points;
855
856 if (rectangle.contains(ray.source())) {
857 points.add(ResultPoint(ray.source()));
858 }
859
860 if (ray.isDegenerate()) {
861 return pointsToSegmentIntersection(points);
862 }
863
864 const Line<Point<ResultNumber, LabelType>> ray_line(ResultPoint(ray.source()), ResultPoint(ray.target()));
865 const auto rectangle_edges = rectangle.edges();
866 for (const auto& edge : rectangle_edges) {
867 const Line<typename RectangleType::PointType> edge_line(edge.min(), edge.max());
868 const auto intersection = edge_line.template intersection<ResultNumber>(ray_line);
869 if (!intersection) {
870 continue;
871 }
872
873 if (std::holds_alternative<ResultPoint>(*intersection)) {
874 const auto& point = std::get<ResultPoint>(*intersection);
875 if (edge.contains(point) && ray.contains(point)) {
876 points.add(point);
877 }
878 } else {
879 if (ray.contains(edge.min())) {
880 points.add(ResultPoint(edge.min()));
881 }
882 if (ray.contains(edge.max())) {
883 points.add(ResultPoint(edge.max()));
884 }
885 }
886 }
887
888 return pointsToSegmentIntersection(points);
889}
890
891template <class ResultNumber, class LabelType, class TriangleType, class LineType>
892constexpr auto triangleLineIntersection(const TriangleType& triangle, const LineType& line) {
893 using ResultPoint = Point<ResultNumber, LabelType>;
894 UniqueIntersectionPoints<ResultPoint, 8> points;
895
896 if (line.isDegenerate()) {
897 if (triangle.contains(line.min())) {
898 points.add(ResultPoint(line.min()));
899 }
900 return pointsToSegmentIntersection(points);
901 }
902
903 const auto triangle_edges = triangle.edges();
904 for (const auto& edge : triangle_edges) {
905 const Line<typename TriangleType::PointType> edge_line(edge.min(), edge.max());
906 const auto intersection = edge_line.template intersection<ResultNumber>(line);
907 if (!intersection) {
908 continue;
909 }
910
911 if (std::holds_alternative<ResultPoint>(*intersection)) {
912 const auto& point = std::get<ResultPoint>(*intersection);
913 if (edge.contains(point)) {
914 points.add(point);
915 }
916 } else {
917 addSegmentEndpoints(Segment<ResultPoint>(ResultPoint(edge.min()), ResultPoint(edge.max())), points);
918 }
919 }
920
921 return pointsToSegmentIntersection(points);
922}
923
924template <class ResultNumber, class LabelType, class TriangleType, class SegmentType>
925constexpr auto triangleSegmentIntersection(const TriangleType& triangle, const SegmentType& segment) {
926 using ResultPoint = Point<ResultNumber, LabelType>;
927 UniqueIntersectionPoints<ResultPoint, 10> points;
928
929 if (triangle.contains(segment.min())) {
930 points.add(ResultPoint(segment.min()));
931 }
932 if (triangle.contains(segment.max())) {
933 points.add(ResultPoint(segment.max()));
934 }
935
936 const auto triangle_edges = triangle.edges();
937 for (const auto& edge : triangle_edges) {
938 const auto intersection = edge.template intersection<ResultNumber>(segment);
939 if (!intersection) {
940 continue;
941 }
942
943 if (std::holds_alternative<ResultPoint>(*intersection)) {
944 points.add(std::get<ResultPoint>(*intersection));
945 } else {
946 addSegmentEndpoints(std::get<Segment<ResultPoint>>(*intersection), points);
947 }
948 }
949
950 return pointsToSegmentIntersection(points);
951}
952
953template <class ResultNumber, class LabelType, class TriangleType, class RayType>
954constexpr auto triangleRayIntersection(const TriangleType& triangle, const RayType& ray) {
955 using ResultPoint = Point<ResultNumber, LabelType>;
956 UniqueIntersectionPoints<ResultPoint, 10> points;
957
958 if (triangle.contains(ray.source())) {
959 points.add(ResultPoint(ray.source()));
960 }
961
962 if (ray.isDegenerate()) {
963 return pointsToSegmentIntersection(points);
964 }
965
966 const Line<Point<ResultNumber, LabelType>> ray_line(ResultPoint(ray.source()), ResultPoint(ray.target()));
967 const auto triangle_edges = triangle.edges();
968 for (const auto& edge : triangle_edges) {
969 const Line<typename TriangleType::PointType> edge_line(edge.min(), edge.max());
970 const auto intersection = edge_line.template intersection<ResultNumber>(ray_line);
971 if (!intersection) {
972 continue;
973 }
974
975 if (std::holds_alternative<ResultPoint>(*intersection)) {
976 const auto& point = std::get<ResultPoint>(*intersection);
977 if (edge.contains(point) && ray.contains(point)) {
978 points.add(point);
979 }
980 } else {
981 if (ray.contains(edge.min())) {
982 points.add(ResultPoint(edge.min()));
983 }
984 if (ray.contains(edge.max())) {
985 points.add(ResultPoint(edge.max()));
986 }
987 }
988 }
989
990 return pointsToSegmentIntersection(points);
991}
992
993} // namespace detail
994
995template <class PointType, class LabelType>
996template <class ResultNumber, PointConcept OtherPoint>
997constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
998Rectangle<PointType, LabelType>::intersection(const OtherPoint& other) const {
999 if (contains(other)) {
1001 }
1002 return {};
1003}
1004
1005template <class PointType, class LabelType>
1006template <class ResultNumber, RectangleConcept OtherRectangle>
1007constexpr std::optional<Rectangle<Point<ResultNumber, typename PointType::LabelType>>>
1008Rectangle<PointType, LabelType>::intersection(const OtherRectangle& other) const {
1010 using ResultRectangle = Rectangle<ResultPoint>;
1011
1012 if (!intersects(other)) {
1013 return {};
1014 }
1015
1016 const auto min_x = min().x() < other.min().x() ? other.min().x() : min().x();
1017 const auto min_y = min().y() < other.min().y() ? other.min().y() : min().y();
1018 const auto max_x = max().x() < other.max().x() ? max().x() : other.max().x();
1019 const auto max_y = max().y() < other.max().y() ? max().y() : other.max().y();
1020
1021 return ResultRectangle(
1022 ResultPoint(detail::asNumber<ResultNumber>(min_x), detail::asNumber<ResultNumber>(min_y)),
1023 ResultPoint(detail::asNumber<ResultNumber>(max_x), detail::asNumber<ResultNumber>(max_y)));
1024}
1025
1026template <class PointType, class LabelType>
1027template <class ResultNumber, LineConcept OtherLine>
1028constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1030 if (empty()) {
1031 // Nothing meets the empty set.
1032 return {};
1033 }
1034 return detail::rectangleLineIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1035}
1036
1037template <class PointType, class LabelType>
1038template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1039constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1040Rectangle<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
1041 return intersection<ResultNumber>(other.asLine());
1042}
1043
1044template <class PointType, class LabelType>
1045template <class ResultNumber, SegmentConcept OtherSegment>
1046constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1047Rectangle<PointType, LabelType>::intersection(const OtherSegment& other) const {
1048 if (empty()) {
1049 // Nothing meets the empty set.
1050 return {};
1051 }
1052 return detail::rectangleSegmentIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1053}
1054
1055template <class PointType, class LabelType>
1056template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1057constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1058Rectangle<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
1060}
1061
1062template <class PointType, class LabelType>
1063template <class ResultNumber, RayConcept OtherRay>
1064constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1066 if (empty()) {
1067 // Nothing meets the empty set.
1068 return {};
1069 }
1070 return detail::rectangleRayIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1071}
1072
1073// A half-plane clips a rectangle to the corners it keeps plus the at most two
1074// points where the boundary line crosses the rectangle. Walking the four
1075// corners counterclockwise emits that polygon in order, so no Convex is built
1076// for the rectangle and the clipped output needs no hull pass, which is what
1077// delegating to the generic convex clip would cost.
1078template <class PointType, class LabelType>
1079template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1080constexpr auto Rectangle<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
1082 using ResultSegment = Segment<ResultPoint>;
1083 using ResultConvex = Convex<ResultPoint>;
1084 using ResultType = std::optional<std::variant<ResultPoint, ResultSegment, ResultConvex>>;
1085
1086 if (empty()) {
1087 // Nothing meets the empty set.
1088 return ResultType{};
1089 }
1091 using ResultSegment = Segment<ResultPoint>;
1092 using ResultConvex = Convex<ResultPoint>;
1093 using ResultType = std::optional<std::variant<ResultPoint, ResultSegment, ResultConvex>>;
1094
1095 const std::array<PointType, 4> corners{min(), bottomRight(), max(), topLeft()};
1096 using Determinant = decltype(orientationDeterminant(other.source(), other.target(), corners[0]));
1097 std::array<Determinant, 4> sides{};
1098 for (std::size_t i = 0; i < 4; ++i) {
1099 sides[i] = orientationDeterminant(other.source(), other.target(), corners[i]);
1100 }
1101
1102 // Kept corners and crossings, counterclockwise. A repeated point can only
1103 // come from a degenerate rectangle, where dropping it is exactly what makes
1104 // the result collapse to a segment or a point.
1105 std::array<ResultPoint, 6> clipped{};
1106 std::size_t count = 0;
1107 const auto keep = [&clipped, &count](const ResultPoint& point) {
1108 if (count == 0 || clipped[count - 1] != point) {
1109 clipped[count++] = point;
1110 }
1111 };
1112
1113 const auto boundary = other.asLine();
1114 for (std::size_t i = 0; i < 4; ++i) {
1115 const std::size_t next = (i + 1) % 4;
1116 if (sides[i] >= 0) {
1117 keep(ResultPoint(corners[i]));
1118 }
1119 if (!((sides[i] > 0 && sides[next] < 0) || (sides[i] < 0 && sides[next] > 0))) {
1120 continue; // the edge stays on one side; a corner on the line is already kept
1121 }
1122 // Edges are axis-parallel and the boundary line strictly crosses this
1123 // one, so it is not parallel to it and one division gives the crossing,
1124 // leaving the coordinate along the edge exact.
1125 if (i % 2 == 0) { // bottom and top edges are horizontal
1126 keep(ResultPoint(*boundary.template xAtY<ResultNumber>(corners[i].y()),
1127 detail::asNumber<ResultNumber>(corners[i].y())));
1128 } else { // right and left edges are vertical
1129 keep(ResultPoint(detail::asNumber<ResultNumber>(corners[i].x()),
1130 *boundary.template yAtX<ResultNumber>(corners[i].x())));
1131 }
1132 }
1133 while (count > 1 && clipped[count - 1] == clipped[0]) {
1134 --count; // the walk closed back onto its first point
1135 }
1136
1137 if (count == 0) {
1138 return ResultType{};
1139 }
1140 if (count == 1) {
1141 return ResultType(clipped[0]);
1142 }
1143 if (count == 2) {
1144 return ResultType(ResultSegment(clipped[0], clipped[1]));
1145 }
1146
1147 // A Convex holds its vertices counterclockwise from the lexicographically
1148 // smallest one, which is where this walk starts only when the corner min()
1149 // survived the clip.
1150 std::size_t first = 0;
1151 for (std::size_t i = 1; i < count; ++i) {
1152 if (clipped[i] < clipped[first]) {
1153 first = i;
1154 }
1155 }
1156 std::vector<ResultPoint> vertices;
1157 vertices.reserve(count);
1158 for (std::size_t i = 0; i < count; ++i) {
1159 vertices.push_back(clipped[(first + i) % count]);
1160 }
1161 return ResultType(ResultConvex(std::move(vertices), /*trusted=*/true));
1162}
1163
1164// -----------------------------------------------------------------------------
1165// Triangle
1166
1167template <class PointType, class LabelType>
1168template <class ResultNumber, PointConcept OtherPoint>
1169constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
1170Triangle<PointType, LabelType>::intersection(const OtherPoint& other) const {
1171 if (contains(other)) {
1173 }
1174 return {};
1175}
1176
1177template <class PointType, class LabelType>
1178template <class ResultNumber, LineConcept OtherLine>
1179constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1181 return detail::triangleLineIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1182}
1183
1184template <class PointType, class LabelType>
1185template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1186constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1187Triangle<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
1188 return intersection<ResultNumber>(other.asLine());
1189}
1190
1191template <class PointType, class LabelType>
1192template <class ResultNumber, SegmentConcept OtherSegment>
1193constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1194Triangle<PointType, LabelType>::intersection(const OtherSegment& other) const {
1195 return detail::triangleSegmentIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1196}
1197
1198template <class PointType, class LabelType>
1199template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1200constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1201Triangle<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
1203}
1204
1205template <class PointType, class LabelType>
1206template <class ResultNumber, RayConcept OtherRay>
1207constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1209 return detail::triangleRayIntersection<ResultNumber, typename PointType::LabelType>(*this, other);
1210}
1211
1212// A triangle is convex, so a half-plane (or rectangle) clip is exactly the
1213// convex-polygon clip; delegating keeps the area overlap (a Convex) instead of
1214// dropping it, and reuses the exact O(log n + k) routine.
1215template <class PointType, class LabelType>
1216template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1217constexpr auto Triangle<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
1218 return asConvex().template intersection<ResultNumber>(other);
1219}
1220
1221template <class PointType, class LabelType>
1222template <class ResultNumber, RectangleConcept OtherRectangle>
1223constexpr auto Triangle<PointType, LabelType>::intersection(const OtherRectangle& other) const {
1224 return asConvex().template intersection<ResultNumber>(other);
1225}
1226
1227// Two triangles are convex, so their intersection is the convex-polygon clip of
1228// one against the other: an area overlap (Convex), a shared boundary segment, a
1229// single touch point, or nothing. Delegating to Convex::intersection keeps this
1230// exact and avoids re-deriving the clip here.
1231template <class PointType, class LabelType>
1232template <class ResultNumber, TriangleConcept OtherTriangle>
1233constexpr auto Triangle<PointType, LabelType>::intersection(const OtherTriangle& other) const {
1234 return asConvex().template intersection<ResultNumber>(other.asConvex());
1235}
1236
1237
1238// ---------------------------------------------------------------------------
1239// Convex
1240
1241template <class PointType, class LabelType>
1242template <class ResultNumber, PointConcept OtherPoint>
1243constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
1244Convex<PointType, LabelType>::intersection(const OtherPoint& other) const {
1245 if (contains(other)) {
1247 }
1248 return {};
1249}
1250
1251template <class PointType, class LabelType>
1252template <class ResultNumber, SegmentConcept OtherSegment>
1253constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherSegment& other) const {
1254 auto isec = this->template intersection<ResultNumber>(Line<typename OtherSegment::PointType>(other));
1255 if (!isec) {
1256 return {};
1257 }
1258 size_t index = isec->index();
1259 if (index == 0) {
1260 const auto& p = std::get<0>(*isec);
1261 if (other.containsCollinear(p)) {
1262 return p;
1263 }
1264 return {};
1265 }
1266 const auto& seg = std::get<1>(*isec);
1267 return seg.template intersection<ResultNumber>(other);
1268}
1269
1270template <class PointType, class LabelType>
1271template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1272constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
1274}
1275
1276template <class PointType, class LabelType>
1277template <class ResultNumber, LineConcept OtherLine>
1278constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherLine& other) const {
1279 if (points_.empty()) {
1280 return {};
1281 }
1282
1283 using CommonNumberType = std::common_type_t<NumberType, typename OtherLine::NumberType>;
1285
1286 const CommonPoint local_p0(detail::asNumber<CommonNumberType>(other[0].x()) - detail::asNumber<CommonNumberType>(translation_.x()),
1287 detail::asNumber<CommonNumberType>(other[0].y()) - detail::asNumber<CommonNumberType>(translation_.y()));
1288 const CommonPoint local_p1(detail::asNumber<CommonNumberType>(other[1].x()) - detail::asNumber<CommonNumberType>(translation_.x()),
1289 detail::asNumber<CommonNumberType>(other[1].y()) - detail::asNumber<CommonNumberType>(translation_.y()));
1290
1291 if (local_p0 == local_p1) {
1292 return {};
1293 }
1294
1295 const Line<CommonPoint> localLine(local_p0, local_p1);
1296
1297 auto orientationValue = [&](size_t index) {
1298 return orientationDeterminant(localLine[0], localLine[1], points_[index]);
1299 };
1300
1301 if (points_.size() == 1) {
1302 if (orientationValue(0) == CommonNumberType(0)) {
1305 return result + static_cast<Point<ResultNumber, typename PointType::LabelType>>(translation_);
1306 }
1307 return {};
1308 }
1309
1310 if (points_.size() == 2) {
1311 auto local_segment = Segment<PointType>(points_[0], points_[1]);
1312 auto isec = local_segment.template intersection<ResultNumber>(localLine);
1313 if (!isec) {
1314 return {};
1315 }
1316 const auto shift = static_cast<Point<ResultNumber, typename PointType::LabelType>>(translation_);
1317 if (std::holds_alternative<Point<ResultNumber, typename PointType::LabelType>>(*isec)) {
1318 return std::get<Point<ResultNumber, typename PointType::LabelType>>(*isec) + shift;
1319 }
1320 auto seg = std::get<Segment<Point<ResultNumber, typename PointType::LabelType>>>(*isec);
1321 return Segment<Point<ResultNumber, typename PointType::LabelType>>(seg[0] + shift, seg[1] + shift);
1322 }
1323
1324 auto max_it = detail::cyclicMax(points_.begin(), points_.end(),
1325 [&](const PointType& a) {
1326 return orientationDeterminant(localLine[0], localLine[1], a);
1327 });
1328 auto min_it = detail::cyclicMax(points_.begin(), points_.end(),
1329 [&](const PointType& a) {
1330 return orientationDeterminant(localLine[1], localLine[0], a);
1331 });
1332
1333 const size_t n = points_.size();
1334 const size_t i_max = static_cast<size_t>(std::distance(points_.begin(), max_it));
1335 const size_t i_min = static_cast<size_t>(std::distance(points_.begin(), min_it));
1336
1337 const CommonNumberType max_value = orientationValue(i_max);
1338 const CommonNumberType min_value = orientationValue(i_min);
1339
1340 if (max_value < CommonNumberType(0) || min_value > CommonNumberType(0)) {
1341 return {};
1342 }
1343
1344 auto translatePoint = [&](const Point<ResultNumber, typename PointType::LabelType>& point) {
1345 return point + static_cast<Point<ResultNumber, typename PointType::LabelType>>(translation_);
1346 };
1347
1348 auto translateSegment = [&](const Segment<Point<ResultNumber, typename PointType::LabelType>>& segment) {
1350 translatePoint(segment[0]), translatePoint(segment[1]));
1351 };
1352
1353 if (max_value == CommonNumberType(0) && min_value == CommonNumberType(0)) {
1354 const CommonPoint direction(localLine[1].x() - localLine[0].x(),
1355 localLine[1].y() - localLine[0].y());
1356
1357 auto extremal_high = detail::cyclicMax(points_.begin(), points_.end(),
1358 [&](const PointType& a) {
1359 return detail::asNumber<CommonNumberType>(a.x()) * direction.x() +
1360 detail::asNumber<CommonNumberType>(a.y()) * direction.y();
1361 });
1362
1363 auto extremal_low = detail::cyclicMax(points_.begin(), points_.end(),
1364 [&](const PointType& a) {
1365 return -(detail::asNumber<CommonNumberType>(a.x()) * direction.x() +
1366 detail::asNumber<CommonNumberType>(a.y()) * direction.y());
1367 });
1368
1369 Segment<PointType> support(*extremal_low, *extremal_high);
1370 auto isec = support.template intersection<ResultNumber>(localLine);
1371 if (!isec) {
1372 return {};
1373 }
1374 if (std::holds_alternative<Point<ResultNumber, typename PointType::LabelType>>(*isec)) {
1375 return translatePoint(std::get<Point<ResultNumber, typename PointType::LabelType>>(*isec));
1376 }
1377 return translateSegment(std::get<Segment<Point<ResultNumber, typename PointType::LabelType>>>(*isec));
1378 }
1379
1380 auto forwardDist = [&](size_t from, size_t to) {
1381 return (to + n - from) % n;
1382 };
1383
1384 auto findBoundaryForward = [&](size_t start, size_t length) -> size_t {
1385 size_t lo = 1;
1386 size_t hi = length;
1387 size_t result = length + 1;
1388 while (lo <= hi) {
1389 size_t mid = lo + (hi - lo) / 2;
1390 if (orientationValue((start + mid) % n) <= CommonNumberType(0)) {
1391 result = mid;
1392 hi = mid - 1;
1393 } else {
1394 lo = mid + 1;
1395 }
1396 }
1397 return result;
1398 };
1399
1400 auto findBoundaryBackward = [&](size_t start, size_t length) -> size_t {
1401 size_t lo = 1;
1402 size_t hi = length;
1403 size_t result = length + 1;
1404 while (lo <= hi) {
1405 size_t mid = lo + (hi - lo) / 2;
1406 size_t index = (start + n - mid) % n;
1407 if (orientationValue(index) <= CommonNumberType(0)) {
1408 result = mid;
1409 hi = mid - 1;
1410 } else {
1411 lo = mid + 1;
1412 }
1413 }
1414 return result;
1415 };
1416
1417 const size_t forward_len = forwardDist(i_max, i_min);
1418 const size_t backward_len = forwardDist(i_min, i_max);
1419
1420 const size_t boundary_forward = findBoundaryForward(i_max, forward_len);
1421 const size_t boundary_backward = findBoundaryBackward(i_max, backward_len);
1422
1423 if (boundary_forward > forward_len || boundary_backward > backward_len) {
1424 return {};
1425 }
1426
1427 const size_t b1 = (i_max + boundary_forward) % n;
1428 const size_t b1_prev = (b1 + n - 1) % n;
1429 const size_t b2 = (i_max + n - boundary_backward) % n;
1430 const size_t b2_next = (b2 + 1) % n;
1431
1432 auto collectPoints = [&](const Segment<PointType>& edge,
1433 std::vector<Point<ResultNumber, typename PointType::LabelType>>& points) {
1434 auto edge_isec = edge.template intersection<ResultNumber>(localLine);
1435 if (!edge_isec) {
1436 return;
1437 }
1438 if (std::holds_alternative<Point<ResultNumber, typename PointType::LabelType>>(*edge_isec)) {
1439 points.push_back(std::get<Point<ResultNumber, typename PointType::LabelType>>(*edge_isec));
1440 } else {
1441 auto seg = std::get<Segment<Point<ResultNumber, typename PointType::LabelType>>>(*edge_isec);
1442 points.push_back(seg[0]);
1443 points.push_back(seg[1]);
1444 }
1445 };
1446
1447 std::vector<Point<ResultNumber, typename PointType::LabelType>> points;
1448 points.reserve(4);
1449 collectPoints(Segment<PointType>(points_[b1_prev], points_[b1]), points);
1450 collectPoints(Segment<PointType>(points_[b2], points_[b2_next]), points);
1451
1452 if (points.empty()) {
1453 return {};
1454 }
1455
1456 const CommonPoint direction(localLine[1].x() - localLine[0].x(),
1457 localLine[1].y() - localLine[0].y());
1458
1459 auto project = [&](const Point<ResultNumber, typename PointType::LabelType>& point) {
1460 return (detail::asNumber<CommonNumberType>(point.x()) - localLine[0].x()) * direction.x() +
1461 (detail::asNumber<CommonNumberType>(point.y()) - localLine[0].y()) * direction.y();
1462 };
1463
1464 std::sort(points.begin(), points.end(), [&](auto const& a, auto const& b) {
1465 return project(a) < project(b);
1466 });
1467 points.erase(std::unique(points.begin(), points.end()), points.end());
1468
1469 if (points.empty()) {
1470 return {};
1471 }
1472 if (points.size() == 1) {
1473 return translatePoint(points[0]);
1474 }
1475
1476 return Segment<Point<ResultNumber, typename PointType::LabelType>>(translatePoint(points.front()),
1477 translatePoint(points.back()));
1478}
1479
1480template <class PointType, class LabelType>
1481template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1482constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
1484}
1485
1486template <class PointType, class LabelType>
1487template <class ResultNumber, RayConcept OtherRay>
1488constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherRay& other) const {
1489 auto line_isec = intersection<ResultNumber>(Line<typename OtherRay::PointType>(other[0], other[1]));
1490 if (!line_isec) {
1491 return {};
1492 }
1493 size_t index = line_isec->index();
1494 if (index == 0) {
1495 if (other.containsCollinear(std::get<0>(*line_isec))) {
1496 return std::get<0>(*line_isec);
1497 }
1498 return {};
1499 }
1500 // index == 1
1501 auto seg = std::get<1>(*line_isec);
1502 return seg.template intersection<ResultNumber>(other);
1503}
1504
1505// Clip the convex polygon to a closed half-plane. The vertices inside the
1506// half-plane form one contiguous arc (orientation is unimodal along a convex
1507// hull), so finding one inside vertex and walking outward to the two boundary
1508// crossings yields the clipped region in O(log n + k) for an output of size k.
1509template <class PointType, class LabelType>
1510template <class ResultNumber, HalfplaneConcept OtherHalfplane>
1511constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>, Convex<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
1513 using ResultSegment = Segment<ResultPoint>;
1514 using ResultConvex = Convex<ResultPoint>;
1515
1516 const std::ptrdiff_t n = static_cast<std::ptrdiff_t>(points_.size());
1517 if (n == 0) {
1518 return {};
1519 }
1520
1521 // Any vertex inside the closed half-plane anchors the inside arc; the deepest
1522 // one is found in O(log n) and is positive when one is strictly inside.
1523 const auto it = detail::cyclicMaxOrPositive(points_.begin(), points_.end(),
1524 [&other, this](const PointType& a) {
1525 return orientationDeterminant(other.source(), other.target(), a + translation_);
1526 });
1527 const std::ptrdiff_t start = it - points_.begin();
1528
1529 if (!other.contains(get(start))) {
1530 return {}; // every vertex is strictly outside the half-plane
1531 }
1532
1533 // Walk CCW (forward) over the contiguous arc of vertices in the half-plane;
1534 // get() indexes the vertices cyclically, so f and b may run past the ends.
1535 std::ptrdiff_t f = start, fSteps = 0;
1536 while (fSteps + 1 < n && other.contains(get(f + 1))) {
1537 ++f;
1538 ++fSteps;
1539 }
1540 if (fSteps + 1 == n) {
1541 // The whole convex lies in the closed half-plane; return it unchanged.
1542 ResultConvex whole(*this, /*trusted=*/true);
1543 if (whole.size() == 1) return whole[0];
1544 if (whole.size() == 2) return ResultSegment(whole[0], whole[1]);
1545 return whole;
1546 }
1547
1548 // Walk CW (backward) over the same arc.
1549 std::ptrdiff_t b = start, bSteps = 0;
1550 while (bSteps + fSteps + 1 < n && other.contains(get(b - 1))) {
1551 --b;
1552 ++bSteps;
1553 }
1554
1555 // The arc spans b .. f (CCW); the edges leaving it at each end cross the
1556 // boundary line. A crossing edge has one endpoint inside and one strictly
1557 // outside, so it meets the boundary line at exactly one point.
1558 auto crossing = [&](std::ptrdiff_t insideIdx, std::ptrdiff_t outsideIdx) {
1559 const ResultSegment edge(static_cast<ResultPoint>(get(insideIdx)),
1560 static_cast<ResultPoint>(get(outsideIdx)));
1561 return std::get<ResultPoint>(*edge.template intersection<ResultNumber>(other.asLine()));
1562 };
1563
1564 std::vector<ResultPoint> result;
1565 result.reserve(static_cast<std::size_t>(fSteps + bSteps + 3));
1566 result.push_back(crossing(b, b - 1)); // entering crossing
1567 for (std::ptrdiff_t i = b; ; ++i) {
1568 result.push_back(static_cast<ResultPoint>(get(i)));
1569 if (i == f) break;
1570 }
1571 result.push_back(crossing(f, f + 1)); // leaving crossing
1572
1573 ResultConvex convex(std::move(result));
1574 if (convex.size() == 1) return convex[0];
1575 if (convex.size() == 2) return ResultSegment(convex[0], convex[1]);
1576 return convex;
1577}
1578
1579template <class PointType, class LabelType>
1580template <class ResultNumber, RectangleConcept OtherRectangle>
1581constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>, Convex<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherRectangle& other) const {
1582 return intersection<ResultNumber>(other.asConvex());
1583}
1584
1585template <class PointType, class LabelType>
1586template <class ResultNumber, TriangleConcept OtherTriangle>
1587constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>, Convex<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherTriangle& other) const {
1588 return intersection<ResultNumber>(other.asConvex());
1589}
1590
1591template <class PointType, class LabelType>
1592template <class ResultNumber, ConvexConcept OtherConvex>
1593constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>, Convex<Point<ResultNumber, typename PointType::LabelType>>>> Convex<PointType, LabelType>::intersection(const OtherConvex& other) const {
1594 if (size() == 0 || other.size() == 0 || !intersects(other)) {
1595 return {};
1596 }
1597
1599
1600 if (size() == 1) {
1601 ResultPointType point = static_cast<ResultPointType>(points_[0] + translation_);
1602 if (other.contains(point)) {
1603 return point;
1604 }
1605 return {};
1606 }
1607
1608 if (other.size() == 1) {
1609 ResultPointType point = static_cast<ResultPointType>(other[0]);
1610 if (contains(point)) {
1611 return point;
1612 }
1613 return {};
1614 }
1615
1616 std::vector<ResultPointType> isecPoints;
1617
1618 for (auto &edge : other.edges()) {
1619 auto isec = intersection<ResultNumber>(edge);
1620 if (isec) {
1621 if (std::holds_alternative<Point<ResultNumber, typename PointType::LabelType>>(*isec)) {
1622 isecPoints.push_back(std::get<Point<ResultNumber, typename PointType::LabelType>>(*isec));
1623 } else {
1624 auto seg = std::get<Segment<Point<ResultNumber, typename PointType::LabelType>>>(*isec);
1625 isecPoints.push_back(seg[0]);
1626 isecPoints.push_back(seg[1]);
1627 }
1628 }
1629 }
1630 for (auto &edge : edges()) {
1631 auto isec = other.template intersection<ResultNumber>(edge);
1632 if (isec) {
1633 if (std::holds_alternative<ResultPointType>(*isec)) {
1634 isecPoints.push_back(std::get<ResultPointType>(*isec));
1635 } else {
1636 auto seg = std::get<Segment<ResultPointType>>(*isec);
1637 isecPoints.push_back(seg[0]);
1638 isecPoints.push_back(seg[1]);
1639 }
1640 }
1641 }
1642
1643 if (isecPoints.empty()) {
1644 return {};
1645 }
1646
1647 Convex<ResultPointType> convex(std::move(isecPoints));
1648
1649 if (convex.size() == 1) {
1650 return convex[0];
1651 }
1652 if (convex.size() == 2) {
1653 return Segment<ResultPointType>(convex[0], convex[1]);
1654 }
1655
1656 return convex;
1657}
1658
1659// ---------------------------------------------------------------------------
1660// Clipping a one-dimensional shape to an area bounded by rings
1661//
1662// A polygon and a region differ here only in how many rings bound them, so the
1663// walk below takes the boundary edges and nothing else: the even-odd rule reads
1664// a ring system exactly as it reads a single ring, and a point inside a hole is
1665// inside two rings and hence outside the area. The three shape-specific
1666// wrappers add only the parameter window their shape occupies on its supporting
1667// line, which leaves Polygon and PolygonWithHoles a one-line overload each.
1668
1669namespace detail {
1670
1672template <class ResultPoint>
1673using LinePieces = std::vector<std::variant<ResultPoint, Segment<ResultPoint>>>;
1674
1681template <class ResultNumber, class ResultPoint>
1682struct LineSpan {
1683 ResultNumber lo, hi;
1684 ResultPoint plo, phi;
1685};
1686
1704template <class ResultNumber, class ResultPoint, class EdgeRange>
1705constexpr std::vector<LineSpan<ResultNumber, ResultPoint>>
1706lineAreaSpans(const ResultPoint& a, const ResultPoint& b, const EdgeRange& edges) {
1707 using Span = LineSpan<ResultNumber, ResultPoint>;
1708
1709 const ResultPoint direction = b - a;
1710 const Line<ResultPoint> line(a, b);
1711 auto tOf = [&](const ResultPoint& p) -> ResultNumber { return (p - a) * direction; };
1712
1713 // Signed side of a vertex w.r.t. the line (CCW positive, 0 on the line).
1714 auto sideOf = [&](const ResultPoint& v) -> int {
1715 const auto o = orientationSign(a, b, v);
1716 if (o > 0) return 1;
1717 if (o < 0) return -1;
1718 return 0;
1719 };
1720
1721 auto makeSpan = [](ResultNumber t1, ResultNumber t2, const ResultPoint& p1, const ResultPoint& p2) -> Span {
1722 return (t1 <= t2) ? Span{t1, t2, p1, p2} : Span{t2, t1, p2, p1};
1723 };
1724 std::vector<Span> spans;
1725
1726 // Boundary crossings of the line, used to recover inside/outside by ray
1727 // parity. A vertex on the line is treated as if perturbed to the -1 side,
1728 // which counts vertex touches and collinear edges consistently: each edge
1729 // whose perturbed endpoint signs differ contributes one crossing. Each
1730 // vertex is the source of exactly one directed edge of its ring, so which
1731 // way the ring is wound changes nothing here — and hole rings are wound the
1732 // other way from the outer one.
1733 std::vector<std::pair<ResultNumber, ResultPoint>> crossings;
1734
1735 for (const auto& edge : edges) {
1736 using EdgePoint = typename std::remove_cvref_t<decltype(edge)>::PointType;
1737 const ResultPoint u = static_cast<ResultPoint>(edge.source());
1738 const ResultPoint w = static_cast<ResultPoint>(edge.target());
1739 const int su = sideOf(u);
1740 const int sw = sideOf(w);
1741
1742 if (su == 0 && sw == 0) {
1743 // Edge collinear with the line: a boundary overlap, always closed.
1744 spans.push_back(makeSpan(tOf(u), tOf(w), u, w));
1745 continue;
1746 }
1747 if (su == 0) {
1748 // Vertex on the line: a boundary touch point (recorded once, here,
1749 // as the source vertex of its edge).
1750 spans.push_back({tOf(u), tOf(u), u, u});
1751 }
1752
1753 if (su != 0 && sw != 0 && su != sw) {
1754 // Transversal crossing through the edge interior.
1755 const auto isec = line.template intersection<ResultNumber>(
1756 Segment<EdgePoint>(edge.source(), edge.target()));
1757 if (isec && std::holds_alternative<ResultPoint>(*isec)) {
1758 const ResultPoint c = std::get<ResultPoint>(*isec);
1759 spans.push_back({tOf(c), tOf(c), c, c});
1760 crossings.emplace_back(tOf(c), c);
1761 }
1762 } else {
1763 // Perturbed crossing located at whichever endpoint is on the line.
1764 const int eu = (su != 0) ? su : -1;
1765 const int ew = (sw != 0) ? sw : -1;
1766 if (eu != ew) {
1767 const ResultPoint& onLine = (su == 0) ? u : w;
1768 crossings.emplace_back(tOf(onLine), onLine);
1769 }
1770 }
1771 }
1772
1773 // Walk the crossings in order; the open cell to the right of a crossing is
1774 // inside the area exactly when an odd number of crossings lie to its left.
1775 // Each maximal inside cell becomes a closed interval (its endpoints are
1776 // boundary crossings, hence in the closed area too).
1777 std::sort(crossings.begin(), crossings.end(),
1778 [](const auto& x, const auto& y) { return x.first < y.first; });
1779 std::size_t idx = 0;
1780 int parity = 0;
1781 while (idx < crossings.size()) {
1782 const ResultNumber tcur = crossings[idx].first;
1783 const ResultPoint pcur = crossings[idx].second;
1784 std::size_t next = idx;
1785 while (next < crossings.size() && crossings[next].first == tcur) {
1786 ++next;
1787 }
1788 parity += static_cast<int>(next - idx);
1789 if ((parity & 1) && next < crossings.size()) {
1790 spans.push_back({tcur, crossings[next].first, pcur, crossings[next].second});
1791 }
1792 idx = next;
1793 }
1794
1795 // Union the spans: sort by lo, then merge touching or overlapping ones.
1796 std::sort(spans.begin(), spans.end(),
1797 [](const Span& x, const Span& y) { return x.lo != y.lo ? x.lo < y.lo : x.hi < y.hi; });
1798 std::vector<Span> merged;
1799 for (const Span& s : spans) {
1800 if (merged.empty() || s.lo > merged.back().hi) {
1801 merged.push_back(s);
1802 } else if (s.hi > merged.back().hi) {
1803 merged.back().hi = s.hi;
1804 merged.back().phi = s.phi;
1805 }
1806 }
1807 return merged;
1808}
1809
1819template <class ResultPoint, class Area, class EdgeRange, class OtherSegment>
1820constexpr LinePieces<ResultPoint>
1821areaSegmentIntersection(const Area& area, const EdgeRange& edges, const OtherSegment& other) {
1822 using ResultNumber = typename ResultPoint::NumberType;
1823 using ResultSegment = Segment<ResultPoint>;
1824
1825 LinePieces<ResultPoint> result;
1826 const ResultPoint a(other.min());
1827 const ResultPoint b(other.max());
1828
1829 // A degenerate segment is just its single point.
1830 if (other.isDegenerate()) {
1831 if (area.contains(a)) {
1832 result.emplace_back(a);
1833 }
1834 return result;
1835 }
1836
1837 // The segment is the parameter window [0, T] of its supporting line.
1838 const ResultNumber T = (b - a) * (b - a);
1839 for (const auto& span : lineAreaSpans<ResultNumber>(a, b, edges)) {
1840 if (span.hi < ResultNumber(0) || span.lo > T) {
1841 continue;
1842 }
1843 const ResultPoint lo = (span.lo < ResultNumber(0)) ? a : span.plo;
1844 const ResultPoint hi = (span.hi > T) ? b : span.phi;
1845 if (lo == hi) {
1846 result.emplace_back(lo);
1847 } else {
1848 result.emplace_back(ResultSegment(lo, hi));
1849 }
1850 }
1851 return result;
1852}
1853
1863template <class ResultPoint, class Area, class EdgeRange, class OtherLine>
1864constexpr LinePieces<ResultPoint>
1865areaLineIntersection(const Area& area, const EdgeRange& edges, const OtherLine& other) {
1866 using ResultNumber = typename ResultPoint::NumberType;
1867 using ResultSegment = Segment<ResultPoint>;
1868
1869 LinePieces<ResultPoint> result;
1870 const ResultPoint a(other.min());
1871 const ResultPoint b(other.max());
1872
1873 // A degenerate line is a single point.
1874 if (other.isDegenerate()) {
1875 if (area.contains(a)) {
1876 result.emplace_back(a);
1877 }
1878 return result;
1879 }
1880
1881 // The line is its whole parametrization, so nothing is clipped away.
1882 for (const auto& span : lineAreaSpans<ResultNumber>(a, b, edges)) {
1883 if (span.lo == span.hi) {
1884 result.emplace_back(span.plo);
1885 } else {
1886 result.emplace_back(ResultSegment(span.plo, span.phi));
1887 }
1888 }
1889 return result;
1890}
1891
1901template <class ResultPoint, class Area, class EdgeRange, class OtherRay>
1902constexpr LinePieces<ResultPoint>
1903areaRayIntersection(const Area& area, const EdgeRange& edges, const OtherRay& other) {
1904 using ResultNumber = typename ResultPoint::NumberType;
1905 using ResultSegment = Segment<ResultPoint>;
1906
1907 LinePieces<ResultPoint> result;
1908 const ResultPoint a(other.source());
1909 const ResultPoint b(other.target());
1910
1911 // A degenerate ray is a single point.
1912 if (other.isDegenerate()) {
1913 if (area.contains(a)) {
1914 result.emplace_back(a);
1915 }
1916 return result;
1917 }
1918
1919 // The source is the parameter origin, so the ray is the window t >= 0.
1920 for (const auto& span : lineAreaSpans<ResultNumber>(a, b, edges)) {
1921 if (span.hi < ResultNumber(0)) {
1922 continue;
1923 }
1924 const ResultPoint lo = (span.lo < ResultNumber(0)) ? a : span.plo;
1925 if (lo == span.phi) {
1926 result.emplace_back(lo);
1927 } else {
1928 result.emplace_back(ResultSegment(lo, span.phi));
1929 }
1930 }
1931 return result;
1932}
1933
1934} // namespace detail
1935
1936// ---------------------------------------------------------------------------
1937// Polygon
1938
1939template <class PointType, class LabelType>
1940template <class ResultNumber, PointConcept OtherPoint>
1941constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
1942Polygon<PointType, LabelType>::intersection(const OtherPoint& other) const {
1943 if (contains(other)) {
1945 }
1946 return {};
1947}
1948
1949template <class PointType, class LabelType>
1950template <class ResultNumber, SegmentConcept OtherSegment>
1951constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1952Polygon<PointType, LabelType>::intersection(const OtherSegment& other) const {
1954 return detail::areaSegmentIntersection<ResultPoint>(*this, orientedEdgesView(), other);
1955}
1956
1957template <class PointType, class LabelType>
1958template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
1959constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1960Polygon<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
1962}
1963
1964template <class PointType, class LabelType>
1965template <class ResultNumber, LineConcept OtherLine>
1966constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1967Polygon<PointType, LabelType>::intersection(const OtherLine& other) const {
1969 return detail::areaLineIntersection<ResultPoint>(*this, orientedEdgesView(), other);
1970}
1971
1972template <class PointType, class LabelType>
1973template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
1974constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1975Polygon<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
1976 return intersection<ResultNumber>(other.asLine());
1977}
1978
1979template <class PointType, class LabelType>
1980template <class ResultNumber, RayConcept OtherRay>
1981constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>>>
1984 return detail::areaRayIntersection<ResultPoint>(*this, orientedEdgesView(), other);
1985}
1986
1987template <class PointType, class LabelType>
1988template <class ResultNumber, PolygonConcept OtherPolygon>
1989constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Polyline<Point<ResultNumber, typename PointType::LabelType>>, Polygon<Point<ResultNumber, typename PointType::LabelType>>>>
1990Polygon<PointType, LabelType>::intersection(const OtherPolygon& other) const {
1992 using ResultSegment = Segment<ResultPoint>;
1993 using ResultPolyline = Polyline<ResultPoint>;
1994 using ResultPolygon = Polygon<ResultPoint>;
1995 using Piece = std::variant<ResultPoint, ResultPolyline, ResultPolygon>;
1996
1997 // The boundary of A ∩ B is (∂A ∩ B) ∪ (∂B ∩ A): clip every edge of each
1998 // polygon against the other and collect the resulting boundary pieces. The
1999 // sets deduplicate shared boundary so each segment/point is stored once.
2000 std::set<ResultSegment> segments;
2001 std::set<ResultPoint> touchPoints;
2002
2003 auto clipEdgesAgainst = [&](const auto& edgePolygon, const auto& clipPolygon) {
2004 for (const auto& edge : edgePolygon.edges()) {
2005 for (const auto& piece : clipPolygon.template intersection<ResultNumber>(edge)) {
2006 if (std::holds_alternative<ResultPoint>(piece)) {
2007 touchPoints.insert(std::get<ResultPoint>(piece));
2008 } else {
2009 segments.insert(std::get<ResultSegment>(piece));
2010 }
2011 }
2012 }
2013 };
2014 clipEdgesAgainst(*this, other);
2015 clipEdgesAgainst(other, *this);
2016
2017 // Make the collected boundary a consistent planar graph: split any segment at
2018 // another segment's endpoint lying in its interior, then dedup, so partially
2019 // overlapping collinear (shared) boundary collapses onto common edges. Only
2020 // genuine pinch points keep degree > 2 afterwards.
2021 auto planarize = [](const std::set<ResultSegment>& input) {
2022 std::vector<ResultPoint> vertices;
2023 for (const auto& s : input) {
2024 vertices.push_back(s.min());
2025 vertices.push_back(s.max());
2026 }
2027 std::sort(vertices.begin(), vertices.end());
2028 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
2029
2030 std::set<ResultSegment> out;
2031 for (const auto& s : input) {
2032 const ResultPoint a = s.min();
2033 const ResultPoint b = s.max();
2034 std::vector<ResultPoint> cut{a, b};
2035 for (const auto& v : vertices) {
2036 if (v == a || v == b || !collinear(a, b, v)) {
2037 continue;
2038 }
2039 if (v.x() < std::min(a.x(), b.x()) || v.x() > std::max(a.x(), b.x()) ||
2040 v.y() < std::min(a.y(), b.y()) || v.y() > std::max(a.y(), b.y())) {
2041 continue; // collinear but outside the segment
2042 }
2043 cut.push_back(v);
2044 }
2045 std::sort(cut.begin(), cut.end());
2046 cut.erase(std::unique(cut.begin(), cut.end()), cut.end());
2047 for (std::size_t i = 0; i + 1 < cut.size(); ++i) {
2048 out.insert(ResultSegment(cut[i], cut[i + 1]));
2049 }
2050 }
2051 return out;
2052 };
2053 segments = planarize(segments);
2054
2055 // Build the undirected graph: nodes are endpoints, edges are the segments.
2056 std::map<ResultPoint, std::vector<ResultPoint>> adjacency;
2057 for (const auto& segment : segments) {
2058 adjacency[segment.min()].push_back(segment.max());
2059 adjacency[segment.max()].push_back(segment.min());
2060 }
2061
2062 std::vector<Piece> result;
2063
2064 // A touch point that no segment uses is an isolated intersection point.
2065 for (const auto& point : touchPoints) {
2066 if (adjacency.find(point) == adjacency.end()) {
2067 result.emplace_back(point);
2068 }
2069 }
2070
2071 using Number = ResultNumber;
2072 auto degree = [&adjacency](const ResultPoint& p) {
2073 const auto it = adjacency.find(p);
2074 return it == adjacency.end() ? std::size_t(0) : it->second.size();
2075 };
2076 auto removeEdge = [&adjacency](const ResultPoint& u, const ResultPoint& v) {
2077 auto& au = adjacency[u];
2078 au.erase(std::find(au.begin(), au.end(), v));
2079 auto& av = adjacency[v];
2080 av.erase(std::find(av.begin(), av.end(), u));
2081 };
2082
2083 // Peel the open boundary (degree-1 chains, and whiskers hanging off a cycle)
2084 // into polylines, removing their edges. What remains is a union of closed
2085 // cycles that may share articulation (pinch) vertices.
2086 while (true) {
2087 const ResultPoint* leaf = nullptr;
2088 for (const auto& [p, neighbors] : adjacency) {
2089 if (neighbors.size() == 1) {
2090 leaf = &p;
2091 break;
2092 }
2093 }
2094 if (!leaf) {
2095 break;
2096 }
2097 std::vector<ResultPoint> path;
2098 ResultPoint current = *leaf;
2099 path.push_back(current);
2100 while (degree(current) == 1) {
2101 const ResultPoint next = adjacency.at(current)[0];
2102 removeEdge(current, next);
2103 path.push_back(next);
2104 current = next;
2105 }
2106 result.emplace_back(ResultPolyline(std::move(path)));
2107 }
2108
2109 // Trace the remaining cycles as faces of the planar graph: following, at each
2110 // vertex, the neighbour immediately clockwise from the edge we arrived on
2111 // visits every face once and hugs its interior. Counterclockwise faces are
2112 // the filled intersection pieces; the clockwise outer face is dropped. This
2113 // resolves the articulation (degree > 2) vertices into their separate cycles.
2114 auto rotationalNext = [&adjacency](const ResultPoint& at, const ResultPoint& from) {
2115 const auto& neighbors = adjacency.at(at);
2116 auto dx = [&](const ResultPoint& p) { return p.x() - at.x(); };
2117 auto dy = [&](const ResultPoint& p) { return p.y() - at.y(); };
2118 // 0 for directions in [0, 180) degrees, 1 for [180, 360); orders the circle.
2119 auto half = [&](const ResultPoint& p) {
2120 const Number y = dy(p);
2121 if (y > Number(0)) return 0;
2122 if (y < Number(0)) return 1;
2123 return dx(p) >= Number(0) ? 0 : 1;
2124 };
2125 auto ccwBefore = [&](const ResultPoint& u, const ResultPoint& w) { // u strictly CCW-before w
2126 const int hu = half(u), hw = half(w);
2127 if (hu != hw) {
2128 return hu < hw;
2129 }
2130 return dx(u) * dy(w) - dy(u) * dx(w) > Number(0);
2131 };
2132 // The neighbour immediately clockwise from `from`: the CCW-largest one
2133 // that is CCW-before `from`, wrapping to the CCW-largest overall.
2134 const ResultPoint* best = nullptr;
2135 for (const auto& n : neighbors) {
2136 if (n == from || !ccwBefore(n, from)) {
2137 continue;
2138 }
2139 if (!best || ccwBefore(*best, n)) {
2140 best = &n;
2141 }
2142 }
2143 if (!best) {
2144 for (const auto& n : neighbors) {
2145 if (n != from && (!best || ccwBefore(*best, n))) {
2146 best = &n;
2147 }
2148 }
2149 }
2150 return *best;
2151 };
2152
2153 std::set<std::pair<ResultPoint, ResultPoint>> usedDart;
2154 for (const auto& [u, neighbors] : adjacency) {
2155 for (const auto& v : neighbors) {
2156 if (usedDart.count({u, v})) {
2157 continue;
2158 }
2159 std::vector<ResultPoint> cycle;
2160 ResultPoint a = u;
2161 ResultPoint b = v;
2162 do {
2163 usedDart.insert({a, b});
2164 cycle.push_back(a);
2165 const ResultPoint c = rotationalNext(b, a);
2166 a = b;
2167 b = c;
2168 } while (a != u || b != v);
2169
2170 // Shoelace twice-area; keep counterclockwise (filled) faces only.
2171 Number twiceArea(0);
2172 for (std::size_t i = 0; i < cycle.size(); ++i) {
2173 const ResultPoint& p = cycle[i];
2174 const ResultPoint& q = cycle[(i + 1) % cycle.size()];
2175 twiceArea += p.x() * q.y() - q.x() * p.y();
2176 }
2177 if (twiceArea > Number(0)) {
2178 result.emplace_back(ResultPolygon(cycle));
2179 }
2180 }
2181 }
2182
2183 return result;
2184}
2185
2186template <class PointType, class LabelType>
2187template <class ResultNumber, ConvexConcept OtherConvex>
2188constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Polyline<Point<ResultNumber, typename PointType::LabelType>>, Polygon<Point<ResultNumber, typename PointType::LabelType>>>>
2189Polygon<PointType, LabelType>::intersection(const OtherConvex& other) const {
2190 return this->template intersection<ResultNumber>(other.asPolygon());
2191}
2192
2193template <class PointType, class LabelType>
2194template <class ResultNumber, TriangleConcept OtherTriangle>
2195constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Polyline<Point<ResultNumber, typename PointType::LabelType>>, Polygon<Point<ResultNumber, typename PointType::LabelType>>>>
2196Polygon<PointType, LabelType>::intersection(const OtherTriangle& other) const {
2197 return this->template intersection<ResultNumber>(other.asConvex());
2198}
2199
2200template <class PointType, class LabelType>
2201template <class ResultNumber, RectangleConcept OtherRectangle>
2202constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Polyline<Point<ResultNumber, typename PointType::LabelType>>, Polygon<Point<ResultNumber, typename PointType::LabelType>>>>
2203Polygon<PointType, LabelType>::intersection(const OtherRectangle& other) const {
2204 return this->template intersection<ResultNumber>(other.asConvex());
2205}
2206
2207template <class PointType, class LabelType>
2208template <class ResultNumber, HalfplaneConcept OtherHalfplane>
2209constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>, Segment<Point<ResultNumber, typename PointType::LabelType>>, Polygon<Point<ResultNumber, typename PointType::LabelType>>>>
2210Polygon<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
2212 using ResultSegment = Segment<ResultPoint>;
2213 using ResultPolygon = Polygon<ResultPoint>;
2214 using Piece = std::variant<ResultPoint, ResultSegment, ResultPolygon>;
2215
2216 std::vector<Piece> result;
2217 if (size() == 0) {
2218 return result;
2219 }
2220
2221 // A degenerate half-plane is a single point.
2222 if (other.isDegenerate()) {
2223 const ResultPoint p(other.source());
2224 if (contains(p)) {
2225 result.emplace_back(p);
2226 }
2227 return result;
2228 }
2229
2230 // The boundary of P ∩ H is (∂P ∩ H) ∪ (∂H ∩ P): clip each polygon edge to
2231 // the closed half-plane, and clip the half-plane's boundary line to the
2232 // polygon. The sets deduplicate shared boundary.
2233 std::set<ResultSegment> segments;
2234 std::set<ResultPoint> touchPoints;
2235
2236 auto collect = [&](const std::optional<std::variant<ResultPoint, ResultSegment>>& piece) {
2237 if (!piece) {
2238 return;
2239 }
2240 if (std::holds_alternative<ResultPoint>(*piece)) {
2241 touchPoints.insert(std::get<ResultPoint>(*piece));
2242 } else {
2243 segments.insert(std::get<ResultSegment>(*piece));
2244 }
2245 };
2246
2247 for (const auto& edge : edges()) {
2248 collect(other.template intersection<ResultNumber>(edge));
2249 }
2250 for (const auto& piece : intersection<ResultNumber>(Line<typename OtherHalfplane::PointType>(other.source(), other.target()))) {
2251 if (std::holds_alternative<ResultPoint>(piece)) {
2252 touchPoints.insert(std::get<ResultPoint>(piece));
2253 } else {
2254 segments.insert(std::get<ResultSegment>(piece));
2255 }
2256 }
2257
2258 // Make the collected boundary a consistent planar graph (see the polygon
2259 // overload): split each segment at any other segment's endpoint in its
2260 // interior, then dedup, so overlapping collinear shared boundary collapses.
2261 auto planarize = [](const std::set<ResultSegment>& input) {
2262 std::vector<ResultPoint> vertices;
2263 for (const auto& s : input) {
2264 vertices.push_back(s.min());
2265 vertices.push_back(s.max());
2266 }
2267 std::sort(vertices.begin(), vertices.end());
2268 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
2269
2270 std::set<ResultSegment> out;
2271 for (const auto& s : input) {
2272 const ResultPoint a = s.min();
2273 const ResultPoint b = s.max();
2274 std::vector<ResultPoint> cut{a, b};
2275 for (const auto& v : vertices) {
2276 if (v == a || v == b || !collinear(a, b, v)) {
2277 continue;
2278 }
2279 if (v.x() < std::min(a.x(), b.x()) || v.x() > std::max(a.x(), b.x()) ||
2280 v.y() < std::min(a.y(), b.y()) || v.y() > std::max(a.y(), b.y())) {
2281 continue;
2282 }
2283 cut.push_back(v);
2284 }
2285 std::sort(cut.begin(), cut.end());
2286 cut.erase(std::unique(cut.begin(), cut.end()), cut.end());
2287 for (std::size_t i = 0; i + 1 < cut.size(); ++i) {
2288 out.insert(ResultSegment(cut[i], cut[i + 1]));
2289 }
2290 }
2291 return out;
2292 };
2293 segments = planarize(segments);
2294
2295 // Build the undirected graph: nodes are endpoints, edges are the segments.
2296 std::map<ResultPoint, std::vector<ResultPoint>> adjacency;
2297 for (const auto& segment : segments) {
2298 adjacency[segment.min()].push_back(segment.max());
2299 adjacency[segment.max()].push_back(segment.min());
2300 }
2301
2302 // A touch point that no segment uses is an isolated intersection point.
2303 for (const auto& point : touchPoints) {
2304 if (adjacency.find(point) == adjacency.end()) {
2305 result.emplace_back(point);
2306 }
2307 }
2308
2309 using Number = ResultNumber;
2310 auto degree = [&adjacency](const ResultPoint& p) {
2311 const auto it = adjacency.find(p);
2312 return it == adjacency.end() ? std::size_t(0) : it->second.size();
2313 };
2314 auto removeEdge = [&adjacency](const ResultPoint& u, const ResultPoint& v) {
2315 auto& au = adjacency[u];
2316 au.erase(std::find(au.begin(), au.end(), v));
2317 auto& av = adjacency[v];
2318 av.erase(std::find(av.begin(), av.end(), u));
2319 };
2320
2321 // Peel the open boundary into segments. Every 1D piece lies on the
2322 // half-plane's straight boundary, so each peeled chain is collinear and is
2323 // returned as the single segment spanning its two ends.
2324 while (true) {
2325 const ResultPoint* leaf = nullptr;
2326 for (const auto& [p, neighbors] : adjacency) {
2327 if (neighbors.size() == 1) {
2328 leaf = &p;
2329 break;
2330 }
2331 }
2332 if (!leaf) {
2333 break;
2334 }
2335 const ResultPoint start = *leaf;
2336 ResultPoint current = start;
2337 while (degree(current) == 1) {
2338 const ResultPoint next = adjacency.at(current)[0];
2339 removeEdge(current, next);
2340 current = next;
2341 }
2342 result.emplace_back(ResultSegment(start, current));
2343 }
2344
2345 // Trace the remaining cycles as faces; keep the counterclockwise (filled)
2346 // ones. The clockwise-next rule resolves any articulation (pinch) vertices.
2347 auto rotationalNext = [&adjacency](const ResultPoint& at, const ResultPoint& from) {
2348 const auto& neighbors = adjacency.at(at);
2349 auto dx = [&](const ResultPoint& p) { return p.x() - at.x(); };
2350 auto dy = [&](const ResultPoint& p) { return p.y() - at.y(); };
2351 auto half = [&](const ResultPoint& p) {
2352 const Number y = dy(p);
2353 if (y > Number(0)) return 0;
2354 if (y < Number(0)) return 1;
2355 return dx(p) >= Number(0) ? 0 : 1;
2356 };
2357 auto ccwBefore = [&](const ResultPoint& u, const ResultPoint& w) {
2358 const int hu = half(u), hw = half(w);
2359 if (hu != hw) {
2360 return hu < hw;
2361 }
2362 return dx(u) * dy(w) - dy(u) * dx(w) > Number(0);
2363 };
2364 const ResultPoint* best = nullptr;
2365 for (const auto& n : neighbors) {
2366 if (n == from || !ccwBefore(n, from)) {
2367 continue;
2368 }
2369 if (!best || ccwBefore(*best, n)) {
2370 best = &n;
2371 }
2372 }
2373 if (!best) {
2374 for (const auto& n : neighbors) {
2375 if (n != from && (!best || ccwBefore(*best, n))) {
2376 best = &n;
2377 }
2378 }
2379 }
2380 return *best;
2381 };
2382
2383 std::set<std::pair<ResultPoint, ResultPoint>> usedDart;
2384 for (const auto& [u, neighbors] : adjacency) {
2385 for (const auto& v : neighbors) {
2386 if (usedDart.count({u, v})) {
2387 continue;
2388 }
2389 std::vector<ResultPoint> cycle;
2390 ResultPoint a = u;
2391 ResultPoint b = v;
2392 do {
2393 usedDart.insert({a, b});
2394 cycle.push_back(a);
2395 const ResultPoint c = rotationalNext(b, a);
2396 a = b;
2397 b = c;
2398 } while (a != u || b != v);
2399
2400 Number twiceArea(0);
2401 for (std::size_t i = 0; i < cycle.size(); ++i) {
2402 const ResultPoint& p = cycle[i];
2403 const ResultPoint& q = cycle[(i + 1) % cycle.size()];
2404 twiceArea += p.x() * q.y() - q.x() * p.y();
2405 }
2406 if (twiceArea > Number(0)) {
2407 result.emplace_back(ResultPolygon(cycle));
2408 }
2409 }
2410 }
2411
2412 return result;
2413}
2414
2415// ---------------------------------------------------------------------------
2416// Disk
2417
2418template <class PointType, class LabelType>
2419template <class ResultNumber, PointConcept OtherPoint>
2420constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
2421Disk<PointType, LabelType>::intersection(const OtherPoint& other) const {
2422 if (contains(other)) {
2424 }
2425 return {};
2426}
2427
2428// ---------------------------------------------------------------------------
2429// MonotoneChain
2430
2431template <class PointType, class LabelType, class Storage>
2432template <class ResultNumber, MonotoneChainConcept OtherChain>
2433constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2437 using ResultSegment = Segment<ResultPoint>;
2438 using Piece = std::variant<ResultPoint, ResultSegment>;
2439
2440 std::vector<Piece> pieces;
2441 if (empty() || other.empty()) {
2442 return pieces;
2443 }
2444 if (size() == 1) {
2445 if (other.contains((*this)[0])) {
2446 pieces.emplace_back(ResultPoint((*this)[0]));
2447 }
2448 return pieces;
2449 }
2450 if (other.size() == 1) {
2451 if (contains(other[0])) {
2452 pieces.emplace_back(ResultPoint(other[0]));
2453 }
2454 return pieces;
2455 }
2456
2457 // Merge sweep, mirroring intersects(): every edge pair with overlapping
2458 // x-ranges delegates to the segment-segment intersection. Pairs skipped by
2459 // a tied advance could only contribute the shared right endpoint, which
2460 // the pair tested at the tie already reports.
2461 std::size_t i = 0;
2462 std::size_t j = 0;
2463 const std::size_t iEnd = size() - 1;
2464 const std::size_t jEnd = other.size() - 1;
2465 while (i < iEnd && j < jEnd) {
2466 const Segment<PointType> mine((*this)[i], (*this)[i + 1]);
2467 const Segment<typename OtherChain::PointType> theirs(other[j], other[j + 1]);
2468 if (!(mine.max().x() < theirs.min().x() || theirs.max().x() < mine.min().x())) {
2469 if (auto piece = mine.template intersection<ResultNumber>(theirs)) {
2470 pieces.push_back(std::move(*piece));
2471 }
2472 }
2473 const auto order = mine.max() <=> theirs.max();
2474 if (order <= 0) {
2475 ++i;
2476 }
2477 if (order >= 0) {
2478 ++j;
2479 }
2480 }
2481
2482 return coalescePieces<ResultNumber>(std::move(pieces));
2483}
2484
2485template <class PointType, class LabelType, class Storage>
2486template <class ResultNumber>
2487constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2489MonotoneChain<PointType, LabelType, Storage>::coalescePieces(
2493 using ResultSegment = Segment<ResultPoint>;
2494 using Piece = std::variant<ResultPoint, ResultSegment>;
2495
2496 // The fold can report the same touch point through several edge pairs and
2497 // splits a long collinear overlap at every vertex it crosses; sort the
2498 // pieces by their lexicographic minimum (segments before points, so a
2499 // following point is absorbed by the segment sharing its position), then
2500 // coalesce in one pass.
2501 std::sort(pieces.begin(), pieces.end(), [](const Piece& lhs, const Piece& rhs) {
2502 const auto pieceMin = [](const Piece& piece) -> ResultPoint {
2503 if (const auto* point = std::get_if<ResultPoint>(&piece)) {
2504 return *point;
2505 }
2506 return std::get<ResultSegment>(piece).min();
2507 };
2508 const ResultPoint leftMin = pieceMin(lhs);
2509 const ResultPoint rightMin = pieceMin(rhs);
2510 if (leftMin != rightMin) {
2511 return leftMin < rightMin;
2512 }
2513 const bool leftIsPoint = std::holds_alternative<ResultPoint>(lhs);
2514 const bool rightIsPoint = std::holds_alternative<ResultPoint>(rhs);
2515 if (leftIsPoint != rightIsPoint) {
2516 return !leftIsPoint;
2517 }
2518 if (leftIsPoint) {
2519 return false;
2520 }
2521 return std::get<ResultSegment>(lhs).max() < std::get<ResultSegment>(rhs).max();
2522 });
2523
2524 std::vector<Piece> result;
2525 for (const Piece& piece : pieces) {
2526 if (const auto* point = std::get_if<ResultPoint>(&piece)) {
2527 if (!result.empty()) {
2528 // Sorting guarantees any piece that can cover this point has
2529 // already been placed, and (both pieces lying on this chain's
2530 // monotone arc) the covering piece is then the previous one.
2531 const Piece& last = result.back();
2532 if (const auto* lastPoint = std::get_if<ResultPoint>(&last)) {
2533 if (*lastPoint == *point) {
2534 continue;
2535 }
2536 } else if (std::get<ResultSegment>(last).contains(*point)) {
2537 continue;
2538 }
2539 }
2540 result.push_back(piece);
2541 continue;
2542 }
2543 const auto& segment = std::get<ResultSegment>(piece);
2544 if (!result.empty()) {
2545 if (auto* lastSegment = std::get_if<ResultSegment>(&result.back());
2546 lastSegment != nullptr && !(segment.min() < lastSegment->min()) &&
2547 !(lastSegment->max() < segment.min()) &&
2548 collinear(lastSegment->min(), lastSegment->max(), segment.min()) &&
2549 collinear(lastSegment->min(), lastSegment->max(), segment.max())) {
2550 // Collinear continuation (chains overlapping across a shared
2551 // vertex without a bend): extend instead of splitting.
2552 if (lastSegment->max() < segment.max()) {
2553 *lastSegment = ResultSegment(lastSegment->min(), segment.max());
2554 }
2555 continue;
2556 }
2557 }
2558 result.push_back(piece);
2559 }
2560 return result;
2561}
2562
2563template <class PointType, class LabelType, class Storage>
2564template <class ResultNumber, PointConcept OtherPoint>
2565constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
2567 if (contains(other)) {
2569 }
2570 return {};
2571}
2572
2573template <class PointType, class LabelType, class Storage>
2574template <class ResultNumber, class OtherShape>
2575constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2577MonotoneChain<PointType, LabelType, Storage>::edgeFoldIntersection(const OtherShape& other) const {
2579 using ResultSegment = Segment<ResultPoint>;
2580 using Piece = std::variant<ResultPoint, ResultSegment>;
2581
2582 std::vector<Piece> pieces;
2583 if (empty()) {
2584 return pieces;
2585 }
2586 if (size() == 1) {
2587 if (other.contains((*this)[0])) {
2588 pieces.emplace_back(ResultPoint((*this)[0]));
2589 }
2590 return pieces;
2591 }
2592 for (std::size_t i = 0; i + 1 < size(); ++i) {
2593 const auto piece =
2594 this->template boundaryAt<false>(i).template intersection<ResultNumber>(other);
2595 if (piece) {
2596 // Re-wrap in this chain's result types: the delegated intersection
2597 // labels its points with the other shape's label type.
2598 pieces.push_back(std::visit(
2599 [](const auto& value) -> Piece {
2600 if constexpr (detail::is_point_v<std::remove_cvref_t<decltype(value)>>) {
2601 return Piece(ResultPoint(value));
2602 } else {
2603 return Piece(ResultSegment(value));
2604 }
2605 },
2606 *piece));
2607 }
2608 }
2609 return coalescePieces<ResultNumber>(std::move(pieces));
2610}
2611
2612template <class PointType, class LabelType, class Storage>
2613template <class ResultNumber, SegmentConcept OtherSegment>
2614constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2615 Segment<Point<ResultNumber, typename PointType::LabelType>>>>
2617 return this->template edgeFoldIntersection<ResultNumber>(other);
2618}
2619
2620template <class PointType, class LabelType, class Storage>
2621template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
2622constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2624MonotoneChain<PointType, LabelType, Storage>::intersection(const OtherOrientedSegment& other) const {
2625 return this->template edgeFoldIntersection<ResultNumber>(other);
2626}
2627
2628template <class PointType, class LabelType, class Storage>
2629template <class ResultNumber, LineConcept OtherLine>
2630constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2633 return this->template edgeFoldIntersection<ResultNumber>(other);
2634}
2635
2636template <class PointType, class LabelType, class Storage>
2637template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
2638constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2641 return this->template edgeFoldIntersection<ResultNumber>(other);
2642}
2643
2644template <class PointType, class LabelType, class Storage>
2645template <class ResultNumber, RayConcept OtherRay>
2646constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2649 return this->template edgeFoldIntersection<ResultNumber>(other);
2650}
2651
2652template <class PointType, class LabelType, class Storage>
2653template <class ResultNumber, HalfplaneConcept OtherHalfplane>
2654constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2657 return this->template edgeFoldIntersection<ResultNumber>(other);
2658}
2659
2660template <class PointType, class LabelType, class Storage>
2661template <class ResultNumber, RectangleConcept OtherRectangle>
2662constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2665 return this->template edgeFoldIntersection<ResultNumber>(other);
2666}
2667
2668template <class PointType, class LabelType, class Storage>
2669template <class ResultNumber, TriangleConcept OtherTriangle>
2670constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2673 return this->template edgeFoldIntersection<ResultNumber>(other);
2674}
2675
2676template <class PointType, class LabelType, class Storage>
2677template <class ResultNumber, ConvexConcept OtherConvex>
2678constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2681 return this->template edgeFoldIntersection<ResultNumber>(other);
2682}
2683
2684// ---------------------------------------------------------------------------
2685// Polyline
2686
2687template <class PointType, class LabelType>
2688template <class ResultNumber, PointConcept OtherPoint>
2689constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
2690Polyline<PointType, LabelType>::intersection(const OtherPoint& other) const {
2691 if (contains(other)) {
2693 }
2694 return {};
2695}
2696
2697template <class PointType, class LabelType>
2698template <class ResultNumber>
2699constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2701Polyline<PointType, LabelType>::coalescePieces(
2705 using ResultSegment = Segment<ResultPoint>;
2706 using Piece = std::variant<ResultPoint, ResultSegment>;
2707
2708 // Merge the segment pieces first. The kept segments never collinearly
2709 // touch each other (every insertion absorbs all kept segments it touches),
2710 // so any kept segment connected to a new one through the growing union
2711 // must touch the new segment itself: one scan over the kept list per
2712 // insertion suffices, in any processing order.
2713 std::vector<ResultSegment> segments;
2714 for (const Piece& piece : pieces) {
2715 const auto* segment = std::get_if<ResultSegment>(&piece);
2716 if (segment == nullptr) {
2717 continue;
2718 }
2719 ResultSegment merged = *segment;
2720 for (std::size_t i = segments.size(); i-- > 0;) {
2721 const ResultSegment& kept = segments[i];
2722 if (collinear(merged.min(), merged.max(), kept.min()) &&
2723 collinear(merged.min(), merged.max(), kept.max()) &&
2724 merged.intersects(kept)) {
2725 // Collinear touching segments cover exactly the span between
2726 // their lexicographically extreme endpoints.
2727 merged = ResultSegment(std::min(merged.min(), kept.min()),
2728 std::max(merged.max(), kept.max()));
2729 segments.erase(segments.begin() + static_cast<std::ptrdiff_t>(i));
2730 }
2731 }
2732 segments.push_back(std::move(merged));
2733 }
2734
2735 // A touch point can be reported by several edge pairs and may lie on a
2736 // reported segment; keep one copy of each point no segment covers.
2737 std::vector<ResultPoint> points;
2738 for (const Piece& piece : pieces) {
2739 if (const auto* point = std::get_if<ResultPoint>(&piece)) {
2740 points.push_back(*point);
2741 }
2742 }
2743 std::sort(points.begin(), points.end());
2744 points.erase(std::unique(points.begin(), points.end()), points.end());
2745
2746 std::vector<Piece> result;
2747 result.reserve(segments.size() + points.size());
2748 for (const ResultPoint& point : points) {
2749 if (std::none_of(segments.begin(), segments.end(),
2750 [&point](const ResultSegment& segment) { return segment.contains(point); })) {
2751 result.emplace_back(point);
2752 }
2753 }
2754 for (ResultSegment& segment : segments) {
2755 result.emplace_back(std::move(segment));
2756 }
2757
2758 // Same output order as MonotoneChain::coalescePieces: lexicographic
2759 // minimum, segments before points sharing their minimum, then by maximum.
2760 std::sort(result.begin(), result.end(), [](const Piece& lhs, const Piece& rhs) {
2761 const auto pieceMin = [](const Piece& piece) -> ResultPoint {
2762 if (const auto* point = std::get_if<ResultPoint>(&piece)) {
2763 return *point;
2764 }
2765 return std::get<ResultSegment>(piece).min();
2766 };
2767 const ResultPoint leftMin = pieceMin(lhs);
2768 const ResultPoint rightMin = pieceMin(rhs);
2769 if (leftMin != rightMin) {
2770 return leftMin < rightMin;
2771 }
2772 const bool leftIsPoint = std::holds_alternative<ResultPoint>(lhs);
2773 const bool rightIsPoint = std::holds_alternative<ResultPoint>(rhs);
2774 if (leftIsPoint != rightIsPoint) {
2775 return !leftIsPoint;
2776 }
2777 if (leftIsPoint) {
2778 return false;
2779 }
2780 return std::get<ResultSegment>(lhs).max() < std::get<ResultSegment>(rhs).max();
2781 });
2782 return result;
2783}
2784
2785template <class PointType, class LabelType>
2786template <class ResultNumber, class OtherShape>
2787constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2788 Segment<Point<ResultNumber, typename PointType::LabelType>>>>
2789Polyline<PointType, LabelType>::edgeFoldIntersection(const OtherShape& other) const {
2790 using ResultPoint = Point<ResultNumber, typename PointType::LabelType>;
2791 using ResultSegment = Segment<ResultPoint>;
2792 using Piece = std::variant<ResultPoint, ResultSegment>;
2793
2794 std::vector<Piece> pieces;
2795 if (empty()) {
2796 return pieces;
2797 }
2798 if (size() == 1) {
2799 if (other.contains((*this)[0])) {
2800 pieces.emplace_back(ResultPoint((*this)[0]));
2801 }
2802 return pieces;
2803 }
2804 for (std::size_t i = 0; i + 1 < size(); ++i) {
2805 const auto piece =
2806 this->template boundaryAt<false>(i).template intersection<ResultNumber>(other);
2807 if (piece) {
2808 // Re-wrap in this polyline's result types: the delegated
2809 // intersection labels its points with the other shape's label type.
2810 pieces.push_back(std::visit(
2811 [](const auto& value) -> Piece {
2812 if constexpr (detail::is_point_v<std::remove_cvref_t<decltype(value)>>) {
2813 return Piece(ResultPoint(value));
2814 } else {
2815 return Piece(ResultSegment(value));
2816 }
2817 },
2818 *piece));
2819 }
2820 }
2821 return coalescePieces<ResultNumber>(std::move(pieces));
2822}
2823
2824template <class PointType, class LabelType>
2825template <class ResultNumber, SegmentConcept OtherSegment>
2826constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2827 Segment<Point<ResultNumber, typename PointType::LabelType>>>>
2828Polyline<PointType, LabelType>::intersection(const OtherSegment& other) const {
2829 return this->template edgeFoldIntersection<ResultNumber>(other);
2830}
2831
2832template <class PointType, class LabelType>
2833template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
2834constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2836Polyline<PointType, LabelType>::intersection(const OtherOrientedSegment& other) const {
2837 return this->template edgeFoldIntersection<ResultNumber>(other);
2838}
2839
2840template <class PointType, class LabelType>
2841template <class ResultNumber, LineConcept OtherLine>
2842constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2845 return this->template edgeFoldIntersection<ResultNumber>(other);
2846}
2847
2848template <class PointType, class LabelType>
2849template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
2850constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2852Polyline<PointType, LabelType>::intersection(const OtherOrientedLine& other) const {
2853 return this->template edgeFoldIntersection<ResultNumber>(other);
2854}
2855
2856template <class PointType, class LabelType>
2857template <class ResultNumber, RayConcept OtherRay>
2858constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2861 return this->template edgeFoldIntersection<ResultNumber>(other);
2862}
2863
2864template <class PointType, class LabelType>
2865template <class ResultNumber, HalfplaneConcept OtherHalfplane>
2866constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2868Polyline<PointType, LabelType>::intersection(const OtherHalfplane& other) const {
2869 return this->template edgeFoldIntersection<ResultNumber>(other);
2870}
2871
2872template <class PointType, class LabelType>
2873template <class ResultNumber, RectangleConcept OtherRectangle>
2874constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2876Polyline<PointType, LabelType>::intersection(const OtherRectangle& other) const {
2877 return this->template edgeFoldIntersection<ResultNumber>(other);
2878}
2879
2880template <class PointType, class LabelType>
2881template <class ResultNumber, TriangleConcept OtherTriangle>
2882constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2884Polyline<PointType, LabelType>::intersection(const OtherTriangle& other) const {
2885 return this->template edgeFoldIntersection<ResultNumber>(other);
2886}
2887
2888template <class PointType, class LabelType>
2889template <class ResultNumber, ConvexConcept OtherConvex>
2890constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2892Polyline<PointType, LabelType>::intersection(const OtherConvex& other) const {
2893 return this->template edgeFoldIntersection<ResultNumber>(other);
2894}
2895
2896template <class PointType, class LabelType>
2897template <class ResultNumber, MonotoneChainConcept OtherChain>
2898constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2900Polyline<PointType, LabelType>::intersection(const OtherChain& other) const {
2902 using ResultSegment = Segment<ResultPoint>;
2903 using Piece = std::variant<ResultPoint, ResultSegment>;
2904
2905 std::vector<Piece> pieces;
2906 if (empty() || other.empty()) {
2907 return pieces;
2908 }
2909 if (size() == 1) {
2910 if (other.contains((*this)[0])) {
2911 pieces.emplace_back(ResultPoint((*this)[0]));
2912 }
2913 return pieces;
2914 }
2915 if (other.size() == 1) {
2916 if (contains(other[0])) {
2917 pieces.emplace_back(ResultPoint(other[0]));
2918 }
2919 return pieces;
2920 }
2921 if (!bbox().intersects(other.bbox())) {
2922 return pieces;
2923 }
2924
2925 // All-pairs edge test: a self-intersecting polyline has no monotone
2926 // structure to drive a merge sweep.
2927 for (std::size_t i = 0; i + 1 < size(); ++i) {
2928 const Segment<PointType> mine((*this)[i], (*this)[i + 1]);
2929 for (std::size_t j = 0; j + 1 < other.size(); ++j) {
2930 const Segment<typename OtherChain::PointType> theirs(other[j], other[j + 1]);
2931 if (auto piece = mine.template intersection<ResultNumber>(theirs)) {
2932 pieces.push_back(std::move(*piece));
2933 }
2934 }
2935 }
2936 return coalescePieces<ResultNumber>(std::move(pieces));
2937}
2938
2939template <class PointType, class LabelType>
2940template <class ResultNumber, PolylineConcept OtherPolyline>
2941constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2943Polyline<PointType, LabelType>::intersection(const OtherPolyline& other) const {
2945 using ResultSegment = Segment<ResultPoint>;
2946 using Piece = std::variant<ResultPoint, ResultSegment>;
2947
2948 std::vector<Piece> pieces;
2949 if (empty() || other.empty()) {
2950 return pieces;
2951 }
2952 if (size() == 1) {
2953 if (other.contains((*this)[0])) {
2954 pieces.emplace_back(ResultPoint((*this)[0]));
2955 }
2956 return pieces;
2957 }
2958 if (other.size() == 1) {
2959 if (contains(other[0])) {
2960 pieces.emplace_back(ResultPoint(other[0]));
2961 }
2962 return pieces;
2963 }
2964 if (!bbox().intersects(other.bbox())) {
2965 return pieces;
2966 }
2967
2968 // All-pairs edge test: neither polyline has a monotone structure to prune
2969 // the scan.
2970 for (std::size_t i = 0; i + 1 < size(); ++i) {
2971 const Segment<PointType> mine((*this)[i], (*this)[i + 1]);
2972 for (std::size_t j = 0; j + 1 < other.size(); ++j) {
2973 const Segment<typename OtherPolyline::PointType> theirs(other[j], other[j + 1]);
2974 if (auto piece = mine.template intersection<ResultNumber>(theirs)) {
2975 pieces.push_back(std::move(*piece));
2976 }
2977 }
2978 }
2979 return coalescePieces<ResultNumber>(std::move(pieces));
2980}
2981
2982template <class PointType, class LabelType>
2983template <class ResultNumber, class OtherArea>
2985constexpr std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
2989 using ResultSegment = Segment<ResultPoint>;
2990 using Piece = std::variant<ResultPoint, ResultSegment>;
2991
2992 std::vector<Piece> pieces;
2993 if (empty()) {
2994 return pieces;
2995 }
2996 if (size() == 1) {
2997 if (other.contains((*this)[0])) {
2998 pieces.emplace_back(ResultPoint((*this)[0]));
2999 }
3000 return pieces;
3001 }
3002 // Unlike the convex edgeFoldIntersection, a non-convex area can split a
3003 // single edge into several disjoint pieces, so each edge yields a vector.
3004 for (std::size_t i = 0; i + 1 < size(); ++i) {
3005 const auto edgePieces =
3006 this->template boundaryAt<false>(i).template intersection<ResultNumber>(other);
3007 for (const auto& piece : edgePieces) {
3008 // Re-wrap in this polyline's result types: the delegated
3009 // intersection labels its points with the area's label type.
3010 pieces.push_back(std::visit(
3011 [](const auto& value) -> Piece {
3012 if constexpr (detail::is_point_v<std::remove_cvref_t<decltype(value)>>) {
3013 return Piece(ResultPoint(value));
3014 } else {
3015 return Piece(ResultSegment(value));
3016 }
3017 },
3018 piece));
3019 }
3020 }
3021 return coalescePieces<ResultNumber>(std::move(pieces));
3022}
3023
3024// Polygon outranks Polyline and MonotoneChain, so it owns these pairs; the
3025// lower-ranked chains reach them by forwarding up. The polyline-vs-polygon clip
3026// itself lives on Polyline (reusing its coalescing), so both overloads delegate
3027// there -- a monotone chain first views itself as a polyline.
3028template <class PointType, class LabelType>
3029template <class ResultNumber, PolylineConcept OtherPolyline>
3030constexpr auto Polygon<PointType, LabelType>::intersection(const OtherPolyline& other) const {
3031 return other.template polygonIntersection<ResultNumber>(*this);
3032}
3033
3034template <class PointType, class LabelType>
3035template <class ResultNumber, MonotoneChainConcept OtherChain>
3036constexpr auto Polygon<PointType, LabelType>::intersection(const OtherChain& other) const {
3037 return other.asPolyline().template polygonIntersection<ResultNumber>(*this);
3038}
3039
3040
3041// ---------------------------------------------------------------------------
3042// PolygonWithHoles
3043//
3044// The one-dimensional operands, which the region-valued booleans of
3045// implementation/booleans.hpp leave alone: `closure(A° ∩ B°)` is empty for every
3046// one of them, so they need the plain, unregularized intersection instead. Each
3047// clip runs over the boundary edges of every ring at once -- see the helpers
3048// above -- which is all that separates these from Polygon's.
3049
3050template <class PointType_, class TLabel>
3051template <class ResultNumber, PointConcept OtherPoint>
3052constexpr std::optional<Point<ResultNumber, typename PointType_::LabelType>>
3054 if (contains(other)) {
3056 }
3057 return {};
3058}
3059
3060template <class PointType_, class TLabel>
3061template <class ResultNumber, SegmentConcept OtherSegment>
3062constexpr std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
3066 return detail::areaSegmentIntersection<ResultPoint>(*this, orientedEdges(), other);
3067}
3068
3069template <class PointType_, class TLabel>
3070template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
3071constexpr std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
3073PolygonWithHoles<PointType_, TLabel>::intersection(const OtherOrientedSegment& other) const {
3076}
3077
3078template <class PointType_, class TLabel>
3079template <class ResultNumber, LineConcept OtherLine>
3080constexpr std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
3084 return detail::areaLineIntersection<ResultPoint>(*this, orientedEdges(), other);
3085}
3086
3087template <class PointType_, class TLabel>
3088template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
3089constexpr std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
3091PolygonWithHoles<PointType_, TLabel>::intersection(const OtherOrientedLine& other) const {
3092 return intersection<ResultNumber>(other.asLine());
3093}
3094
3095template <class PointType_, class TLabel>
3096template <class ResultNumber, RayConcept OtherRay>
3097constexpr std::vector<std::variant<Point<ResultNumber, typename PointType_::LabelType>,
3101 return detail::areaRayIntersection<ResultPoint>(*this, orientedEdges(), other);
3102}
3103
3104// A region outranks Polyline and MonotoneChain, so it owns these pairs; as with
3105// Polygon, the clip itself lives on Polyline (reusing its coalescing) and a
3106// monotone chain first views itself as a polyline.
3107template <class PointType_, class TLabel>
3108template <class ResultNumber, PolylineConcept OtherPolyline>
3109constexpr auto PolygonWithHoles<PointType_, TLabel>::intersection(const OtherPolyline& other) const {
3110 return other.template polygonIntersection<ResultNumber>(*this);
3111}
3112
3113template <class PointType_, class TLabel>
3114template <class ResultNumber, MonotoneChainConcept OtherChain>
3115constexpr auto PolygonWithHoles<PointType_, TLabel>::intersection(const OtherChain& other) const {
3116 return other.asPolyline().template polygonIntersection<ResultNumber>(*this);
3117}
3118
3119
3120// ---------------------------------------------------------------------------
3121// HalfplaneIntersection
3122
3123template <class PointType, class LabelType>
3124template <class ResultNumber, PointConcept OtherPoint>
3125constexpr std::optional<Point<ResultNumber, typename PointType::LabelType>>
3127 if (contains(other)) {
3129 }
3130 return {};
3131}
3132
3133template <class PointType, class LabelType>
3134template <class ResultNumber, LineConcept OtherLine>
3135constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3141 if (empty()) {
3142 return {};
3143 }
3144 if (other.isDegenerate()) {
3145 if (contains(other[0])) {
3146 return ResultPoint(other[0]);
3147 }
3148 return {};
3149 }
3150 if (halfplanes_.empty()) {
3151 return Line<ResultPoint>(ResultPoint(other[0]), ResultPoint(other[1]));
3152 }
3153 const Halfplane<typename OtherLine::PointType> along(other[0], other[1]);
3154 const auto clip = clipLine(along);
3155 if (clip.empty) {
3156 return {};
3157 }
3158 const Line<typename OtherLine::PointType> supporting(other[0], other[1]);
3159 const auto crossing = [&](std::ptrdiff_t idx) {
3160 const auto isec = halfplanes_[static_cast<std::size_t>(idx)].asLine()
3161 .template intersection<ResultNumber>(supporting);
3162 assert(isec && isec->index() == 0);
3163 return std::get<0>(*isec);
3164 };
3165 if (clip.entry < 0 && clip.exit < 0) {
3166 return Line<ResultPoint>(ResultPoint(other[0]), ResultPoint(other[1]));
3167 }
3168 const ResultPoint source(other[0]);
3169 const ResultPoint target(other[1]);
3170 const ResultNumber dx = target.x() - source.x();
3171 const ResultNumber dy = target.y() - source.y();
3172 if (clip.entry < 0) {
3173 const ResultPoint finish = crossing(clip.exit);
3174 return Ray<ResultPoint>(finish, ResultPoint(finish.x() - dx, finish.y() - dy));
3175 }
3176 if (clip.exit < 0) {
3177 const ResultPoint start = crossing(clip.entry);
3178 return Ray<ResultPoint>(start, ResultPoint(start.x() + dx, start.y() + dy));
3179 }
3180 const auto det = detail::boundaryLinesDeterminant(
3181 halfplanes_[static_cast<std::size_t>(clip.entry)],
3182 halfplanes_[static_cast<std::size_t>(clip.exit)], along);
3183 if (det == decltype(det){}) {
3184 return crossing(clip.entry);
3185 }
3186 return Segment<ResultPoint>(crossing(clip.entry), crossing(clip.exit));
3187}
3188
3189template <class PointType, class LabelType>
3190template <class ResultNumber, OrientedLineConcept OtherOrientedLine>
3191constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3196 return intersection<ResultNumber>(other.asLine());
3197}
3198
3199template <class PointType, class LabelType>
3200template <class ResultNumber, SegmentConcept OtherSegment>
3201constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3205 if (empty()) {
3206 return {};
3207 }
3208 if (other.isDegenerate()) {
3209 if (contains(other.min())) {
3210 return ResultPoint(other.min());
3211 }
3212 return {};
3213 }
3214 if (halfplanes_.empty()) {
3215 return Segment<ResultPoint>(ResultPoint(other.min()), ResultPoint(other.max()));
3216 }
3217 const Halfplane<typename OtherSegment::PointType> along(other.min(), other.max());
3218 const auto clip = clipLine(along);
3219 if (clip.empty) {
3220 return {};
3221 }
3222 const Line<typename OtherSegment::PointType> supporting(other.min(), other.max());
3223 const auto crossing = [&](std::ptrdiff_t idx) {
3224 const auto isec = halfplanes_[static_cast<std::size_t>(idx)].asLine()
3225 .template intersection<ResultNumber>(supporting);
3226 assert(isec && isec->index() == 0);
3227 return std::get<0>(*isec);
3228 };
3229 // Clamp the clip interval to the segment's [0, 1] parameter window; the
3230 // comparisons against the window edges are point-side tests.
3231 const bool startsAtMin = clip.entry < 0 ||
3232 constraintSide(static_cast<std::size_t>(clip.entry), other.min()) >= 0;
3233 if (!startsAtMin && constraintSide(static_cast<std::size_t>(clip.entry), other.max()) < 0) {
3234 return {}; // the region begins after the segment ends
3235 }
3236 const bool endsAtMax = clip.exit < 0 ||
3237 constraintSide(static_cast<std::size_t>(clip.exit), other.max()) >= 0;
3238 if (!endsAtMax && constraintSide(static_cast<std::size_t>(clip.exit), other.min()) < 0) {
3239 return {}; // the region ends before the segment starts
3240 }
3241 if (startsAtMin && endsAtMax) {
3242 return Segment<ResultPoint>(ResultPoint(other.min()), ResultPoint(other.max()));
3243 }
3244 if (startsAtMin) {
3245 if (constraintSide(static_cast<std::size_t>(clip.exit), other.min()) == 0) {
3246 return ResultPoint(other.min());
3247 }
3248 return Segment<ResultPoint>(ResultPoint(other.min()), crossing(clip.exit));
3249 }
3250 if (endsAtMax) {
3251 if (constraintSide(static_cast<std::size_t>(clip.entry), other.max()) == 0) {
3252 return ResultPoint(other.max());
3253 }
3254 return Segment<ResultPoint>(crossing(clip.entry), ResultPoint(other.max()));
3255 }
3256 const auto det = detail::boundaryLinesDeterminant(
3257 halfplanes_[static_cast<std::size_t>(clip.entry)],
3258 halfplanes_[static_cast<std::size_t>(clip.exit)], along);
3259 if (det == decltype(det){}) {
3260 return crossing(clip.entry);
3261 }
3262 return Segment<ResultPoint>(crossing(clip.entry), crossing(clip.exit));
3263}
3264
3265template <class PointType, class LabelType>
3266template <class ResultNumber, OrientedSegmentConcept OtherOrientedSegment>
3267constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3272
3273template <class PointType, class LabelType>
3274template <class ResultNumber, RayConcept OtherRay>
3275constexpr std::optional<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3280 if (empty()) {
3281 return {};
3282 }
3283 if (halfplanes_.empty()) {
3284 return Ray<ResultPoint>(ResultPoint(other.source()), ResultPoint(other.target()));
3285 }
3286 const Halfplane<typename OtherRay::PointType> along(other.source(), other.target());
3287 const auto clip = clipLine(along);
3288 if (clip.empty) {
3289 return {};
3290 }
3291 const Line<typename OtherRay::PointType> supporting(other.source(), other.target());
3292 const auto crossing = [&](std::ptrdiff_t idx) {
3293 const auto isec = halfplanes_[static_cast<std::size_t>(idx)].asLine()
3294 .template intersection<ResultNumber>(supporting);
3295 assert(isec && isec->index() == 0);
3296 return std::get<0>(*isec);
3297 };
3298 // Clamp the clip interval to the ray's [0, +inf) parameter window.
3299 const bool startsAtSource = clip.entry < 0 ||
3300 constraintSide(static_cast<std::size_t>(clip.entry), other.source()) >= 0;
3301 if (clip.exit < 0) {
3302 if (startsAtSource) {
3303 return Ray<ResultPoint>(ResultPoint(other.source()), ResultPoint(other.target()));
3304 }
3305 const ResultPoint start = crossing(clip.entry);
3306 const ResultPoint source(other.source());
3307 const ResultPoint target(other.target());
3308 return Ray<ResultPoint>(start, ResultPoint(start.x() + (target.x() - source.x()),
3309 start.y() + (target.y() - source.y())));
3310 }
3311 const auto exitSide = constraintSide(static_cast<std::size_t>(clip.exit), other.source());
3312 if (exitSide < 0) {
3313 return {}; // the region ends before the ray starts
3314 }
3315 if (startsAtSource) {
3316 if (exitSide == 0) {
3317 return ResultPoint(other.source());
3318 }
3319 return Segment<ResultPoint>(ResultPoint(other.source()), crossing(clip.exit));
3320 }
3321 const auto det = detail::boundaryLinesDeterminant(
3322 halfplanes_[static_cast<std::size_t>(clip.entry)],
3323 halfplanes_[static_cast<std::size_t>(clip.exit)], along);
3324 if (det == decltype(det){}) {
3325 return crossing(clip.entry);
3326 }
3327 return Segment<ResultPoint>(crossing(clip.entry), crossing(clip.exit));
3328}
3329
3330template <class PointType, class LabelType>
3331template <class ResultNumber, HalfplaneConcept OtherHalfplane>
3334 // Half-plane intersections are closed under intersecting with one more
3335 // half-plane, and no coordinate division is involved, so the result is
3336 // exact whenever ResultNumber represents the inputs exactly.
3338 result.insert(other);
3339 return result;
3340}
3341
3342template <class PointType, class LabelType>
3343template <class ResultNumber, RectangleConcept OtherRectangle>
3346 // Intersecting with the rectangle's four edge half-planes stays closed and
3347 // needs no division, so the result is exact whenever ResultNumber
3348 // represents the inputs exactly.
3350 using ResultHalfplane = typename HalfplaneIntersection<ResultPoint>::HalfplaneType;
3352 if (other.empty()) {
3353 // The empty rectangle carries no edge, so the loop below would insert no
3354 // constraint and hand back this whole region. Force emptiness with two
3355 // contradictory parallel constraints ({x <= 0} and {x >= 1}), as the
3356 // Convex and half-plane-intersection overloads do for their own empty
3357 // operand.
3358 result.insert(ResultHalfplane(ResultPoint(0, 0), ResultPoint(0, 1)));
3359 result.insert(ResultHalfplane(ResultPoint(1, 1), ResultPoint(1, 0)));
3360 return result;
3361 }
3362 for (const auto& halfplane : HalfplaneIntersection<ResultPoint>(other)) {
3363 result.insert(halfplane);
3364 }
3365 return result;
3366}
3367
3368template <class PointType, class LabelType>
3369template <class ResultNumber, TriangleConcept OtherTriangle>
3374 for (const auto& halfplane : HalfplaneIntersection<ResultPoint>(other)) {
3375 result.insert(halfplane);
3376 }
3377 return result;
3378}
3379
3380template <class PointType, class LabelType>
3381template <class ResultNumber, ConvexConcept OtherConvex>
3385 using ResultHalfplane = typename HalfplaneIntersection<ResultPoint>::HalfplaneType;
3387 if (other.size() == 0) {
3388 // The empty convex polygon is the empty set: force emptiness with two
3389 // contradictory parallel constraints ({x <= 0} and {x >= 1}).
3390 result.insert(ResultHalfplane(ResultPoint(0, 0), ResultPoint(0, 1)));
3391 result.insert(ResultHalfplane(ResultPoint(1, 1), ResultPoint(1, 0)));
3392 return result;
3393 }
3394 for (const auto& halfplane : HalfplaneIntersection<ResultPoint>(other)) {
3395 result.insert(halfplane);
3396 }
3397 return result;
3398}
3399
3400template <class PointType, class LabelType>
3401template <class ResultNumber, HalfplaneIntersectionConcept OtherRegion>
3404 // Half-plane intersections are closed under intersection and no division
3405 // is involved, so the result is exact whenever ResultNumber represents
3406 // the inputs exactly.
3408 using ResultHalfplane = typename HalfplaneIntersection<ResultPoint>::HalfplaneType;
3410 if (other.empty()) {
3411 // Force emptiness with two contradictory parallel constraints
3412 // ({x <= 0} and {x >= 1}).
3413 result.insert(ResultHalfplane(ResultPoint(0, 0), ResultPoint(0, 1)));
3414 result.insert(ResultHalfplane(ResultPoint(1, 1), ResultPoint(1, 0)));
3415 return result;
3416 }
3417 for (const auto& halfplane : other) {
3418 result.insert(ResultHalfplane(halfplane));
3419 }
3420 return result;
3421}
3422
3423// A polygon is bounded, so only the part of the region near it matters: the
3424// region clipped to the polygon's own bounding rectangle has the same
3425// intersection with it — P ⊆ bbox(P), so A ∩ P = (A ∩ bbox) ∩ P — and, being
3426// bounded and convex, is a convex polygon, which Polygon::intersection already
3427// handles. That is where all the work happens; everything here is the
3428// reduction to it.
3429//
3430// The clip is done over exact rationals, and against bbox(P) itself rather
3431// than an inflated box: an inflated one would move the clip vertices off the
3432// crossings the answer already carries and deepen them for nothing, which for
3433// an inexact ResultNumber is the difference between an answer that closes up
3434// and one that does not.
3435template <class PointType, class LabelType>
3436template <class ResultNumber, PolygonConcept OtherPolygon>
3437std::vector<std::variant<Point<ResultNumber, typename PointType::LabelType>,
3442 using ResultSegment = Segment<ResultPoint>;
3443 using ResultPolyline = Polyline<ResultPoint>;
3444 using ResultPolygon = Polygon<ResultPoint>;
3445 using Piece = std::variant<ResultPoint, ResultPolyline, ResultPolygon>;
3446 using ExactNumber = detail::region_exact_number_t<NumberType>;
3448 using ExactRegion = HalfplaneIntersection<ExactPoint>;
3449
3450 std::vector<Piece> result;
3451 if (empty() || other.size() == 0) {
3452 return result;
3453 }
3454 // The polygon is relabelled into the region's label type so the components
3455 // come back carrying it, as every other overload here does.
3456 const Polygon<ExactPoint> exact(other);
3457
3458 // A region with empty interior is its carrier — a point, a segment, a ray
3459 // or a line — and the polygon clips those itself. Their pieces are points
3460 // and segments, and a segment is the two-vertex polyline of this contract.
3461 const auto carrierPieces = [&exact, &result](const auto& region) {
3462 std::visit(
3463 [&exact, &result](const auto& carrier) {
3464 using Carrier = std::remove_cvref_t<decltype(carrier)>;
3465 if constexpr (PointConcept<Carrier>) {
3466 if (exact.contains(carrier)) {
3467 result.emplace_back(ResultPoint(carrier));
3468 }
3469 } else {
3470 for (const auto& piece : exact.template intersection<ResultNumber>(carrier)) {
3471 if (const auto* point = std::get_if<ResultPoint>(&piece)) {
3472 result.emplace_back(*point);
3473 } else {
3474 const ResultSegment& chord = std::get<ResultSegment>(piece);
3475 result.emplace_back(
3476 ResultPolyline(std::vector<ResultPoint>{chord.min(), chord.max()}));
3477 }
3478 }
3479 }
3480 },
3481 detail::degenerateRegionCarrier(region));
3482 };
3483
3484 if (isDegenerate()) {
3485 carrierPieces(*this); // unclipped: the polygon handles a ray or a line itself
3486 return result;
3487 }
3488
3489 ExactRegion clipped(*this);
3490 for (const auto& halfplane : ExactRegion(exact.bbox())) {
3491 clipped.insert(halfplane);
3492 }
3493 if (clipped.empty()) {
3494 return result; // the region misses the polygon's bounding rectangle
3495 }
3496 // A full-dimensional region can still meet the bounding rectangle in a
3497 // segment or a point, and then so does everything below it.
3498 if (clipped.isDegenerate()) {
3499 carrierPieces(clipped);
3500 return result;
3501 }
3502 return exact.template intersection<ResultNumber>(clipped.template asConvex<ExactNumber>());
3503}
3504
3505} // namespace pgl
Coordinate-evaluation helpers for linear primitives.
Definition forward.hpp:306
Definition forward.hpp:316
Definition forward.hpp:317
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
@ edge
Definition bitmatrix.hpp:37
Line() -> Line< Point<>, NoLabel >
Point() -> Point< int >
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 bool collinear(const Point< ANumber, ALabel > &a, const Point< BNumber, BLabel > &b, const Point< CNumber, CLabel > &c)
Tests whether three points are collinear.
Definition orientation.hpp:651
Segment() -> Segment< Point<>, NoLabel >
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
Ray() -> Ray< Point<>, NoLabel >
Closed convex polygon stored by its vertices.
Definition convex.hpp:170
constexpr bool intersects(const OtherSegment &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:716
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 convex.hpp:289
constexpr std::ptrdiff_t index(const PointType &point) const
Returns the smallest index i with (*this)[i] == point, or -1 if point is not a vertex.
Definition predicates.hpp:1085
constexpr auto intersection(const Shape< OtherPoint > &other) const
Returns the intersection of the two shapes (A ∩ B), re-dispatching through the wrapper's own intersec...
Definition convex.hpp:2496
constexpr std::vector< Segment< PointType > > edges() const
Returns the edges of the convex polygon.
Definition convex.hpp:529
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1135
constexpr Convex()=default
Creates a convex with no vertex.
size_t size() const
Returns the number of vertices in the convex polygon.
Definition convex.hpp:840
PointType_ PointType
Definition convex.hpp:171
constexpr auto intersection(const Shape< OtherPoint > &other) const
Returns the intersection of the two shapes (A ∩ B), re-dispatching through the wrapper's own intersec...
Definition disk.hpp:787
constexpr bool contains(const OtherPoint &other) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:1015
Intersection of closed half-planes; convex but possibly unbounded or empty.
Definition halfplaneintersection.hpp:244
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:2511
Halfplane< PointType > HalfplaneType
Definition halfplaneintersection.hpp:248
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 bool isDegenerate() const
Returns whether the region has empty interior (it is empty or lower-dimensional: a line,...
Definition halfplaneintersection.hpp:664
constexpr Convex< Point< ResultNumber, typename PointType::LabelType > > asConvex() const
Returns the region as a convex polygon.
Definition halfplaneintersection.hpp:955
friend struct HalfplaneIntersection
Definition halfplaneintersection.hpp:2308
constexpr bool insert(const OtherHalfplane &other)
Intersects the region with one more half-plane.
Definition halfplaneintersection.hpp:509
Closed half-plane defined by an oriented boundary line.
Definition halfplane.hpp:51
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:524
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 Line()=default
Creates the degenerate line (0,0)--(0,0).
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:203
constexpr bool isDegenerate() const
Returns whether the defining points coincide.
Definition predicates.hpp:451
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 std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:2566
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:280
constexpr Line< PointType > asLine() const
Returns the line without orientation.
Definition orientedline.hpp:321
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:180
Two-dimensional point with optional label payload.
Definition point.hpp:129
constexpr std::optional< Point< ResultNumber, LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
constexpr bool contains(const OtherPoint &other) const
constexpr std::vector< OrientedSegment< PointType > > orientedEdges() const
Definition polygonwithholes.hpp:364
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
constexpr bool contains(const OtherPoint &point) const
Closed simple polygon stored by its vertices.
Definition polygon.hpp:59
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 orientedEdgesView() const
Lazy view counterpart of orientedEdges(); see edgesView().
Definition polygon.hpp:790
constexpr Polygon()=default
Creates a polygon with no vertex.
constexpr auto intersection(const Shape< OtherPoint > &other) const
Returns the intersection of the two shapes (A ∩ B), re-dispatching through the wrapper's own intersec...
Definition polygon.hpp:2510
constexpr std::size_t size() const
Returns the number of vertices in the polygon.
Definition polygon.hpp:259
constexpr ResultNumber twiceArea() const
Computes twice the (unsigned) area of the polygon via the shoelace formula.
Definition polygon.hpp:273
constexpr std::vector< PointType > vertices() const
Returns the vertices of the polygon (translation applied).
Definition polygon.hpp:587
constexpr std::vector< Segment< PointType > > edges() const
Returns the edges of the polygon.
Definition polygon.hpp:598
Open polygonal chain stored in traversal order; may self-intersect.
Definition polyline.hpp:69
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the polyline.
Definition bounding.hpp:515
constexpr std::vector< std::variant< Point< ResultNumber, typename PointType::LabelType >, Segment< Point< ResultNumber, typename PointType::LabelType > > > > polygonIntersection(const OtherArea &other) const
Returns the intersection with a polygon or a region (A ∩ B), a sequence of points and segments sorted...
Definition intersection.hpp:2987
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:2690
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
Half-infinite line starting from one source point plus optional ray label.
Definition ray.hpp:51
constexpr Ray()=default
Creates the degenerate ray (0,0)--(0,0)->.
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:323
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 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< 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 Rectangle()
Creates the empty rectangle [(0,0),(-1,-1)].
Definition rectangle.hpp:120
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:728
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:998
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 std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:100
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
constexpr Segment()=default
Creates the degenerate segment (0,0)--(0,0).
constexpr bool contains(const OtherPoint &point) const
Tests whether this shape contains the other shape (A ⊇ B).
Definition contains.hpp:223
constexpr Convex< PointType > asConvex() const
Returns the triangle as a convex polygon.
Definition triangle.hpp:490
constexpr std::optional< Point< ResultNumber, typename PointType::LabelType > > intersection(const OtherPoint &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
Definition intersection.hpp:1170