70template <
class Number>
84 bool upright()
const {
return lo.x() == hi.x(); }
85 SweepSegment segment()
const {
return SweepSegment(lo, hi); }
92 enum class Kind :
unsigned char {
101 std::size_t edge = 0;
102 Kind kind = Kind::Left;
104 bool operator<(
const Event& other)
const {
105 if (at < other.at)
return true;
106 if (other.at < at)
return false;
107 if (kind != other.kind)
return kind < other.kind;
108 return edge < other.edge;
132 const std::vector<Edge>* edges =
nullptr;
133 using is_transparent = void;
136 static int sideOf(
const Edge& one,
const Edge& that) {
142 return side > 0 ? -1 : (side < 0 ? 1 : 0);
145 int compare(std::size_t first, std::size_t second)
const {
146 if (first == second) {
149 const Edge& one = (*edges)[first];
150 const Edge& other = (*edges)[second];
152 one.lo.x() <= other.lo.x() ? sideOf(one, other) : -sideOf(other, one);
156 return first < second ? -1 : 1;
159 bool operator()(std::size_t first, std::size_t second)
const {
160 return compare(first, second) < 0;
165 bool operator()(std::size_t first,
const SweepPoint& probe)
const {
166 const Edge& one = (*edges)[first];
169 bool operator()(
const SweepPoint& probe, std::size_t second)
const {
170 const Edge& other = (*edges)[second];
175 std::vector<Edge> edges_;
176 std::vector<Event> events_;
177 std::set<std::size_t, Below> status_;
178 bool touching_ =
false;
182 Number xlo{}, ylo{}, xhi{}, yhi{};
185 void insert(
const SweepPoint& point) {
187 xlo = xhi = point.x();
188 ylo = yhi = point.y();
192 if (point.x() < xlo) xlo = point.x();
193 if (xhi < point.x()) xhi = point.x();
194 if (point.y() < ylo) ylo = point.y();
195 if (yhi < point.y()) yhi = point.y();
198 bool overlaps(
const SweepPoint& lo,
const SweepPoint& hi)
const {
199 return any && !(hi.x() < xlo) && !(xhi < lo.x()) &&
200 !(std::max(lo.y(), hi.y()) < ylo) && !(yhi < std::min(lo.y(), hi.y()));
204 template <
class Range>
205 void gather(
const Range& range,
bool blue, Extent& extent) {
206 for (
const auto&
edge : range) {
207 Edge stored{SweepPoint(
edge.min()), SweepPoint(
edge.max()), blue};
208 extent.insert(stored.lo);
209 extent.insert(stored.hi);
210 edges_.push_back(std::move(stored));
215 bool adjacent(std::size_t first, std::size_t second) {
216 const Edge& one = edges_[first];
217 const Edge& other = edges_[second];
218 if (one.blue == other.blue) {
221 const SweepSegment mine = one.segment();
222 const SweepSegment theirs = other.segment();
223 if (mine.crosses(theirs)) {
226 if (mine.intersects(theirs)) {
244 bool scanUpright(std::size_t index) {
245 const Edge& upright = edges_[index];
246 const SweepSegment bar = upright.segment();
247 for (
auto it = status_.lower_bound(upright.lo); it != status_.end(); ++it) {
248 const Edge& other = edges_[*it];
252 if (other.blue == upright.blue) {
255 if (other.segment().crosses(bar)) {
272 void uprightPairs(std::size_t begin, std::size_t end) {
273 std::vector<std::size_t> red;
274 std::vector<std::size_t> blue;
275 for (std::size_t k = begin; k < end; ++k) {
276 if (events_[k].kind != Kind::UprightLow) {
279 (edges_[events_[k].edge].blue ? blue : red).push_back(events_[k].
edge);
283 while (i < red.size() && j < blue.size()) {
284 const Edge& one = edges_[red[i]];
285 const Edge& other = edges_[blue[j]];
286 if (one.hi.y() < other.lo.y()) {
288 }
else if (other.hi.y() < one.lo.y()) {
298 bool batch(std::size_t begin, std::size_t end) {
305 for (std::size_t k = begin; k < end;) {
309 while (run < end && !(events_[k].at < events_[run].at)) {
310 (edges_[events_[run].edge].blue ? blue : red) =
true;
322 for (std::size_t k = begin; k < end; ++k) {
323 if (events_[k].kind == Kind::UprightLow && scanUpright(events_[k].
edge)) {
327 uprightPairs(begin, end);
329 for (std::size_t k = begin; k < end; ++k) {
330 if (events_[k].kind != Kind::Right) {
333 const auto it = status_.find(events_[k].
edge);
334 if (it == status_.end()) {
337 const bool hasBelow = it != status_.begin();
342 const auto above = std::next(it);
344 if (hasBelow && above != status_.end() && adjacent(*below, *above)) {
349 for (std::size_t k = begin; k < end; ++k) {
350 if (events_[k].kind != Kind::Left) {
353 const auto [it, fresh] = status_.insert(events_[k].
edge);
357 if (it != status_.begin() && adjacent(*std::prev(it), *it)) {
360 const auto above = std::next(it);
361 if (above != status_.end() && adjacent(*it, *above)) {
366 for (std::size_t k = begin; k < end; ++k) {
367 if (events_[k].kind == Kind::UprightLow && scanUpright(events_[k].
edge)) {
375 template <
class RedRange,
class BlueRange>
376 RedBlueSweeper(
const RedRange& red,
const BlueRange& blue) : status_(Below{&edges_}) {
379 gather(red,
false, redExtent);
380 const std::size_t split = edges_.size();
381 gather(blue,
true, blueExtent);
387 std::vector<Edge> kept;
388 kept.reserve(edges_.size());
389 for (std::size_t i = 0; i < edges_.size(); ++i) {
390 const Extent& against = i < split ? blueExtent : redExtent;
391 if (against.overlaps(edges_[i].lo, edges_[i].hi)) {
392 kept.push_back(edges_[i]);
395 edges_ = std::move(kept);
397 events_.reserve(2 * edges_.size());
398 for (std::size_t i = 0; i < edges_.size(); ++i) {
399 const Edge&
edge = edges_[i];
400 if (
edge.upright()) {
401 events_.push_back(Event{
edge.lo, i, Kind::UprightLow});
402 events_.push_back(Event{
edge.hi, i, Kind::Upper});
404 events_.push_back(Event{
edge.lo, i, Kind::Left});
405 events_.push_back(Event{
edge.hi, i, Kind::Right});
411 std::sort(events_.begin(), events_.end());
413 while (i < events_.size()) {
415 while (j < events_.size() && !(events_[i].at.x() < events_[j].at.x())) {
459template <
class RedRange,
class BlueRange>
461 using RedNumber =
typename std::ranges::range_value_t<RedRange>::PointType::NumberType;
462 using BlueNumber =
typename std::ranges::range_value_t<BlueRange>::PointType::NumberType;
463 using Number = std::common_type_t<RedNumber, BlueNumber>;
464 return detail::RedBlueSweeper<Number>(red, blue).run();
481template <
class RedRange,
class BlueRange>
493template <
class RedRange,
class BlueRange>
514template <
class RedRange,
class BlueRange>
560#ifndef PGL_BOUNDARY_STRATEGY
561#define PGL_BOUNDARY_STRATEGY 0
567template <
class Shape>
568constexpr std::size_t boundaryEdgeCount(
const Shape& shape) {
569 if constexpr (PolygonWithHolesConcept<Shape>) {
570 return shape.vertexCount();
619constexpr bool clearsEdgeFloor(std::size_t redEdges, std::size_t blueEdges) {
620 constexpr std::size_t kMinEdges = 128;
621 return redEdges >= kMinEdges && blueEdges >= kMinEdges;
647constexpr bool sweepBeatsChains(std::size_t redEdges, std::size_t redChains,
648 std::size_t blueEdges, std::size_t blueChains) {
649 const std::size_t edges = redEdges + blueEdges;
650 return 3 * redChains * blueChains > edges;
672template <
class RedShape,
class BlueShape>
674 [[maybe_unused]]
const BlueShape& blue) {
675#if PGL_BOUNDARY_STRATEGY == 1
677#elif PGL_BOUNDARY_STRATEGY == 2
680 const std::size_t redEdges = detail::boundaryEdgeCount(red);
681 const std::size_t blueEdges = detail::boundaryEdgeCount(blue);
685 if (!detail::clearsEdgeFloor(redEdges, blueEdges)) {
688 return detail::sweepBeatsChains(redEdges, red.chainCount(), blueEdges, blue.chainCount());
715template <PolygonConcept OuterPolygon, PolygonConcept InnerPolygon>
717 using InnerPoint =
typename InnerPolygon::PointType;
719 if (inner.size() == 0) {
722 if (!outer.bbox().contains(inner.bbox())) {
725 if (inner.size() == 1) {
726 return outer.contains(inner[0]);
728 if (
const auto vertex = inner.getIfPoint()) {
729 return outer.contains(*
vertex);
731 if (outer.isPoint()) {
735 if (!outer.contains(inner.get(0))) {
752 for (std::size_t i = 0; i < inner.size(); ++i) {
constexpr bool preferSweep(const RedShape &red, const BlueShape &blue)
Whether red against blue is a job for redBlueSweep rather than for a pairwise test of their monotone ...
Definition redbluesweep.hpp:673
Segment intersection and crossing algorithms.
Definition arrangement.hpp:67
bool sweepContains(const OuterPolygon &outer, const InnerPolygon &inner)
Sweep-based counterpart of Polygon::contains(Polygon).
Definition redbluesweep.hpp:716
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37
bool boundariesCross(const RedRange &red, const BlueRange &blue)
True exactly when some red edge properly crosses some blue edge.
Definition redbluesweep.hpp:482
bool boundariesMeet(const RedRange &red, const BlueRange &blue)
True exactly when some red edge meets some blue edge, crossing or not.
Definition redbluesweep.hpp:494
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
Shape(const std::variant< T, Ts... > &) -> Shape< detail::shape_point_type_t< T > >
BoundaryContact
How two edge sets meet, as classified by redBlueSweep.
Definition redbluesweep.hpp:55
@ Disjoint
No red edge meets any blue edge.
Definition redbluesweep.hpp:56
@ Touching
The two edge sets meet; no crossing pair was found.
Definition redbluesweep.hpp:57
@ Crossing
A red edge properly crosses a blue edge.
Definition redbluesweep.hpp:58
SweepContact boundaryContactBits(const RedRange &red, const BlueRange &blue)
Definition redbluesweep.hpp:515
BoundaryContact redBlueSweep(const RedRange &red, const BlueRange &blue)
Classifies how two edge sets meet, in one combined left-to-right sweep.
Definition redbluesweep.hpp:460
Two-dimensional point with optional label payload.
Definition point.hpp:129
Unoriented closed segment between two endpoints plus optional segment label.
Definition segment.hpp:58