20#include <unordered_set>
41template <
class Coefficient,
class Part,
class Fraction>
42struct sweepHeightNumber {
43 using type = promoted_number_t<std::common_type_t<Coefficient, Part>>;
46template <
class Int,
class Part,
class Fraction>
47struct sweepHeightNumber<pgl::
Rational<Int>, Part, Fraction> {
48 using type = Fraction;
51template <
class Coefficient,
class Part,
class Fraction>
52using sweepHeightNumber_t =
typename sweepHeightNumber<Coefficient, Part, Fraction>::type;
54template <
class Rational, SegmentConcept Segment>
58 static_assert(!std::is_floating_point_v<Number>,
59 "Bentley-Ottmann requires exact (non-floating-point) input "
60 "coordinates; the sweep line's predicates are not robust under "
61 "rounding. Use integer or rational coordinates.");
62 using Rectangle = pgl::Rectangle<Point>;
67 using RPoint = pgl::Point<Rational, typename Point::LabelType>;
68 using RSegment = pgl::Segment<RPoint>;
69 using CrossingPair = std::array<Segment,2>;
77 using Coordinate = pgl::detail::promoted_number_t<Number>;
78 using Wide = pgl::detail::promoted_number_t<Coordinate>;
79 using Exact = pgl::detail::sweepHeightNumber_t<Wide, Integer, Rational>;
81 enum class EventEnum {
82 RIGHT, CROSS, VERTICAL, LEFT
92 pgl::detail::Approximate approx;
97 : x(std::move(x_)), approx(pgl::detail::approximate(x)),
98 type(type_), s1(std::move(s1_)) {}
100 auto operator<(
const Event &other)
const {
101 const std::partial_ordering filtered =
102 pgl::detail::approximateSign(other.approx - approx);
103 if (filtered != std::partial_ordering::unordered) {
109 friend std::ostream &operator<<(std::ostream &out,
const Event &e) {
110 return out << e.type <<
"@" << e.x <<
": " << e.s1;
114 std::priority_queue<Event> queue;
135 pgl::detail::Approximate approx{};
147 const BentleyOttmann *sweep;
149 return sweep->CompareAlongLine(a, b);
152 using Tree = std::set<Segment, AlongLine>;
156 std::array<std::vector<Event>, 4> events;
169 typename Tree::iterator node;
170 std::vector<Segment> segments;
178 struct CrossingPairHash {
179 std::size_t operator()(
const CrossingPair &pair)
const {
180 std::size_t seed = 0;
181 pgl::detail::hashCombine(seed, pair[0]);
182 pgl::detail::hashCombine(seed, pair[1]);
186 using CrossingPairSet = std::unordered_set<CrossingPair, CrossingPairHash>;
187 CrossingPairSet crossingsSet, intersectionSet;
190 static std::vector<CrossingPair> sorted(
const CrossingPairSet &pairs) {
191 std::vector<CrossingPair> ordered(pairs.begin(), pairs.end());
192 std::sort(ordered.begin(), ordered.end());
196 std::function<bool(
const CrossingPair&)> onCrossing = [](
const CrossingPair&){
return false;},
197 onIntersection = [](
const CrossingPair&){
return false;};
198 bool onlyCrossings =
true;
199 bool stopNow =
false;
201 bool addCrossing(
const CrossingPair &p) {
202 crossingsSet.insert(p);
211 bool addIntersection(
const CrossingPair &p) {
212 intersectionSet.insert(p);
213 if (onIntersection(p)) {
221 void initQueue(
const std::vector<Segment> &segments) {
222 for (
const Segment &s :segments) {
224 queue.emplace(
static_cast<Rational>(s.min().x()), EventEnum::VERTICAL, s);
227 queue.emplace(
static_cast<Rational>(s.min().x()), EventEnum::LEFT, s);
232 void initBbox(
const std::vector<Segment> &segments) {
235 for (
const Segment &s :segments) {
239 bbox =
Rectangle(bbox.min().x()-1, bbox.min().y()-1, bbox.max().x()+1, bbox.max().y()+1);
273 using FilteredEnd =
decltype(pgl::detail::filtered<Coordinate>(std::declval<const Point&>()));
275 FilteredEnd aLo, aHi, bLo, bHi;
284 int heightSign(
const Segment &a,
const Segment &b,
const Abscissa &at)
const {
289 const Endpoints ends{pgl::detail::filtered<Coordinate>(a.min()),
290 pgl::detail::filtered<Coordinate>(a.max()),
291 pgl::detail::filtered<Coordinate>(b.min()),
292 pgl::detail::filtered<Coordinate>(b.max())};
298 const int atLeft = a.min().x() < b.min().x()
299 ? pgl::detail::signOf(
300 pgl::detail::orientationSignOf(ends.aLo, ends.aHi, ends.bLo).value())
301 : -pgl::detail::signOf(
302 pgl::detail::orientationSignOf(ends.bLo, ends.bHi, ends.aLo).value());
303 const int atRight = b.max().
x() < a.max().x()
304 ? pgl::detail::signOf(
305 pgl::detail::orientationSignOf(ends.aLo, ends.aHi, ends.bHi).value())
306 : -pgl::detail::signOf(
307 pgl::detail::orientationSignOf(ends.bLo, ends.bHi, ends.aHi).value());
309 if (atLeft == atRight) {
320 return at.x > std::max(a.min().x(), b.min().x()) ? atRight : 0;
323 return at.x < std::min(a.max().x(), b.max().x()) ? atLeft : 0;
325 return crossedHeightSign(a, b, at, ends);
332 bool sameHeight(
const Segment &a,
const Segment &b,
const Abscissa &at)
const {
333 return a == b || heightSign(a, b, at) == 0;
344 static std::pair<Wide, Wide> heightCoefficients(
const Segment &a,
const Segment &b) {
345 const Wide dax =
static_cast<Wide
>(a.max().
x()) -
static_cast<Wide
>(a.min().
x());
346 const Wide day =
static_cast<Wide
>(a.max().
y()) -
static_cast<Wide
>(a.min().
y());
347 const Wide dbx =
static_cast<Wide
>(b.max().
x()) -
static_cast<Wide
>(b.min().
x());
348 const Wide dby =
static_cast<Wide
>(b.max().
y()) -
static_cast<Wide
>(b.min().
y());
350 return {dax * dby - dbx * day,
351 dax * dbx * (
static_cast<Wide
>(b.min().
y()) -
static_cast<Wide
>(a.min().y())) -
352 dax * dby *
static_cast<Wide
>(b.min().
x()) +
353 dbx * day *
static_cast<Wide
>(a.min().
x())};
373 static std::partial_ordering approximateHeightSign(
const Endpoints &ends,
374 const Abscissa &at) {
375 const pgl::detail::ApproximatePoint aMin = pgl::detail::approximationOf(ends.aLo);
376 const pgl::detail::ApproximatePoint aMax = pgl::detail::approximationOf(ends.aHi);
377 const pgl::detail::ApproximatePoint bMin = pgl::detail::approximationOf(ends.bLo);
378 const pgl::detail::ApproximatePoint bMax = pgl::detail::approximationOf(ends.bHi);
379 const auto axMin = aMin.x;
380 const auto ayMin = aMin.y;
381 const auto bxMin = bMin.x;
382 const auto byMin = bMin.y;
383 const auto dax = aMax.x - axMin;
384 const auto day = aMax.y - ayMin;
385 const auto dbx = bMax.x - bxMin;
386 const auto dby = bMax.y - byMin;
388 const auto slope = dax * dby - dbx * day;
389 const auto offset = dax * dbx * (byMin - ayMin) - dax * dby * bxMin + dbx * day * axMin;
390 return pgl::detail::approximateSign(slope * at.approx + offset);
393 int crossedHeightSign(
const Segment &a,
const Segment &b,
const Abscissa &at,
394 const Endpoints &ends)
const {
395 if constexpr (pgl::detail::filtersSign<Wide>) {
404 const std::partial_ordering approximated = approximateHeightSign(ends, at);
405 if (approximated != std::partial_ordering::unordered) {
406 return pgl::detail::signOf(approximated);
410 const auto [slope, offset] = heightCoefficients(a, b);
412 if constexpr (pgl::detail::filtersSign<Exact>) {
422 const std::partial_ordering filtered = pgl::detail::approximateSign(
423 pgl::detail::approximate(slope) * at.approx +
424 pgl::detail::approximate(offset));
425 if (filtered != std::partial_ordering::unordered) {
426 return pgl::detail::signOf(filtered);
432 const Exact scaled =
static_cast<Exact
>(slope) *
static_cast<Exact
>(at.num) +
433 static_cast<Exact
>(offset) *
static_cast<Exact
>(at.den);
434 return scaled > Exact(0) ? 1 : (scaled < Exact(0) ? -1 : 0);
438 bool CompareAlongLine (
const Segment& a,
const Segment& b)
const {
444 if (a.isVertical()) {
445 if (b.isVertical()) {
446 return a.min().y() < b.min().y();
448 assert(line.x == a.min().x());
449 if (a.min().y() < std::min(b.min().y(),b.max().y()))
452 if (std::max(b.min().y(),b.max().y()) < a.min().y())
458 assert(line.x == b.min().x());
459 if (std::max(a.min().y(),a.max().y()) < b.min().y())
462 if (b.min().y() < std::min(a.min().y(),a.max().y()))
468 if (std::max(a.min().y(),a.max().y()) < std::min(b.min().y(),b.max().y()))
471 if (std::max(b.min().y(),b.max().y()) < std::min(a.min().y(),a.max().y()))
478 const int height = heightSign(a, b, line);
512 at.num = at.x.numerator();
513 at.den = at.x.denominator();
520 at.approx = pgl::detail::approximate(at.x);
525 tree = Tree(AlongLine{
this});
526 tree.emplace(bbox.edges()[0]);
527 tree.emplace(bbox.edges()[2]);
530 void printTree()
const {
531 std::cout <<
"Tree: ";
532 Segment previous = *tree.begin();
534 if (s != *tree.begin()) {
535 if (CompareAlongLine(previous,s)) {
537 }
else if (CompareAlongLine(s,previous)) {
538 std::cout <<
" _>_ ";
541 std::cout <<
" _=_ ";
547 std::cout << std::endl;
550 void printCrossings()
const {
551 std::cout <<
"Crossings: ";
552 for(
auto [sa,sb] : crossingsSet) {
553 auto p = std::get<0>(*sa.template intersection<Rational>(sb));
554 std::cout << sa <<
"crosses" << sb <<
" at " << p <<
"; ";
556 std::cout << std::endl;
559 void printQueue()
const {
560 std::cout <<
"Queue: ";
567 std::cout << std::endl;
575 void getEvents(
const Rational ¤tX) {
576 for (std::vector<Event> &bucket : events) {
580 events[
static_cast<std::size_t
>(queue.top().type)].push_back(queue.top());
582 }
while (!queue.empty() && queue.top().x == currentX);
585 void possibleCrossing(Tree::iterator ita, Tree::iterator itb) {
588 CrossingPair pair{sa,sb};
589 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
591 if (sa.crosses(sb) && !crossingsSet.contains(pair)) {
592 RPoint cross = std::get<RPoint>(*sa.template intersection<Rational>(sb));
593 if (cross.x() > line.x) {
595 queue.emplace(cross.x(), EventEnum::CROSS, sa);
601 void processRIGHT(
const std::vector<Event> &evts) {
602 for (Event ev : evts) {
603 auto it1 = tree.find(ev.s1);
611 assert(it1 != tree.end() &&
"RIGHT event for a segment not in the status");
612 if (it1 == tree.end()) {
617 auto it0 = it1; --it0;
618 auto it2 = it1; ++it2;
621 possibleCrossing(it0, it2);
626 void getNewCrossEvents(std::vector<Event> &crossEvents,
const Rational ¤tX) {
627 while (!queue.empty() && queue.top().x == currentX) {
628 crossEvents.push_back(queue.top());
640 auto findFirst(
const Segment &s,
const Abscissa &at) {
641 auto it = tree.find(s);
642 while (it != tree.begin() && sameHeight(*it, s, at)) {
657 std::vector<Run> getCrossingSegments(
const std::vector<Event> &evts,
const Abscissa &at) {
658 std::vector<Run> ret;
659 std::set<Segment> done;
661 for (
const Event &ev : evts) {
662 if (done.contains(ev.s1)) {
666 run.node = findFirst(ev.s1, at);
667 for (
auto it = run.node; it != tree.end() && sameHeight(*it, ev.s1, at); ++it) {
668 run.segments.push_back(*it);
671 if (!run.segments.empty()) {
672 ret.push_back(std::move(run));
679 void processCROSS(std::vector<Event> &evts,
const Rational ¤tX) {
681 getNewCrossEvents(evts, currentX);
687 const Abscissa crossing = abscissa(currentX);
689 std::vector<Run> crossingAt = getCrossingSegments(evts, crossing);
696 for (
const Run &run : crossingAt) {
698 for (std::size_t i = 0; i < run.segments.size(); ++i) {
707 for (Run &run : crossingAt) {
708 for (
const Segment &s : run.segments) {
709 run.node = tree.insert(s).first;
714 for (
const Run &run : crossingAt) {
720 while (it1 != tree.begin() &&
721 sameHeight(*std::prev(it1), run.segments.front(), line)) {
725 while (std::next(it2) != tree.end() &&
726 sameHeight(*std::next(it2), run.segments.front(), line)) {
730 auto it0 = it1; --it0;
731 auto it3 = it2; ++it3;
734 possibleCrossing(it0, it1);
735 possibleCrossing(it2, it3);
739 for (
const Run &run : crossingAt) {
740 const std::vector<Segment> &segs = run.segments;
741 for (
size_t i = 0; i+1 < segs.size(); i++) {
742 for (
size_t j = i+1; j < segs.size(); j++) {
743 CrossingPair pair{segs[i], segs[j]};
744 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
746 if (pair[0].crosses(pair[1])) {
747 if (addCrossing(pair)) {
756 void processRIGHT_interior(
const std::vector<Event> &evts) {
757 for (Event ev : evts) {
760 Segment sv(ev.s1.max().x(), ev.s1.max().y(), ev.s1.max().x(), ev.s1.max().y()+1);
761 auto it = tree.lower_bound(sv);
763 for (; it != tree.begin() && it->contains(ev.s1.max()); --it) {
765 if (it != tree.end()) {
769 for (; it != tree.end() && it->contains(ev.s1.max()); ++it) {
770 CrossingPair pair{ev.s1,*it};
771 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
772 if (addIntersection(pair)) {
779 void processVERTICAL(
const std::vector<Event> &evts) {
780 for (Event ev : evts) {
781 for (
auto it = tree.lower_bound(ev.s1); it != tree.end(); ++it) {
782 if (!ev.s1.intersects(*it))
785 if (ev.s1.crosses(*it)) {
786 CrossingPair pair{ev.s1, *it};
787 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
792 if (ev.s1.intersects(*it)) {
793 CrossingPair pair{ev.s1, *it};
794 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
802 void processVERTICAL_interior(
const std::vector<Event> &v_evts,
const std::vector<Event> &r_evts,
const std::vector<Event> &l_evts) {
803 std::vector<std::pair<Number, Segment>> order;
804 for (Event ev : l_evts) {
805 order.emplace_back(ev.s1.min().y(), ev.s1);
807 for (Event ev : r_evts) {
808 order.emplace_back(ev.s1.max().y(), ev.s1);
810 for (Event ev : v_evts) {
811 order.emplace_back(ev.s1.min().y(), ev.s1);
812 order.emplace_back(ev.s1.max().y(), ev.s1);
814 std::sort(order.begin(),order.end());
816 for (Event ev : v_evts) {
817 auto y1 = ev.s1.min().y();
818 auto y2 = ev.s1.max().y();
819 for (
auto it = std::lower_bound(order.begin(), order.end(), std::make_pair(y1,
Segment()));
820 it != order.end() && it->first < y2;
822 CrossingPair pair{ev.s1, it->second};
823 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
824 if (pair[0] != pair[1]) {
825 addIntersection(pair);
831 void processLEFT(
const std::vector<Event> &evts) {
832 for (Event ev : evts) {
833 auto [it1,_] = tree.insert(ev.s1);
834 auto it0 = it1; --it0;
835 auto it2 = it1; ++it2;
837 queue.emplace(
static_cast<Rational>(ev.s1.max().x()), EventEnum::RIGHT, ev.s1);
838 possibleCrossing(it0, it1);
839 possibleCrossing(it1, it2);
841 if (!onlyCrossings) {
842 while (it0->contains(ev.s1.min())) {
843 CrossingPair pair{*it0, ev.s1};
844 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
845 addIntersection(pair);
848 while (it2->contains(ev.s1.min())) {
849 CrossingPair pair{*it2, ev.s1};
850 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
851 addIntersection(pair);
862 void run(
const std::vector<Segment> &segments) {
864 if (segments.size() <= (
size_t) 1)
869 line = abscissa(
static_cast<Rational>(bbox.min().x()));
872 while (!queue.empty()) {
875 const Rational currentX = queue.top().x;
879 processRIGHT(events[(
size_t)EventEnum::RIGHT]);
887 processCROSS(events[1], currentX);
889 if (!onlyCrossings) {
890 processRIGHT_interior(events[(
size_t)EventEnum::RIGHT]);
896 processVERTICAL(events[(
size_t)EventEnum::VERTICAL]);
897 if (!onlyCrossings) {
898 processVERTICAL_interior(events[(
size_t)EventEnum::VERTICAL],
899 events[(
size_t)EventEnum::RIGHT],
900 events[(
size_t)EventEnum::LEFT]);
906 processLEFT(events[(
size_t)EventEnum::LEFT]);
914 std::vector<CrossingPair> findCrossings(
const std::vector<Segment> &segments) {
915 onlyCrossings =
true;
917 return sorted(crossingsSet);
920 std::vector<CrossingPair> findIntersections(
const std::vector<Segment> &segments) {
921 onlyCrossings =
false;
923 intersectionSet.insert(crossingsSet.begin(), crossingsSet.end());
926 std::map<Point,std::vector<Segment>> adjacent;
927 for (
const Segment &s : segments) {
928 adjacent[s.min()].push_back(s);
929 adjacent[s.max()].push_back(s);
931 for (
const auto &[_,segs] : adjacent) {
932 for (
size_t i = 0; i+1 < segs.size(); i++) {
933 for (
size_t j = i+1; j < segs.size(); j++) {
934 CrossingPair pair{segs[i], segs[j]};
935 if (pair[1] < pair[0]) std::swap(pair[0],pair[1]);
936 intersectionSet.insert(pair);
941 return sorted(intersectionSet);
944 bool detectCrossings(
const std::vector<Segment> &segments) {
945 onlyCrossings =
true;
946 onCrossing = [] (
const CrossingPair &) {
return true;};
948 return !crossingsSet.empty();
951 bool detectIntersections(
const std::vector<Segment> &segments) {
952 onlyCrossings =
false;
953 onCrossing = [] (
const CrossingPair &) {
return true;};
954 onIntersection = [] (
const CrossingPair &) {
return true;};
957 if (!crossingsSet.empty() || !intersectionSet.empty())
961 std::set<Point> adjacent;
962 for (
const Segment &s : segments) {
963 auto [_1,b1] = adjacent.insert(s.min());
966 auto [_2,b2] = adjacent.insert(s.max());
976 bool testPolygon(
const std::vector<Segment> &segments) {
977 onlyCrossings =
false;
978 bool notSimple =
false;
979 onCrossing = [¬Simple] (
const CrossingPair &) {notSimple =
true;
return true;};
981 size_t n = segments.size();
982 onIntersection = [n,&count, ¬Simple] (
const CrossingPair &p) {
983 if (p[0].
collinear(p[1]) && p[0].interiorsIntersect(p[1])) {
987 const int shared = (p[0].min() == p[1].min()) + (p[0].min() == p[1].max())
988 + (p[0].max() == p[1].min()) + (p[0].max() == p[1].max());
1007 bool testPolyLine(
const std::vector<Segment> &segments) {
1008 onlyCrossings =
false;
1009 bool notSimple =
false;
1010 onCrossing = [¬Simple] (
const CrossingPair &) {notSimple =
true;
return true;};
1012 size_t n = segments.size();
1013 onIntersection = [n,&count, ¬Simple] (
const CrossingPair &p) {
1014 if (p[0].
collinear(p[1]) && p[0].interiorsIntersect(p[1])) {
1018 const int shared = (p[0].min() == p[1].min()) + (p[0].min() == p[1].max())
1019 + (p[0].max() == p[1].min()) + (p[0].max() == p[1].max());
1025 if (count > 2*n - 2) {
1051template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1053 using Segment = Container::value_type;
1054 std::vector<Segment> v(segments.begin(),segments.end());
1056 pgl::detail::BentleyOttmann<Rational, Segment> bo;
1057 return bo.findIntersections(v);
1072template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1074 using Segment = Container::value_type;
1075 std::vector<Segment> v(segments.begin(),segments.end());
1077 pgl::detail::BentleyOttmann<Rational,Segment> bo;
1078 return bo.findCrossings(v);
1093template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1095 using Segment = Container::value_type;
1096 std::vector<Segment> v(segments.begin(),segments.end());
1098 pgl::detail::BentleyOttmann<Rational,Segment> bo;
1099 return bo.detectIntersections(v);
1113template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1115 using Segment = Container::value_type;
1116 std::vector<Segment> v(segments.begin(),segments.end());
1118 pgl::detail::BentleyOttmann<Rational,Segment> bo;
1119 return bo.detectCrossings(v);
1132template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1134 using Point = Container::value_type::PointType;
1135 std::vector<std::array<pgl::Segment<Point>,2>> ret;
1137 for (
auto it_i = segments.begin(); it_i != segments.end(); ++it_i) {
1138 for (
auto it_j = it_i; it_j != segments.end(); ++it_j) {
1145 ret.push_back({s1,s2});
1165template<
class Rational = pgl::Rational<pgl::BigInt>,
class Container>
1167 using Point = Container::value_type::PointType;
1168 std::vector<std::array<pgl::Segment<Point>,2>> ret;
1170 for (
auto it_i = segments.begin(); it_i != segments.end(); ++it_i) {
1171 for (
auto it_j = it_i; it_j != segments.end(); ++it_j) {
1178 ret.push_back({s1,s2});
1187template <
class Po
intType_,
class LabelType>
1188template <
class Rational>
1192 return holes_.empty();
1205 for (
const auto&
hole : holes_) {
1206 if (!outer_.contains(
hole)) {
1214 for (std::size_t i = 0; i < holes_.size(); ++i) {
1215 for (std::size_t j = i + 1; j < holes_.size(); ++j) {
1229template <
class Po
intType_,
class LabelType>
1241 return detail::regionSlits(*this).empty();
1246template <
class Po
intType_,
class LabelType>
1247template <
class Rational>
1250 for (
const auto&
component : components_) {
1255 for (std::size_t i = 0; i < components_.size(); ++i) {
1256 for (std::size_t j = i + 1; j < components_.size(); ++j) {
1272 for (
const auto& first : components_[i].
edges()) {
1273 for (
const auto& second : components_[j].
edges()) {
1275 if (shared && std::holds_alternative<ExactSegment>(*shared)) {
Simple undirected graph with hashable vertices.
Definition arrangement.hpp:67
@ y
Definition intervaltree.hpp:24
@ x
Definition intervaltree.hpp:24
Rectangle() -> Rectangle< Point<>, NoLabel >
Definition rectangle.hpp:2384
constexpr bool is_Rational_v
Definition rational.hpp:37
auto bruteForceIntersections(const Container &segments)
Finds all intersecting segment pairs by brute force.
Definition intersections.hpp:1166
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
Rational(T) -> Rational< T >
typename rational_int< T >::type rational_int_t
Definition rational.hpp:61
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
auto bruteForceCrossings(const Container &segments)
Finds all crossing segment pairs by brute force.
Definition intersections.hpp:1133
Segment() -> Segment< Point<>, NoLabel >
auto findCrossings(const Container &segments)
Finds all proper crossing segment pairs with Bentley-Ottmann.
Definition intersections.hpp:1073
auto findIntersections(const Container &segments)
Finds all intersecting segment pairs with Bentley-Ottmann.
Definition intersections.hpp:1052
bool detectCrossings(const Container &segments)
Detects whether any two segments properly cross.
Definition intersections.hpp:1114
bool detectIntersections(const Container &segments)
Detects whether any two segments intersect.
Definition intersections.hpp:1094
Two-dimensional point with optional label payload.
Definition point.hpp:129
TNumber NumberType
Definition point.hpp:131
constexpr std::vector< EdgeType > edges() const
Returns the boundary edges of every ring of every component.
Definition polygonset.hpp:420
std::vector< std::variant< Point< ResultNumber, typename PointType::LabelType >, Polyline< Point< ResultNumber, typename PointType::LabelType > >, PolygonWithHoles< Point< ResultNumber, typename PointType::LabelType > > > > intersection(const OtherShape &other) const
Returns the intersection of the two shapes (A ∩ B), empty when they are disjoint.
bool interiorsIntersect(const OtherShape &other) const
Tests whether the interiors of the shapes intersect (A° ∩ B° ≠ ∅).
Definition interiorsintersect.hpp:2764
bool intersects(const OtherShape &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:2234
constexpr const ComponentType & component(std::size_t index) const
Accesses a component by index.
Definition polygonset.hpp:271
bool isValid() const
Tests the structural contract: every component valid, component interiors pairwise disjoint,...
Definition intersections.hpp:1248
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the set.
Definition bounding.hpp:469
constexpr const Rectangle< PointType > & bbox() const
Computes the bounding box of the region.
Definition polygonwithholes.hpp:1571
constexpr bool interiorsIntersect(const OtherPoint &) const
Tests whether the interiors of the shapes intersect (A° ∩ B° ≠ ∅).
Definition polygonwithholes.hpp:1660
constexpr bool isDegenerate() const
Tests whether the region has zero area.
Definition polygonwithholes.hpp:441
constexpr const PolygonType & hole(std::size_t index) const
Accesses a hole by index.
Definition polygonwithholes.hpp:196
bool isRegular() const
Tests whether the region is the closure of its own interior (A = closure(A°)).
Definition intersections.hpp:1230
constexpr bool intersects(const OtherPoint &point) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition polygonwithholes.hpp:1649
bool isValid() const
Tests the structural contract: every ring simple, every hole inside the outer boundary,...
Definition intersections.hpp:1189
constexpr bool empty() const
Tests whether the region has no outer boundary at all.
Definition polygonwithholes.hpp:430
bool isSimple() const
Tests whether every ring is simple.
Definition polygonwithholes.hpp:478
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58
constexpr bool crosses(const OtherSegment &other) const
Tests whether the two shapes mutually separate each other (each disconnects the other).
Definition crosses.hpp:38
constexpr bool intersects(const OtherPoint &other) const
Tests whether this shape and the other shape intersect (A ∩ B ≠ ∅).
Definition intersects.hpp:48
TPoint PointType
Definition segment.hpp:59