177 class TrapezoidPointLocation;
187 using CellId = std::variant<VertexId, HalfedgeId, FaceId>;
206 using HalfedgeType = std::variant<OrientedSegmentType, OrientedLineType, RayType>;
208 using EdgeType = std::variant<SegmentType, LineType, RayType>;
212 static_assert(detail::is_point_v<PointType>,
"Arrangement requires pgl::Point vertices");
261 template <std::ranges::input_range ShapeRange>
263 std::vector<InputSegment> segments;
264 std::vector<InputCurve> curves;
265 std::vector<PointType> isolated;
266 collect(shapes, segments, curves, isolated);
267 build(segments, curves, isolated,
false);
272 template <std::ranges::input_range ShapeRange>
273 Arrangement(
const ShapeRange& shapes, detail::SimpleBoundariesTag) {
274 std::vector<InputSegment> segments;
275 std::vector<InputCurve> curves;
276 std::vector<PointType> isolated;
277 collect(shapes, segments, curves, isolated);
278 build(segments, curves, isolated,
true);
299 template <std::ranges::input_range ShapeRange, std::ranges::input_range Po
intRange>
301 std::vector<InputSegment> segments;
302 std::vector<InputCurve> curves;
303 std::vector<PointType> isolated;
304 collect(shapes, segments, curves, isolated);
305 for (
const auto& point : points) {
306 isolated.emplace_back(point);
308 build(segments, curves, isolated,
false);
316 return points_.size();
321 return origin_.size();
326 return origin_.size() / 2;
331 return outerCycle_.size();
341 [[nodiscard]]
const std::vector<PointType>&
vertices()
const {
347 std::vector<SegmentType> result;
349 for (std::size_t i = 0; i < edgeGeometry_.size(); ++i) {
350 const EdgeGeometry& geometry = edgeGeometry_[i];
351 if (geometry.kind != EdgeKind::segment) {
355 if constexpr (detail::has_label_v<TLabel>) {
356 edge.label() = edgeLabel_[i];
358 result.push_back(std::move(
edge));
369 [[nodiscard]] std::vector<EdgeType>
edges()
const {
370 std::vector<EdgeType> result;
372 for (std::size_t i = 0; i < edgeGeometry_.size(); ++i) {
373 const EdgeGeometry& geometry = edgeGeometry_[i];
374 if (geometry.kind == EdgeKind::segment) {
376 if constexpr (detail::has_label_v<TLabel>) {
377 edge.label() = edgeLabel_[i];
379 result.emplace_back(std::move(
edge));
380 }
else if (geometry.kind == EdgeKind::line) {
382 if constexpr (detail::has_label_v<TLabel>) {
383 edge.label() = edgeLabel_[i];
385 result.emplace_back(std::move(
edge));
388 if constexpr (detail::has_label_v<TLabel>) {
389 edge.label() = edgeLabel_[i];
391 result.emplace_back(std::move(
edge));
404 if (!v.valid() || v.index() >= points_.size()) {
405 throw std::logic_error(
"the fictitious arrangement vertex has no finite position");
407 return points_[v.index()];
420 assert(h.valid() && h.index() < origin_.size());
421 const EdgeGeometry& geometry = edgeGeometry_[h.index() / 2];
422 const TLabel&
label = edgeLabel_[h.index() / 2];
423 if (geometry.kind == EdgeKind::segment) {
425 points_[origin_[h.index() ^ 1]]);
426 if constexpr (detail::has_label_v<TLabel>) {
431 if (geometry.kind == EdgeKind::line) {
433 h.index() % 2 == 0 ? geometry.b : geometry.a);
434 if constexpr (detail::has_label_v<TLabel>) {
439 RayType ray(geometry.a, geometry.b);
440 if constexpr (detail::has_label_v<TLabel>) {
457 assert(h.valid() && h.index() < origin_.size());
467 assert(h.valid() && h.index() < next_.size());
477 assert(h.valid() && h.index() < origin_.size());
478 return VertexId(origin_[h.index()]);
496 assert(h.valid() && h.index() < face_.size());
497 return FaceId(face_[h.index()]);
512 assert(v.valid() && v.index() < outgoing_.size());
513 return outgoing_[v.index()];
527 assert(v.valid() && v.index() < outgoing_.size());
528 const HalfedgeId start = outgoing_[v.index()];
529 if (!start.valid()) {
532 std::size_t count = 0;
537 }
while (h != start);
551 assert(v.valid() && v.index() < outgoing_.size());
552 std::vector<HalfedgeId> halfedges;
553 const HalfedgeId start = outgoing_[v.index()];
554 if (!start.valid()) {
559 halfedges.push_back(h);
561 }
while (h != start);
590 for (std::uint32_t v = 0; v < topologicalVertexCount(); ++v) {
593 for (std::size_t h = 0; h < origin_.size(); h += 2) {
609 template <
class ResultNumber = NumberType>
622 template <
class ResultNumber = division_result_t<NumberType>>
624 assert(h.valid() && h.index() < origin_.size());
625 const EdgeGeometry& geometry = edgeGeometry_[h.index() / 2];
628 const ResultNumber two =
static_cast<ResultNumber
>(
NumberType(2));
630 (detail::asNumber<ResultNumber>(a.x()) + detail::asNumber<ResultNumber>(b.x())) / two,
631 (detail::asNumber<ResultNumber>(a.y()) + detail::asNumber<ResultNumber>(b.y())) / two);
662 template <
class ResultNumber = division_result_t<NumberType>>
666 return ringWitness<ResultNumber>(outerCycle_[f.index()]);
668 return sweptWitness<ResultNumber>(f);
687 assert(f.valid() && f.index() < outerCycle_.size());
688 if (!outerCycle_[f.index()].valid() || !
innerCycles(f).empty()) {
691 const std::uint32_t start = outerCycle_[f.index()].index();
692 std::uint32_t h = start;
694 if (face_[h ^ 1] == f.index()) {
698 }
while (h != start);
708 template <
class ResultNumber>
710 std::uint32_t leftmost = start.
index();
711 std::uint32_t beforeLeftmost = start.index();
712 std::uint32_t previous = start.index();
713 for (std::uint32_t h = next_[start.index()]; h != start.index();
714 previous = h, h = next_[h]) {
715 if (points_[origin_[h]] < points_[origin_[leftmost]]) {
717 beforeLeftmost = previous;
720 if (leftmost == start.index()) {
721 beforeLeftmost = previous;
723 const PointType& corner = points_[origin_[leftmost]];
724 const PointType& ahead = points_[origin_[next_[leftmost]]];
725 const PointType& behind = points_[origin_[beforeLeftmost]];
727 assert(!ear.isDegenerate());
731 for (std::uint32_t h = next_[next_[leftmost]]; h != leftmost; h = next_[h]) {
733 if (ear.interiorContains(
vertex) &&
734 (diagonal ==
nullptr ||
vertex < *diagonal)) {
738 if (diagonal !=
nullptr) {
742 return ear.template pointInside<ResultNumber>();
746 template <
class ResultNumber>
748 const HalfedgeId seed = outerCycle_[f.index()];
749 const PointType& a = points_[origin_[seed.index()]];
750 const PointType& b = points_[origin_[seed.index() ^ 1]];
763 const auto wide = [](
const NumberType& value) {
764 return static_cast<WideNumber
>(value);
766 const WideNumber midX = wide(a.x()) + wide(b.x());
767 const WideNumber midY = wide(a.y()) + wide(b.y());
768 const WideNumber normalX = wide(a.y()) - wide(b.y());
769 const WideNumber normalY = wide(b.x()) - wide(a.x());
770 const WideNumber two(2);
774 WideNumber hitNumerator(0);
775 WideNumber hitDenominator(0);
776 const auto offer = [&](WideNumber numerator, WideNumber denominator) {
777 if (denominator < WideNumber(0)) {
778 numerator = -numerator;
779 denominator = -denominator;
781 if (numerator <= WideNumber(0)) {
784 if (hitDenominator == WideNumber(0) ||
785 numerator * hitDenominator < hitNumerator * denominator) {
786 hitNumerator = numerator;
787 hitDenominator = denominator;
791 forEachBoundaryHalfedge(f, [&](
HalfedgeId h) {
792 if (h.index() / 2 == seed.index() / 2) {
795 const PointType& p = points_[origin_[h.index()]];
796 const PointType& q = points_[origin_[h.index() ^ 1]];
797 const WideNumber edgeX = wide(q.x()) - wide(p.x());
798 const WideNumber edgeY = wide(q.y()) - wide(p.y());
800 const WideNumber toEdgeX = two * wide(p.x()) - midX;
801 const WideNumber toEdgeY = two * wide(p.y()) - midY;
802 const WideNumber denominator = normalX * edgeY - normalY * edgeX;
803 const WideNumber alongEdge = toEdgeX * normalY - toEdgeY * normalX;
804 if (denominator != WideNumber(0)) {
808 WideNumber along = alongEdge;
809 WideNumber scale = two * denominator;
810 if (scale < WideNumber(0)) {
814 if (along < WideNumber(0) || along > scale) {
817 offer(toEdgeX * edgeY - toEdgeY * edgeX, two * denominator);
818 }
else if (alongEdge == WideNumber(0)) {
821 const WideNumber squaredNormal = normalX * normalX + normalY * normalY;
822 for (
const PointType& endpoint : {p, q}) {
823 offer((two * wide(endpoint.x()) - midX) * normalX +
824 (two * wide(endpoint.y()) - midY) * normalY,
825 two * squaredNormal);
831 assert(hitDenominator != WideNumber(0));
832 const ResultNumber scale =
static_cast<ResultNumber
>(two * hitDenominator);
834 static_cast<ResultNumber
>(midX * hitDenominator + hitNumerator * normalX) / scale,
835 static_cast<ResultNumber
>(midY * hitDenominator + hitNumerator * normalY) / scale);
850 return infinity_.valid();
864 assert(h.valid() && h.index() < origin_.size());
865 return infinity_.valid() &&
875 assert(f.valid() && f.index() < outerCycle_.size());
876 return unboundedFace_[f.index()];
881 assert(v.valid() && v.index() < topologicalVertexCount());
882 return infinity_.valid() && v == infinity_;
892 assert(f.valid() && f.index() < outerCycle_.size());
893 return outerCycle_[f.index()];
907 assert(f.valid() && f.index() + 1 < innerOffset_.size());
908 const std::size_t from = innerOffset_[f.index()];
909 const std::size_t to = innerOffset_[f.index() + 1];
910 return std::span<const HalfedgeId>(innerCycle_.data() + from, to - from);
925 assert(f.valid() && f.index() < outerCycle_.size());
926 std::vector<HalfedgeId> boundary;
927 const auto walkCycle = [&](
HalfedgeId start) {
930 boundary.push_back(h);
932 }
while (h != start);
934 if (outerCycle_[f.index()].valid()) {
935 walkCycle(outerCycle_[f.index()]);
952 assert(f.valid() && f.index() < outerCycle_.size());
953 std::vector<HalfedgeId> boundary;
954 const HalfedgeId start = outerCycle_[f.index()];
955 if (!start.valid()) {
960 boundary.push_back(h);
962 }
while (h != start);
976 assert(f.valid() && f.index() < outerCycle_.size());
977 std::vector<std::vector<HalfedgeId>> boundaries;
980 std::vector<HalfedgeId>& boundary = boundaries.emplace_back();
983 boundary.push_back(h);
985 }
while (h != start);
1004 template <
class ResultNumber = NumberType>
1006 if (!f.valid() || f.index() >= outerCycle_.size() ||
isUnbounded(f)) {
1007 throw std::logic_error(
1008 "Arrangement::polygonWithHoles is only defined for bounded faces");
1012 std::vector<ExactPolygon> rings;
1013 collectRings(cycleRing(outerCycle_[f.index()]), rings);
1014 assert(!rings.empty());
1018 const auto largest =
1019 std::max_element(rings.begin(), rings.end(),
1020 [](
const ExactPolygon& left,
const ExactPolygon& right) {
1021 return left.twiceArea() < right.twiceArea();
1023 if (largest != rings.begin()) {
1024 std::iter_swap(rings.begin(), largest);
1026 std::vector<ExactPolygon> holes(rings.begin() + 1, rings.end());
1028 collectRings(cycleRing(inner), holes);
1055 template <
class ResultNumber = NumberType>
1058 if (!f.valid() || f.index() >= outerCycle_.size()) {
1059 throw std::logic_error(
1060 "Arrangement::halfplaneIntersection requires a valid face");
1065 std::vector<ResultHalfplane> halfplanes;
1066 const auto appendCycle = [&](
HalfedgeId start) {
1070 const EdgeGeometry& geometry = edgeGeometry_[h.index() / 2];
1071 const bool forward = h.index() % 2 == 0;
1072 halfplanes.emplace_back(
1073 ResultPoint(forward ? geometry.a : geometry.b),
1074 ResultPoint(forward ? geometry.b : geometry.a));
1077 }
while (h != start);
1080 if (outerCycle_[f.index()].valid()) {
1081 appendCycle(outerCycle_[f.index()]);
1084 bool reachesInfinity =
false;
1087 reachesInfinity = reachesInfinity ||
isUnbounded(h);
1089 }
while (h != start);
1090 if (reachesInfinity) {
1112 assert(h.valid() && h.index() < origin_.size());
1113 return edgeLabel_[h.index() / 2];
1118 assert(h.valid() && h.index() < origin_.size());
1119 return edgeLabel_[h.index() / 2];
1132 assert(f.valid() && f.index() < faceLabel_.size());
1133 return faceLabel_[f.index()];
1138 assert(f.valid() && f.index() < faceLabel_.size());
1139 return faceLabel_[f.index()];
1152 assert(h.valid() && h.index() < origin_.size());
1153 const std::size_t
edge = h.index() / 2;
1154 const std::size_t from = originOffset_[
edge];
1155 const std::size_t to = originOffset_[
edge + 1];
1156 return std::span<const std::uint32_t>(originIndex_.data() + from, to - from);
1170 assert(v.valid() && v.index() < outgoing_.size());
1171 std::vector<std::uint32_t> origins;
1172 const HalfedgeId start = outgoing_[v.index()];
1173 if (!start.valid()) {
1178 const std::span<const std::uint32_t> edgeOrigins =
originsOf(h);
1179 origins.insert(origins.end(), edgeOrigins.begin(), edgeOrigins.end());
1181 }
while (h != start);
1182 std::sort(origins.begin(), origins.end());
1183 origins.erase(std::unique(origins.begin(), origins.end()), origins.end());
1211 template <
class UniformRandomBitGenerator>
1216 pointLocation_.reset();
1221 return static_cast<bool>(pointLocation_);
1269 template <
class Q,
class Fn>
1279 std::vector<IntersectionId> result;
1289 std::optional<IntersectionId> result;
1315 detail::promoted_number_t<detail::promoted_number_t<NumberType>>;
1325 if (infinity_.valid()) {
1326 return face(infinityBoundaryAtWest(p));
1335 for (std::uint32_t v = 0; v < points_.size(); ++v) {
1336 if (points_[v] == p) {
1340 for (std::uint32_t
edge = 0;
edge < edgeGeometry_.size(); ++
edge) {
1341 const EdgeGeometry& geometry = edgeGeometry_[
edge];
1345 bool contains = geometry.kind == EdgeKind::line;
1346 if (geometry.kind == EdgeKind::segment) {
1347 contains = p.x() >= std::min(geometry.a.x(), geometry.b.x()) &&
1348 p.x() <= std::max(geometry.a.x(), geometry.b.x()) &&
1349 p.y() >= std::min(geometry.a.y(), geometry.b.y()) &&
1350 p.y() <= std::max(geometry.a.y(), geometry.b.y());
1351 }
else if (geometry.kind == EdgeKind::ray) {
1356 contains =
dotSign(geometry.a, p, geometry.a, geometry.b) >= 0;
1362 return locateFaceLinear(p);
1367 [[nodiscard]] std::size_t topologicalVertexCount()
const {
1368 return points_.size() + (infinity_.valid() ? 1 : 0);
1373 enum class EdgeKind : std::uint8_t { segment, ray, line };
1379 struct EdgeGeometry {
1388 template <
class Q,
class Fn>
1389 bool visitStraightIntersecting(
const Q& piece, Fn& fn,
1390 std::vector<bool>* seenVertices,
1391 std::vector<bool>* seenEdges)
const;
1394 template <Po
intConcept Q,
class Fn>
1395 bool visitPointIntersecting(
const Q& point, Fn& fn,
1396 std::vector<bool>* seenVertices,
1397 std::vector<bool>* seenEdges)
const;
1400 struct InputSegment {
1401 Segment<PointType> segment;
1402 std::uint32_t origin;
1403 [[no_unique_address]] TLabel label;
1411 std::uint32_t origin;
1412 [[no_unique_address]] TLabel label;
1420 std::uint32_t origin;
1421 [[no_unique_address]] TLabel label;
1429 template <
class ShapeRange>
1430 static void collect(
const ShapeRange& shapes, std::vector<InputSegment>& segments,
1431 std::vector<InputCurve>& curves, std::vector<PointType>& isolated) {
1432 std::uint32_t index = 0;
1433 for (
const auto& shape : shapes) {
1434 append(shape, index, segments, curves, isolated);
1440 template <
class InputShape>
1441 static constexpr bool isSupported =
1442 detail::is_empty_shape_v<InputShape> || detail::is_point_v<InputShape> ||
1443 detail::is_segment_v<InputShape> || detail::is_oriented_segment_v<InputShape> ||
1444 detail::is_line_v<InputShape> || detail::is_oriented_line_v<InputShape> ||
1445 detail::is_ray_v<InputShape> ||
1446 detail::is_polyline_v<InputShape> || detail::is_monotone_chain_v<InputShape> ||
1447 detail::is_triangle_v<InputShape> || detail::is_rectangle_v<InputShape> ||
1448 detail::is_convex_v<InputShape> || detail::is_polygon_v<InputShape> ||
1449 detail::is_polygon_with_holes_v<InputShape>;
1452 template <
class InputShape>
1453 static void append(
const InputShape& shape, std::uint32_t index,
1454 std::vector<InputSegment>& segments, std::vector<InputCurve>& curves,
1455 std::vector<PointType>& isolated) {
1456 static_assert(isSupported<InputShape> || detail::is_shape_v<InputShape>,
1457 "Arrangement accepts points, segment-bounded shapes, lines, and rays");
1458 const auto addSegment = [&](
const auto&
edge) {
1462 isolated.push_back(a);
1465 InputSegment input{Segment<PointType>(a, b), index, TLabel{}};
1466 if constexpr (detail::has_label_v<TLabel>) {
1467 input.label = detail::copyLabel<TLabel>(shape);
1469 segments.push_back(std::move(input));
1470 InputCurve curve{EdgeKind::segment, a < b ? a : b, a < b ? b : a, index, TLabel{}};
1471 if constexpr (detail::has_label_v<TLabel>) {
1472 curve.label = detail::copyLabel<TLabel>(shape);
1474 curves.push_back(std::move(curve));
1477 if constexpr (detail::is_shape_v<InputShape>) {
1478 std::visit([&](
const auto& alternative) {
1479 if constexpr (isSupported<std::remove_cvref_t<
decltype(alternative)>>) {
1480 append(alternative, index, segments, curves, isolated);
1482 throw std::invalid_argument(
1483 "Arrangement accepts only points, segment-bounded shapes, lines, and rays");
1485 }, shape.variant());
1486 }
else if constexpr (detail::is_empty_shape_v<InputShape>) {
1488 }
else if constexpr (detail::is_point_v<InputShape>) {
1489 isolated.emplace_back(shape);
1490 }
else if constexpr (detail::is_segment_v<InputShape> ||
1491 detail::is_oriented_segment_v<InputShape>) {
1493 }
else if constexpr (detail::is_line_v<InputShape> ||
1494 detail::is_oriented_line_v<InputShape> ||
1495 detail::is_ray_v<InputShape>) {
1499 isolated.push_back(a);
1502 constexpr EdgeKind kind = detail::is_ray_v<InputShape> ? EdgeKind::ray : EdgeKind::line;
1503 InputCurve curve{kind, a, b, index, TLabel{}};
1504 if constexpr (detail::has_label_v<TLabel>) {
1505 curve.label = detail::copyLabel<TLabel>(shape);
1507 curves.push_back(std::move(curve));
1508 }
else if constexpr (detail::is_polygon_with_holes_v<InputShape>) {
1509 for (
const auto&
edge : shape.edges()) {
1512 }
else if constexpr (detail::is_polyline_v<InputShape> ||
1513 detail::is_monotone_chain_v<InputShape>) {
1514 if (shape.size() == 1) {
1515 isolated.emplace_back(shape[0]);
1517 for (
const auto&
edge : shape.edgesView()) {
1521 }
else if constexpr (
requires { shape.edgesView(); }) {
1522 for (
const auto&
edge : shape.edgesView()) {
1526 for (
const auto&
edge : shape.edges()) {
1535 void build(std::vector<InputSegment>& segments, std::vector<InputCurve>& curves,
1536 std::vector<PointType>& isolated,
bool simpleBoundaries) {
1537 const bool hasUnbounded = std::ranges::any_of(
1538 curves, [](
const InputCurve& curve) {
return curve.kind != EdgeKind::segment; });
1540 buildUnbounded(curves, isolated);
1543 std::vector<Piece> pieces = split(segments, isolated, simpleBoundaries);
1544 internVertices(pieces, isolated);
1545 simplifyStoredCoordinates();
1546 syncVertexApproximations();
1554 struct CarrierInterval {
1555 std::optional<NumberType> low;
1556 std::optional<NumberType> high;
1557 std::uint32_t origin;
1558 [[no_unique_address]] TLabel label;
1570 std::vector<CarrierInterval> intervals;
1571 std::vector<NumberType> cuts;
1577 struct AtomicCurve {
1578 std::uint32_t carrier;
1579 std::optional<NumberType> low;
1580 std::optional<NumberType> high;
1581 std::vector<std::uint32_t> origins;
1582 [[no_unique_address]] TLabel label;
1589 return carrier.usesX ? point.x() : point.y();
1595 static bool covers(
const CarrierInterval& interval,
1596 const std::optional<NumberType>& low,
1597 const std::optional<NumberType>& high) {
1598 if (!low.has_value()) {
1599 if (interval.low.has_value()) {
1602 }
else if (interval.low.has_value() && *low < *interval.low) {
1605 if (!high.has_value()) {
1606 if (interval.high.has_value()) {
1609 }
else if (interval.high.has_value() && *interval.high < *high) {
1616 static bool covers(
const CarrierInterval& interval,
const NumberType& value) {
1617 return (!interval.low.has_value() || !(value < *interval.low)) &&
1618 (!interval.high.has_value() || !(*interval.high < value));
1624 if (carrier.usesX) {
1630 const auto wide = [](
const NumberType& value) {
1631 return static_cast<WideNumber
>(value);
1633 const WideNumber dx = wide(carrier.b.x()) - wide(carrier.a.x());
1634 const WideNumber dy = wide(carrier.b.y()) - wide(carrier.a.y());
1635 const WideNumber rise =
1636 (wide(parameter) - wide(carrier.a.x())) * dy / dx;
1638 carrier.a.y() + detail::asNumber<NumberType>(rise));
1640 return PointType(carrier.a.x(), parameter);
1647 point.x().simplify();
1648 point.y().simplify();
1655 static bool sameCarrier(
const Carrier& carrier,
const InputCurve& curve) {
1663 void buildUnbounded(
const std::vector<InputCurve>& curves,
1664 const std::vector<PointType>& isolated) {
1665 std::vector<Carrier> carriers;
1666 for (
const InputCurve& curve : curves) {
1667 auto found = std::find_if(carriers.begin(), carriers.end(),
1668 [&](
const Carrier& carrier) {
1669 return sameCarrier(carrier, curve);
1671 if (found == carriers.end()) {
1677 carriers.push_back(Carrier{a, b, !(a.x() == b.x()), {}, {}});
1678 found = std::prev(carriers.end());
1680 Carrier& carrier = *found;
1681 const NumberType ta = parameterOf(carrier, curve.a);
1682 const NumberType tb = parameterOf(carrier, curve.b);
1683 CarrierInterval interval{{}, {}, curve.origin, curve.label};
1684 if (curve.kind == EdgeKind::segment) {
1685 interval.low = std::min(ta, tb);
1686 interval.high = std::max(ta, tb);
1687 }
else if (curve.kind == EdgeKind::ray) {
1694 if (interval.low.has_value()) {
1695 carrier.cuts.push_back(*interval.low);
1697 if (interval.high.has_value()) {
1698 carrier.cuts.push_back(*interval.high);
1700 carrier.intervals.push_back(std::move(interval));
1703 for (Carrier& carrier : carriers) {
1704 for (
const PointType& point : isolated) {
1708 const NumberType value = parameterOf(carrier, point);
1709 if (std::ranges::any_of(carrier.intervals,
1710 [&](
const CarrierInterval& interval) {
1711 return covers(interval, value);
1713 carrier.cuts.push_back(value);
1718 for (std::size_t i = 0; i < carriers.size(); ++i) {
1719 for (std::size_t j = i + 1; j < carriers.size(); ++j) {
1722 const auto intersection = first.template intersection<NumberType>(second);
1723 if (!intersection || !std::holds_alternative<PointType>(*intersection)) {
1726 const PointType& point = std::get<PointType>(*intersection);
1727 const NumberType ti = parameterOf(carriers[i], point);
1728 const NumberType tj = parameterOf(carriers[j], point);
1729 const bool onFirst = std::ranges::any_of(
1730 carriers[i].intervals,
1731 [&](
const CarrierInterval& interval) {
return covers(interval, ti); });
1732 const bool onSecond = std::ranges::any_of(
1733 carriers[j].intervals,
1734 [&](
const CarrierInterval& interval) {
return covers(interval, tj); });
1735 if (onFirst && onSecond) {
1736 carriers[i].cuts.push_back(ti);
1737 carriers[j].cuts.push_back(tj);
1742 std::vector<AtomicCurve> atoms;
1743 for (std::uint32_t c = 0; c < carriers.size(); ++c) {
1744 Carrier& carrier = carriers[c];
1754 std::sort(carrier.cuts.begin(), carrier.cuts.end());
1755 carrier.cuts.erase(std::unique(carrier.cuts.begin(), carrier.cuts.end()),
1756 carrier.cuts.end());
1758 const auto emit = [&](std::optional<NumberType> low,
1759 std::optional<NumberType> high) {
1760 const CarrierInterval* first =
nullptr;
1761 std::vector<std::uint32_t> origins;
1762 for (
const CarrierInterval& interval : carrier.intervals) {
1763 if (!covers(interval, low, high)) {
1766 if (first ==
nullptr || interval.origin < first->origin) {
1769 origins.push_back(interval.origin);
1771 if (first ==
nullptr) {
1774 std::sort(origins.begin(), origins.end());
1775 origins.erase(std::unique(origins.begin(), origins.end()), origins.end());
1776 atoms.push_back(AtomicCurve{c, std::move(low), std::move(high),
1777 std::move(origins), first->label});
1780 if (carrier.cuts.empty()) {
1784 emit({}, carrier.cuts.front());
1785 for (std::size_t i = 0; i + 1 < carrier.cuts.size(); ++i) {
1786 if (!(carrier.cuts[i] == carrier.cuts[i + 1])) {
1787 emit(carrier.cuts[i], carrier.cuts[i + 1]);
1790 emit(carrier.cuts.back(), {});
1793 std::unordered_map<PointType, std::uint32_t> vertexOf;
1794 const auto idOf = [&](
const PointType& point) {
1795 const auto found = vertexOf.find(point);
1796 if (found != vertexOf.end()) {
1797 return found->second;
1799 const auto id =
static_cast<std::uint32_t
>(points_.size());
1800 points_.push_back(point);
1801 vertexOf.emplace(point,
id);
1805 for (
const AtomicCurve& atom : atoms) {
1806 if (atom.low.has_value()) {
1807 idOf(pointAt(carriers[atom.carrier], *atom.low));
1809 if (atom.high.has_value()) {
1810 idOf(pointAt(carriers[atom.carrier], *atom.high));
1813 for (
const PointType& point : isolated) {
1816 infinity_ =
VertexId(
static_cast<std::uint32_t
>(points_.size()));
1818 originOffset_.push_back(0);
1819 for (
const AtomicCurve& atom : atoms) {
1820 const Carrier& carrier = carriers[atom.carrier];
1821 if (atom.low.has_value() && atom.high.has_value()) {
1822 const PointType a = pointAt(carrier, *atom.low);
1823 const PointType b = pointAt(carrier, *atom.high);
1824 origin_.push_back(idOf(a));
1825 origin_.push_back(idOf(b));
1826 edgeGeometry_.push_back({EdgeKind::segment, a, b});
1827 }
else if (atom.low.has_value() || atom.high.has_value()) {
1828 const bool increasing = atom.low.has_value();
1830 carrier, increasing ? *atom.low : *atom.high);
1831 const NumberType dx = carrier.b.x() - carrier.a.x();
1832 const NumberType dy = carrier.b.y() - carrier.a.y();
1834 increasing ? sourcePoint.x() + dx : sourcePoint.x() - dx,
1835 increasing ? sourcePoint.y() + dy : sourcePoint.y() - dy);
1836 origin_.push_back(idOf(sourcePoint));
1837 origin_.push_back(infinity_.index());
1838 edgeGeometry_.push_back({EdgeKind::ray, sourcePoint, directionPoint});
1840 origin_.push_back(infinity_.index());
1841 origin_.push_back(infinity_.index());
1842 edgeGeometry_.push_back({EdgeKind::line, carrier.a, carrier.b});
1844 edgeLabel_.push_back(atom.label);
1845 originIndex_.insert(originIndex_.end(), atom.origins.begin(), atom.origins.end());
1846 originOffset_.push_back(
static_cast<std::uint32_t
>(originIndex_.size()));
1849 simplifyStoredCoordinates();
1850 syncVertexApproximations();
1851 next_.assign(origin_.size(), 0);
1852 face_.assign(origin_.size(), 0);
1853 outgoing_.assign(topologicalVertexCount(),
HalfedgeId());
1854 wireHalfedgesUnbounded();
1855 buildFacesUnbounded();
1892 static std::vector<Piece> split(std::vector<InputSegment>& segments,
1893 const std::vector<PointType>& isolated,
1894 bool simpleBoundaries) {
1895 using IntegralPoint = Point<std::int64_t>;
1897 constexpr bool mayNeedIntegralNarrowing =
1902 std::sort(segments.begin(), segments.end(),
1903 [](
const InputSegment& left,
const InputSegment& right) {
1904 if (!(left.segment == right.segment)) {
1905 return left.segment < right.segment;
1907 return left.origin < right.origin;
1911 std::vector<std::size_t> group;
1912 for (std::size_t i = 0; i < segments.size(); ++i) {
1913 if (i == 0 || !(segments[i].segment == segments[i - 1].segment)) {
1917 group.push_back(segments.size());
1918 const std::size_t count = group.size() - 1;
1923 std::vector<NumberType> right, low, high;
1924 right.reserve(count);
1926 high.reserve(count);
1927 for (std::size_t i = 0; i < count; ++i) {
1928 const Segment<PointType>& current = segments[group[i]].segment;
1929 right.push_back(current.max().x());
1930 const auto [lo, hi] = std::minmax(current.min().y(), current.max().y());
1935 std::vector<std::uint32_t> order(count);
1936 for (std::size_t i = 0; i < count; ++i) {
1937 order[i] =
static_cast<std::uint32_t
>(i);
1939 std::sort(order.begin(), order.end(), [&](std::uint32_t a, std::uint32_t b) {
1940 return segments[group[a]].segment.min().x() < segments[group[b]].segment.min().x();
1943 std::vector<std::vector<PointType>> cuts(count);
1944 std::vector<std::optional<IntegralSegment>> integral;
1945 if constexpr (mayNeedIntegralNarrowing) {
1946 integral.resize(count);
1948 for (std::size_t i = 0; i < count; ++i) {
1949 const Segment<PointType>& segment = segments[group[i]].segment;
1950 cuts[i].push_back(segment.min());
1951 cuts[i].push_back(segment.max());
1960 const auto asIntegral = [](
const PointType& point)
1961 -> std::optional<IntegralPoint> {
1962 const auto store = [](
const auto&
x,
const auto&
y)
1963 -> std::optional<IntegralPoint> {
1964 if (!detail::representableAs<std::int64_t>(
x) ||
1965 !detail::representableAs<std::int64_t>(
y)) {
1966 return std::nullopt;
1968 return IntegralPoint(detail::narrowTo<std::int64_t>(
x),
1969 detail::narrowTo<std::int64_t>(
y));
1973 if (!point.x().isInteger() || !point.y().isInteger()) {
1974 return std::nullopt;
1977 return store(Integer(
static_cast<Integer
>(point.x())),
1978 Integer(
static_cast<Integer
>(point.y())));
1979 }
else if constexpr (detail::extended_integral<NumberType> ||
1980 std::same_as<NumberType, BigInt>) {
1981 return store(point.x(), point.y());
1983 return std::nullopt;
1986 if constexpr (mayNeedIntegralNarrowing) {
1987 const auto a = asIntegral(segment.min());
1988 const auto b = asIntegral(segment.max());
1990 integral[i].emplace(*a, *b);
1995 const auto meet = [&](std::size_t a, std::size_t b) {
1996 const auto add = [&](
const auto& piece) {
2000 if (
const auto* point = std::get_if<0>(&*piece)) {
2001 cuts[a].emplace_back(*point);
2002 cuts[b].emplace_back(*point);
2004 const auto& overlap = std::get<1>(*piece);
2005 for (
const auto& end : {overlap.min(), overlap.max()}) {
2006 cuts[a].emplace_back(end);
2007 cuts[b].emplace_back(end);
2012 if constexpr (mayNeedIntegralNarrowing) {
2013 if (integral[a] && integral[b]) {
2014 add(integral[a]->
template intersection<NumberType>(*integral[b]));
2018 add(segments[group[a]].segment.template intersection<NumberType>(
2019 segments[group[b]].segment));
2030 const std::uint32_t originCount = segments.empty()
2032 : 1 + std::ranges::max(segments, {}, &InputSegment::origin).origin;
2033 const bool useIntersectionSweep = simpleBoundaries && originCount == 2 && count >= 256 &&
2034 !std::floating_point<NumberType>;
2035 if (useIntersectionSweep) {
2036 if constexpr (!std::floating_point<NumberType>) {
2037 const bool allIntegral = mayNeedIntegralNarrowing &&
2038 std::ranges::all_of(
2039 integral, [](
const auto& segment) {
return segment.has_value(); });
2041 std::vector<IntegralSegment> unique;
2042 unique.reserve(count);
2043 for (
const auto& segment : integral) {
2044 unique.push_back(*segment);
2046 detail::BentleyOttmann<NumberType, IntegralSegment> sweep;
2047 for (
const auto& pair : sweep.findIntersections(unique)) {
2048 const auto indexOf = [&](
const IntegralSegment& segment) {
2049 return static_cast<std::size_t
>(
2050 std::lower_bound(unique.begin(), unique.end(), segment) -
2053 const std::size_t a = indexOf(pair[0]);
2054 const std::size_t b = indexOf(pair[1]);
2060 std::vector<Segment<PointType>> unique;
2061 unique.reserve(count);
2062 for (std::size_t i = 0; i < count; ++i) {
2063 unique.push_back(segments[group[i]].segment);
2065 detail::BentleyOttmann<NumberType, Segment<PointType>> sweep;
2066 for (
const auto& pair : sweep.findIntersections(unique)) {
2067 const auto indexOf = [&](
const Segment<PointType>& segment) {
2068 return static_cast<std::size_t
>(
2069 std::lower_bound(unique.begin(), unique.end(), segment) -
2072 const std::size_t a = indexOf(pair[0]);
2073 const std::size_t b = indexOf(pair[1]);
2084 std::vector<std::uint32_t> active;
2085 for (
const std::uint32_t current : order) {
2086 const NumberType& left = segments[group[current]].segment.min().x();
2087 std::size_t write = 0;
2088 for (std::size_t read = 0; read < active.size(); ++read) {
2089 const std::uint32_t other = active[read];
2092 if constexpr (mayNeedIntegralNarrowing) {
2093 if (integral[current] && integral[other]) {
2094 const IntegralSegment& currentSegment = *integral[current];
2095 const IntegralSegment& otherSegment = *integral[other];
2096 expired = otherSegment.max().x() < currentSegment.min().x();
2097 const auto [currentLow, currentHigh] =
2098 std::minmax(currentSegment.min().y(), currentSegment.max().y());
2099 const auto [otherLow, otherHigh] =
2100 std::minmax(otherSegment.min().y(), otherSegment.max().y());
2101 missesInY = otherHigh < currentLow || currentHigh < otherLow;
2103 expired = right[other] < left;
2104 missesInY = high[other] < low[current] || high[current] < low[other];
2107 expired = right[other] < left;
2108 missesInY = high[other] < low[current] || high[current] < low[other];
2113 active[write++] = other;
2117 meet(other, current);
2119 active.resize(write);
2120 active.push_back(current);
2124 std::vector<Piece> pieces;
2125 for (std::size_t i = 0; i < count; ++i) {
2126 for (
const PointType& point : isolated) {
2127 if (segments[group[i]].segment.contains(point)) {
2128 cuts[i].push_back(point);
2146 std::sort(cuts[i].begin(), cuts[i].end());
2147 cuts[i].erase(std::unique(cuts[i].begin(), cuts[i].end()), cuts[i].end());
2148 for (std::size_t k = 0; k + 1 < cuts[i].size(); ++k) {
2149 for (std::size_t s = group[i]; s < group[i + 1]; ++s) {
2150 pieces.push_back(Piece{cuts[i][k], cuts[i][k + 1], segments[s].origin,
2151 segments[s].label});
2161 void internVertices(std::vector<Piece>& pieces,
const std::vector<PointType>& isolated) {
2165 std::sort(pieces.begin(), pieces.end(), [](
const Piece& left,
const Piece& right) {
2166 if (!(left.a == right.a)) {
2167 return left.a < right.a;
2169 if (!(left.b == right.b)) {
2170 return left.b < right.b;
2172 return left.origin < right.origin;
2175 std::unordered_map<PointType, std::uint32_t> vertexOf;
2176 const auto idOf = [&](
const PointType& point) {
2177 const auto found = vertexOf.find(point);
2178 if (found != vertexOf.end()) {
2179 return found->second;
2181 const auto id =
static_cast<std::uint32_t
>(points_.size());
2182 points_.push_back(point);
2183 vertexOf.emplace(point,
id);
2187 originOffset_.push_back(0);
2188 for (std::size_t i = 0; i < pieces.size();) {
2189 origin_.push_back(idOf(pieces[i].a));
2190 origin_.push_back(idOf(pieces[i].b));
2191 edgeGeometry_.push_back({EdgeKind::segment, pieces[i].a, pieces[i].b});
2192 edgeLabel_.push_back(pieces[i].label);
2196 while (j < pieces.size() && pieces[j].a == pieces[i].a && pieces[j].b == pieces[i].b) {
2197 if (j == i || pieces[j].origin != pieces[j - 1].origin) {
2198 originIndex_.push_back(pieces[j].origin);
2202 originOffset_.push_back(
static_cast<std::uint32_t
>(originIndex_.size()));
2206 for (
const PointType& point : isolated) {
2209 next_.assign(origin_.size(), 0);
2210 face_.assign(origin_.size(), 0);
2211 outgoing_.assign(points_.size(), HalfedgeId());
2232 void simplifyStoredCoordinates() {
2234 const auto reduce = [](PointType& point) {
2235 point.x().simplify();
2236 point.y().simplify();
2238 for (PointType& point : points_) {
2241 for (EdgeGeometry& geometry : edgeGeometry_) {
2250 using VertexCoordinate =
2251 detail::orientation_coordinate_t<NumberType, NumberType, NumberType>;
2262 void syncVertexApproximations() {
2263 if constexpr (detail::filtersSign<VertexCoordinate>) {
2264 vertexApproximations_.clear();
2265 vertexApproximations_.reserve(points_.size());
2266 for (
const PointType& point : points_) {
2267 vertexApproximations_.push_back(detail::approximatePoint(point));
2276 [[nodiscard]]
auto filteredVertex(std::uint32_t index)
const {
2277 assert(!detail::filtersSign<VertexCoordinate> ||
2278 vertexApproximations_.size() == points_.size());
2279 return detail::filtered<VertexCoordinate>(points_[index], vertexApproximations_, index);
2286 void wireHalfedges() {
2287 std::vector<std::vector<std::uint32_t>> fan(points_.size());
2288 for (std::uint32_t h = 0; h < origin_.size(); ++h) {
2289 fan[origin_[h]].push_back(h);
2291 for (std::uint32_t v = 0; v < fan.size(); ++v) {
2292 std::vector<std::uint32_t>& around = fan[v];
2293 if (around.empty()) {
2296 const PointType& center = points_[v];
2299 const auto half = [&](std::uint32_t h) {
2300 const PointType& to = points_[origin_[h ^ 1]];
2301 if (to.y() > center.y()) {
2304 if (to.y() < center.y()) {
2307 return to.x() > center.x() ? 0 : 1;
2309 const auto filteredCenter = filteredVertex(v);
2310 std::sort(around.begin(), around.end(), [&](std::uint32_t left, std::uint32_t right) {
2311 const int leftHalf = half(left);
2312 const int rightHalf = half(right);
2313 if (leftHalf != rightHalf) {
2314 return leftHalf < rightHalf;
2316 return detail::orientationSignOf(filteredCenter,
2317 filteredVertex(origin_[left ^ 1]),
2318 filteredVertex(origin_[right ^ 1]))
2321 const std::size_t degree = around.size();
2322 for (std::size_t i = 0; i < degree; ++i) {
2323 next_[around[i] ^ 1] = around[(i + degree - 1) % degree];
2325 outgoing_[v] = HalfedgeId(around.front());
2332 struct FanDirection {
2336 std::uint32_t halfedge;
2342 [[nodiscard]] FanDirection fanDirection(std::uint32_t h)
const {
2343 const EdgeGeometry& geometry = edgeGeometry_[h / 2];
2344 NumberType dx = geometry.b.x() - geometry.a.x();
2345 NumberType dy = geometry.b.y() - geometry.a.y();
2346 if (geometry.kind == EdgeKind::segment) {
2347 const PointType& from = points_[origin_[h]];
2348 const PointType& to = points_[origin_[h ^ 1]];
2349 dx = to.x() - from.x();
2350 dy = to.y() - from.y();
2351 }
else if (geometry.kind == EdgeKind::line && h % 2 == 0) {
2355 return {dx, dy, geometry.a, h};
2362 static int directionHalf(
const FanDirection& direction) {
2363 if (direction.dy > NumberType(0)) {
2366 if (direction.dy < NumberType(0)) {
2369 return direction.dx > NumberType(0) ? 0 : 1;
2375 static bool fanLess(
const FanDirection& left,
const FanDirection& right) {
2376 const int leftHalf = directionHalf(left);
2377 const int rightHalf = directionHalf(right);
2378 if (leftHalf != rightHalf) {
2379 return leftHalf < rightHalf;
2381 using Vector = Point<NumberType>;
2382 const Vector leftDirection(left.dx, left.dy);
2383 const auto cross =
crossSign(leftDirection, Vector(right.dx, right.dy));
2391 leftDirection, Vector(right.anchor.x() - left.anchor.x(),
2392 right.anchor.y() - left.anchor.y()));
2396 return left.halfedge < right.halfedge;
2401 static bool infinityFanLess(
const FanDirection& left,
const FanDirection& right) {
2402 return fanLess(right, left);
2409 void wireHalfedgesUnbounded() {
2410 std::vector<std::vector<std::uint32_t>> fan(topologicalVertexCount());
2411 for (std::uint32_t h = 0; h < origin_.size(); ++h) {
2412 fan[origin_[h]].push_back(h);
2414 for (std::uint32_t v = 0; v < fan.size(); ++v) {
2415 std::vector<std::uint32_t>& around = fan[v];
2416 if (around.empty()) {
2419 const bool atInfinity = infinity_.valid() && v == infinity_.index();
2420 std::sort(around.begin(), around.end(), [&](std::uint32_t left, std::uint32_t right) {
2422 ? infinityFanLess(fanDirection(left), fanDirection(right))
2423 : fanLess(fanDirection(left), fanDirection(right));
2425 const std::size_t degree = around.size();
2426 for (std::size_t i = 0; i < degree; ++i) {
2427 next_[around[i] ^ 1] = around[(i + degree - 1) % degree];
2429 outgoing_[v] = HalfedgeId(around.front());
2430 if (infinity_.valid() && v == infinity_.index()) {
2431 infinityFan_ = around;
2438 [[nodiscard]] HalfedgeId infinityBoundaryAtWest(
const PointType& point)
const {
2439 assert(!infinityFan_.empty());
2443 const FanDirection query{NumberType(-1), NumberType(0), point, 0};
2444 const auto after = std::upper_bound(
2445 infinityFan_.begin(), infinityFan_.end(), query,
2446 [&](
const FanDirection& value, std::uint32_t halfedge) {
2447 return infinityFanLess(value, fanDirection(halfedge));
2449 const std::uint32_t nextDirection =
2450 after == infinityFan_.end() ? infinityFan_.front() : *after;
2451 return HalfedgeId(nextDirection ^ 1);
2457 void buildFacesUnbounded() {
2458 constexpr std::uint32_t none = ~std::uint32_t{};
2459 const std::uint32_t halfedges =
static_cast<std::uint32_t
>(origin_.size());
2460 std::vector<std::uint32_t> cycleOf(halfedges, none);
2461 std::vector<std::uint32_t> representative;
2462 std::vector<bool> reachesInfinity;
2463 for (std::uint32_t h = 0; h < halfedges; ++h) {
2464 if (cycleOf[h] != none) {
2467 const auto id =
static_cast<std::uint32_t
>(representative.size());
2468 representative.push_back(h);
2469 bool unbounded =
false;
2470 std::uint32_t walk = h;
2473 unbounded = unbounded || origin_[walk] == infinity_.index();
2475 }
while (walk != h);
2476 reachesInfinity.push_back(unbounded);
2479 const auto cycles =
static_cast<std::uint32_t
>(representative.size());
2480 std::vector<std::uint32_t> parent(cycles);
2481 for (std::uint32_t i = 0; i < cycles; ++i) {
2484 const auto root = [&parent](std::uint32_t
x) {
2485 while (parent[x] != x) {
2486 parent[
x] = parent[parent[
x]];
2492 std::vector<bool> isOuter(cycles,
false);
2493 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2494 if (reachesInfinity[
id]) {
2497 const std::uint32_t leftmost = leftmostVertexOf(representative[
id]);
2498 if (turnsLeftEverywhereAt(representative[
id], leftmost)) {
2502 const HalfedgeId left = halfedgeLeftOf(points_[leftmost]);
2503 const HalfedgeId other = left.valid() ? left : infinityBoundaryAtWest(points_[leftmost]);
2504 parent[root(
id)] = root(cycleOf[other.index()]);
2507 std::vector<std::uint32_t> infinityCycles;
2508 infinityCycles.reserve(cycles);
2509 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2510 if (reachesInfinity[
id]) {
2511 infinityCycles.push_back(
id);
2514 const PointType zero(NumberType(0), NumberType(0));
2515 const std::uint32_t westCycle = cycleOf[infinityBoundaryAtWest(zero).index()];
2516 const auto west = std::find(infinityCycles.begin(), infinityCycles.end(), westCycle);
2517 if (west != infinityCycles.end()) {
2518 std::iter_swap(infinityCycles.begin(), west);
2521 std::vector<std::uint32_t> faceOfComponent(cycles, none);
2522 outerCycle_.clear();
2523 unboundedFace_.clear();
2524 for (
const std::uint32_t
id : infinityCycles) {
2525 const std::uint32_t component = root(
id);
2526 if (faceOfComponent[component] != none) {
2529 faceOfComponent[component] =
static_cast<std::uint32_t
>(outerCycle_.size());
2530 outerCycle_.push_back(HalfedgeId());
2531 unboundedFace_.push_back(
true);
2533 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2537 const std::uint32_t component = root(
id);
2538 assert(faceOfComponent[component] == none);
2539 faceOfComponent[component] =
static_cast<std::uint32_t
>(outerCycle_.size());
2540 outerCycle_.push_back(HalfedgeId(representative[
id]));
2541 unboundedFace_.push_back(
false);
2544 std::vector<std::vector<HalfedgeId>> inner(outerCycle_.size());
2545 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2546 const std::uint32_t f = faceOfComponent[root(
id)];
2549 inner[f].push_back(HalfedgeId(representative[
id]));
2551 std::uint32_t walk = representative[id];
2555 }
while (walk != representative[
id]);
2558 innerCycle_.clear();
2559 innerOffset_.assign(1, 0);
2560 for (
const std::vector<HalfedgeId>& cyclesOfFace : inner) {
2561 innerCycle_.insert(innerCycle_.end(), cyclesOfFace.begin(), cyclesOfFace.end());
2562 innerOffset_.push_back(
static_cast<std::uint32_t
>(innerCycle_.size()));
2564 faceLabel_.assign(outerCycle_.size(), TLabel{});
2571 constexpr std::uint32_t none = ~std::uint32_t{};
2572 const std::uint32_t halfedges =
static_cast<std::uint32_t
>(origin_.size());
2574 std::vector<std::uint32_t> cycleOf(halfedges, none);
2575 std::vector<std::uint32_t> representative;
2576 for (std::uint32_t h = 0; h < halfedges; ++h) {
2577 if (cycleOf[h] != none) {
2580 const auto id =
static_cast<std::uint32_t
>(representative.size());
2581 representative.push_back(h);
2582 std::uint32_t walk = h;
2586 }
while (walk != h);
2591 const auto cycles =
static_cast<std::uint32_t
>(representative.size());
2592 std::vector<std::uint32_t> parent(cycles + 1);
2593 for (std::uint32_t i = 0; i < parent.size(); ++i) {
2596 const auto root = [&parent](std::uint32_t
x) {
2597 while (parent[x] != x) {
2598 parent[
x] = parent[parent[
x]];
2608 std::vector<bool> isOuter(cycles,
false);
2609 std::vector<std::uint32_t> asking;
2610 std::vector<std::uint32_t> askedFrom;
2611 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2612 const std::uint32_t leftmost = leftmostVertexOf(representative[
id]);
2613 if (turnsLeftEverywhereAt(representative[
id], leftmost)) {
2617 asking.push_back(
id);
2618 askedFrom.push_back(leftmost);
2624 const std::vector<HalfedgeId> toTheLeft = halfedgesLeftOf(askedFrom);
2625 for (std::size_t i = 0; i < asking.size(); ++i) {
2626 const std::uint32_t other =
2627 toTheLeft[i].valid() ? cycleOf[toTheLeft[i].index()] : cycles;
2628 parent[root(asking[i])] = root(other);
2632 std::vector<std::uint32_t> faceOfComponent(cycles + 1, none);
2633 faceOfComponent[root(cycles)] = 0;
2634 outerCycle_.assign(1, HalfedgeId());
2635 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2641 assert(faceOfComponent[root(
id)] == none);
2642 faceOfComponent[root(
id)] =
static_cast<std::uint32_t
>(outerCycle_.size());
2643 outerCycle_.push_back(HalfedgeId(representative[
id]));
2646 std::vector<std::vector<HalfedgeId>> inner(outerCycle_.size());
2647 for (std::uint32_t
id = 0;
id < cycles; ++id) {
2648 const std::uint32_t f = faceOfComponent[root(
id)];
2651 inner[f].push_back(HalfedgeId(representative[
id]));
2653 std::uint32_t walk = representative[id];
2657 }
while (walk != representative[
id]);
2660 innerOffset_.assign(1, 0);
2661 for (
const std::vector<HalfedgeId>& cyclesOfFace : inner) {
2662 innerCycle_.insert(innerCycle_.end(), cyclesOfFace.begin(), cyclesOfFace.end());
2663 innerOffset_.push_back(
static_cast<std::uint32_t
>(innerCycle_.size()));
2665 faceLabel_.assign(outerCycle_.size(), TLabel{});
2666 unboundedFace_.assign(outerCycle_.size(),
false);
2667 unboundedFace_.front() =
true;
2692 [[nodiscard]] HalfedgeId halfedgeLeftOf(
const PointType& p)
const {
2694 WideNumber bestNumerator(0);
2695 WideNumber bestUpX(0);
2696 WideNumber bestUpY(1);
2697 const auto wide = [](
const NumberType& value) {
2698 return static_cast<WideNumber
>(value);
2700 for (std::uint32_t h = 0; h < origin_.size(); h += 2) {
2701 const EdgeGeometry& geometry = edgeGeometry_[h / 2];
2702 const PointType& a = geometry.a;
2703 const PointType& b = geometry.b;
2704 if (a.y() == b.y()) {
2709 const bool upwards = a.y() < b.y();
2710 if (geometry.kind == EdgeKind::segment) {
2711 const NumberType& lowY = upwards ? a.y() : b.y();
2712 const NumberType& highY = upwards ? b.y() : a.y();
2713 if (p.y() < lowY || !(p.y() < highY)) {
2716 }
else if (geometry.kind == EdgeKind::ray) {
2717 if (upwards ? p.y() < a.y() : !(p.y() < a.y())) {
2725 const PointType& low = upwards ? a : b;
2726 const PointType& high = upwards ? b : a;
2734 const WideNumber upX = wide(high.x()) - wide(low.x());
2735 const WideNumber upY = wide(high.y()) - wide(low.y());
2736 const WideNumber numerator =
2737 (wide(low.x()) - wide(p.x())) * upY - (wide(low.y()) - wide(p.y())) * upX;
2739 const WideNumber here = numerator * bestUpY;
2740 const WideNumber there = bestNumerator * upY;
2744 if (here == there) {
2747 if (!(upX * bestUpY - upY * bestUpX > WideNumber(0))) {
2754 best = HalfedgeId(upwards ? h + 1 : h);
2755 bestNumerator = numerator;
2792 using is_transparent = void;
2793 const Arrangement* arrangement;
2794 const std::vector<std::uint32_t>* position;
2796 bool operator()(std::uint32_t left, std::uint32_t right)
const {
2797 const std::vector<std::uint32_t>& origin = arrangement->origin_;
2798 const std::uint32_t leftLow = origin[left ^ 1];
2799 const std::uint32_t rightLow = origin[right ^ 1];
2800 const auto vertex = [
this](std::uint32_t index) {
2801 return arrangement->filteredVertex(index);
2803 if (leftLow == rightLow) {
2806 return detail::orientationSignOf(
vertex(leftLow),
vertex(origin[left]),
2810 if ((*position)[leftLow] > (*position)[rightLow]) {
2811 return detail::orientationSignOf(
vertex(rightLow),
vertex(origin[right]),
2815 return detail::orientationSignOf(
vertex(leftLow),
vertex(origin[left]),
2823 bool operator()(std::uint32_t left,
const PointType& p)
const {
2824 return detail::orientationSignOf(
2825 arrangement->filteredVertex(arrangement->origin_[left ^ 1]),
2826 arrangement->filteredVertex(arrangement->origin_[left]),
2827 detail::filtered<VertexCoordinate>(p))
2831 bool operator()(
const PointType& p, std::uint32_t right)
const {
2832 return detail::orientationSignOf(
2833 arrangement->filteredVertex(arrangement->origin_[right ^ 1]),
2834 arrangement->filteredVertex(arrangement->origin_[right]),
2835 detail::filtered<VertexCoordinate>(p))
2863 [[nodiscard]] std::vector<HalfedgeId> halfedgesLeftOf(
2864 const std::vector<std::uint32_t>& queries)
const {
2865 const std::uint64_t edges = origin_.size() / 2;
2866 const std::uint64_t asked = queries.size();
2868 const std::uint64_t depth = std::bit_width(edges);
2874 constexpr std::uint64_t perComparisonNum = 1;
2875 constexpr std::uint64_t perComparisonDen = 2;
2876 if (!infinity_.valid() &&
2877 perComparisonDen * asked * edges >
2878 perComparisonNum * (2 * edges + asked) * depth) {
2879 return sweepHalfedgesLeftOf(queries);
2881 std::vector<HalfedgeId> answer;
2882 answer.reserve(queries.size());
2883 for (
const std::uint32_t query : queries) {
2884 answer.push_back(halfedgeLeftOf(points_[query]));
2912 [[nodiscard]] std::vector<HalfedgeId> sweepHalfedgesLeftOf(
2913 const std::vector<std::uint32_t>& queries)
const {
2916 std::vector<std::uint32_t> byHeight(points_.size());
2917 for (std::uint32_t v = 0; v < byHeight.size(); ++v) {
2920 std::sort(byHeight.begin(), byHeight.end(), [
this](std::uint32_t a, std::uint32_t b) {
2921 if (!(points_[a].y() == points_[b].y())) {
2922 return points_[a].y() < points_[b].y();
2924 return points_[a].x() < points_[b].x();
2926 std::vector<std::uint32_t> position(points_.size());
2927 for (std::uint32_t i = 0; i < byHeight.size(); ++i) {
2928 position[byHeight[i]] = i;
2932 enum Phase : std::uint8_t { leaves = 0, joins = 1, asks = 2 };
2935 std::uint32_t subject;
2938 std::vector<Event> events;
2939 events.reserve(origin_.size() + queries.size());
2940 for (std::uint32_t h = 0; h < origin_.size(); h += 2) {
2941 if (points_[origin_[h]].
y() == points_[origin_[h + 1]].
y()) {
2944 const std::uint32_t downward =
2945 points_[origin_[h]].y() > points_[origin_[h + 1]].y() ? h : h + 1;
2946 events.push_back({position[origin_[downward]], downward, leaves});
2947 events.push_back({position[origin_[downward ^ 1]], downward, joins});
2949 for (std::uint32_t q = 0; q < queries.size(); ++q) {
2950 events.push_back({position[queries[q]], q, asks});
2952 std::sort(events.begin(), events.end(), [](
const Event& left,
const Event& right) {
2953 if (left.at != right.at) {
2954 return left.at < right.at;
2956 return left.phase < right.phase;
2959 std::vector<HalfedgeId> answer(queries.size());
2960 using Status = std::set<std::uint32_t, SweepOrder>;
2961 Status line(SweepOrder{
this, &position});
2965 std::vector<typename Status::iterator> seat(origin_.size() / 2);
2966 for (
const Event& event : events) {
2967 if (event.phase == leaves) {
2968 line.erase(seat[event.subject / 2]);
2969 }
else if (event.phase == joins) {
2970 const auto placed = line.insert(event.subject);
2971 assert(placed.second);
2972 seat[
event.subject / 2] = placed.first;
2974 const auto above = line.lower_bound(points_[queries[event.subject]]);
2975 if (above != line.begin()) {
2976 answer[
event.subject] = HalfedgeId(*std::prev(above));
2984 template <
class Function>
2985 void forEachBoundaryHalfedge(FaceId f,
const Function& fn)
const {
2986 const auto walkCycle = [&](HalfedgeId start) {
2987 std::uint32_t h = start.index();
2991 }
while (h != start.index());
2993 if (outerCycle_[f.index()].valid()) {
2994 walkCycle(outerCycle_[f.index()]);
2996 for (HalfedgeId inner : innerCycles(f)) {
3002 [[nodiscard]] std::uint32_t leftmostVertexOf(std::uint32_t start)
const {
3003 std::uint32_t leftmost = origin_[start];
3004 for (std::uint32_t h = next_[start]; h != start; h = next_[h]) {
3005 if (points_[origin_[h]] < points_[leftmost]) {
3006 leftmost = origin_[h];
3039 [[nodiscard]]
bool turnsLeftEverywhereAt(std::uint32_t start, std::uint32_t vertex)
const {
3040 std::uint32_t h = start;
3044 const std::uint32_t ahead = next_[h];
3045 if (origin_[ahead] == vertex &&
3046 !(detail::orientationSignOf(filteredVertex(vertex),
3047 filteredVertex(origin_[ahead ^ 1]),
3048 filteredVertex(origin_[h]))
3053 }
while (h != start);
3058 [[nodiscard]] std::vector<PointType> cycleRing(HalfedgeId start)
const {
3059 std::vector<PointType> ring;
3060 std::uint32_t h = start.index();
3062 ring.push_back(points_[origin_[h]]);
3064 }
while (h != start.index());
3072 static void collectRings(std::vector<PointType> walk, std::vector<Polygon<PointType>>& out) {
3074 if (walk.size() < 3) {
3077 std::vector<std::vector<PointType>> rings;
3078 detail::splitWalkIntoRings(walk, rings);
3079 for (std::vector<PointType>& ring : rings) {
3081 if (ring.size() < 3) {
3084 const int orientation = detail::ringOrientation(ring);
3085 if (orientation == 0) {
3088 if (orientation < 0) {
3089 std::reverse(ring.begin(), ring.end());
3093 std::rotate(ring.begin(), std::min_element(ring.begin(), ring.end()), ring.end());
3094 out.emplace_back(std::move(ring),
true);
3100 static void pruneSpikes(std::vector<PointType>& ring) {
3101 bool changed =
true;
3102 while (changed && ring.size() >= 3) {
3104 for (std::size_t i = 0; i < ring.size() && ring.size() >= 3; ++i) {
3105 const std::size_t size = ring.size();
3106 if (!(ring[(i + size - 1) % size] == ring[(i + 1) % size])) {
3109 const auto at =
static_cast<std::ptrdiff_t
>(i);
3111 ring.erase(ring.begin() + at, ring.begin() + at + 2);
3113 ring.erase(ring.begin() + at);
3114 ring.erase(ring.begin());
3120 if (ring.size() < 3) {
3144 class TrapezoidPointLocation {
3145 using WorkNumber = division_result_t<NumberType>;
3146 using WorkPoint = Point<WorkNumber>;
3147 using WorkLine = OrientedLine<WorkPoint>;
3148 using IntegralPoint = Point<std::int64_t>;
3149 using IntegralLine = OrientedLine<IntegralPoint>;
3150 using StoredLine = std::variant<WorkLine, IntegralLine>;
3151 using QueryInteger = rational_int_t<WorkNumber>;
3152 using WideIntegralPoint = Point<QueryInteger>;
3153 static constexpr std::uint32_t none = ~std::uint32_t{};
3161 std::int8_t epsInfinity = 0;
3167 static bool abscissaLess(
const Abscissa& left,
const Abscissa& right) {
3168 if (left.point.x() != right.point.x()) {
3169 return left.point.x() < right.point.x();
3171 if (left.epsInfinity != right.epsInfinity) {
3172 return left.epsInfinity < right.epsInfinity;
3174 return left.epsInfinity == 0 && left.point.y() < right.point.y();
3179 struct SampleAbscissa {
3196 std::int8_t infinity = 0;
3197 std::uint32_t wall = none;
3199 static Bound negativeInfinity() {
return Bound{-1, none}; }
3200 static Bound positiveInfinity() {
return Bound{1, none}; }
3203 return infinity == other.infinity && wall == other.wall;
3212 std::vector<std::uint32_t> starts;
3217 StoredLine queryLine;
3220 HalfedgeId leftToRight;
3221 WorkNumber originalDx{};
3222 WorkNumber originalDy{};
3226 const PointType* point =
nullptr;
3227 std::optional<IntegralPoint> integralPoint;
3228 std::optional<WideIntegralPoint> wideIntegralPoint;
3234 std::uint32_t bottom = none;
3235 std::uint32_t top = none;
3236 std::uint32_t leaf = none;
3241 enum class NodeKind : std::uint8_t { leaf,
x, curve };
3244 NodeKind kind = NodeKind::leaf;
3245 std::uint32_t value = 0;
3246 std::uint32_t low = none;
3247 std::uint32_t high = none;
3251 template <
class UniformRandomBitGenerator>
3252 TrapezoidPointLocation(
const Arrangement& arrangement,
3253 UniformRandomBitGenerator&& generator) {
3254 makeWalls(arrangement);
3255 makeVertexIndex(arrangement);
3256 makeCurves(arrangement);
3258 const std::uint32_t initial = newTrapezoid(
3259 Bound::negativeInfinity(), Bound::positiveInfinity(), none, none);
3260 root_ = trapezoids_[initial].leaf;
3262 std::vector<std::uint32_t> order(curves_.size());
3263 for (std::uint32_t i = 0; i < order.size(); ++i) {
3266 std::shuffle(order.begin(), order.end(),
3267 std::forward<UniformRandomBitGenerator>(generator));
3268 for (
const std::uint32_t curve : order) {
3271 labelTrapezoids(arrangement);
3272 std::vector<WorkLine>().swap(constructionLines_);
3275 [[nodiscard]] FaceId locateFace(
const Arrangement& arrangement,
3276 const PointType& point)
const {
3277 const Query query = makeQuery(point);
3278 std::uint32_t node = root_;
3279 while (nodes_[node].kind != NodeKind::leaf) {
3280 const Node& decision = nodes_[node];
3281 if (decision.kind == NodeKind::x) {
3284 node = againstWall(point, decision.value) <= 0 ? decision.low
3288 const Curve& curve = curves_[decision.value];
3289 const auto orientation = sideOf(curve, query);
3290 int side = orientation > 0 ? 1 : (orientation < 0 ? -1 : 0);
3294 side = curve.originalDy > WorkNumber(0)
3296 : (curve.originalDy < WorkNumber(0)
3298 : (curve.originalDx > WorkNumber(0) ? 1 : -1));
3300 node = side < 0 ? decision.low : decision.high;
3302 const FaceId result = trapezoids_[nodes_[node].value].face;
3303 assert(result.valid() && result.index() < arrangement.faceCount());
3307 [[nodiscard]] CellId locateCell(
const Arrangement& arrangement,
3308 const PointType& point)
const {
3309 const auto vertex = std::lower_bound(
3310 vertices_.begin(), vertices_.end(), point,
3311 [&](VertexId v,
const PointType& p) { return arrangement.points_[v.index()] < p; });
3312 if (vertex != vertices_.end() && arrangement.points_[
vertex->index()] == point) {
3316 const Query query = makeQuery(point);
3317 std::uint32_t node = root_;
3318 while (nodes_[node].kind != NodeKind::leaf) {
3319 const Node& decision = nodes_[node];
3320 if (decision.kind == NodeKind::x) {
3321 node = againstWall(point, decision.value) < 0 ? decision.low
3325 const Curve& curve = curves_[decision.value];
3326 const auto side = sideOf(curve, query);
3328 return HalfedgeId(2 * (curve.leftToRight.index() / 2));
3330 node = side < 0 ? decision.low : decision.high;
3332 return trapezoids_[nodes_[node].value].face;
3335 [[nodiscard]] VertexId indexedVertex(std::size_t index)
const {
3336 return vertices_[index];
3344 template <
class Number,
class Fn>
3345 void visitCandidateVertices(
const Arrangement& arrangement,
3346 const std::optional<Number>& low,
3347 const std::optional<Number>& high,
3349 auto first = vertices_.begin();
3350 auto last = vertices_.end();
3352 first = std::lower_bound(
3353 first, last, *low, [&](VertexId v,
const Number& x) {
3354 return arrangement.points_[v.index()].x() <
x;
3358 last = std::upper_bound(
3359 first, last, *high, [&](
const Number& x, VertexId v) {
3360 return x < arrangement.points_[v.index()].x();
3363 for (
auto it = first; it != last; ++it) {
3368 [[nodiscard]] HalfedgeId indexedHalfedge(std::size_t index)
const {
3369 return HalfedgeId(2 * (curves_[index].leftToRight.index() / 2));
3391 template <
class Parameter,
class Q,
class Fn>
3392 bool visitCandidateHalfedges(
const Q& query, Fn&& fn)
const {
3393 const Parameter zero(0);
3395 const Parameter x0(query[0].
x());
3396 const Parameter y0(query[0].
y());
3397 const Parameter dx = Parameter(query[1].
x()) - x0;
3398 const Parameter dy = Parameter(query[1].
y()) - y0;
3404 std::optional<Parameter> low;
3405 std::optional<Parameter> high;
3410 const auto narrow = [&zero](Span& span,
const Parameter& value,
3411 const Parameter& rate,
bool nonNegative) {
3413 return nonNegative ? !(value < zero) : !(value > zero);
3415 const Parameter root = -value / rate;
3416 if ((rate > zero) == nonNegative) {
3417 if (!span.low || *span.low < root) {
3421 if (!span.high || root < *span.high) {
3425 return !(span.low && span.high && *span.high < *span.low);
3435 const auto narrowSheared = [&](Span& span,
const Parameter& value,
3436 const Parameter& rate,
3437 const Parameter& epsValue,
3438 const Parameter& epsRate,
bool nonNegative) {
3440 return narrow(span, value, rate, nonNegative);
3442 if (value != zero) {
3443 return (value > zero) == nonNegative;
3445 return narrow(span, epsValue, epsRate, nonNegative);
3449 const auto sideAlongQuery = [&](
const Curve& curve) {
3451 [&](
const auto& line) {
3452 const Parameter sourceX(line.source().x());
3453 const Parameter sourceY(line.source().y());
3454 const Parameter edgeX = Parameter(line.target().x()) - sourceX;
3455 const Parameter edgeY = Parameter(line.target().y()) - sourceY;
3456 return std::pair<Parameter, Parameter>(
3457 edgeX * (y0 - sourceY) - edgeY * (x0 - sourceX),
3458 edgeX * dy - edgeY * dx);
3465 const auto laterFirst = [](
const Span& left,
const Span& right) {
3472 return *right.low < *left.low;
3475 Span start{root_, std::nullopt, std::nullopt};
3476 if constexpr (!OrientedLineConcept<Q>) {
3478 if constexpr (!RayConcept<Q>) {
3479 start.high = Parameter(1);
3483 std::vector<Span> pending;
3484 pending.push_back(std::move(start));
3485 const auto offer = [&](Span span) {
3486 pending.push_back(std::move(span));
3487 std::push_heap(pending.begin(), pending.end(), laterFirst);
3490 while (!pending.empty()) {
3491 std::pop_heap(pending.begin(), pending.end(), laterFirst);
3492 const Span span = std::move(pending.back());
3495 const Parameter* frontier = span.low ? &*span.low :
nullptr;
3496 if (fn(HalfedgeId(), frontier)) {
3500 const Node& node = nodes_[span.node];
3501 if (node.kind == NodeKind::leaf) {
3505 if (node.kind == NodeKind::x) {
3507 const Abscissa& wall = wallXs_[node.value];
3508 const Parameter splitX(wall.point.x());
3513 if (wall.epsInfinity != 0) {
3517 below = narrow(low, splitX - x0, -dx,
true);
3518 above = narrow(high, x0 - splitX, dx,
true);
3520 const Parameter splitY(wall.point.y());
3521 below = narrowSheared(low, splitX - x0, -dx, splitY - y0, -dy,
3523 above = narrowSheared(high, x0 - splitX, dx, y0 - splitY, dy,
3527 low.node = node.low;
3528 offer(std::move(low));
3531 high.node = node.high;
3532 offer(std::move(high));
3537 const Curve& curve = curves_[node.value];
3538 const auto [value, rate] = sideAlongQuery(curve);
3540 const bool below = narrow(low, value, rate,
false);
3542 const bool above = narrow(high, value, rate,
true);
3543 if (below && above) {
3546 const HalfedgeId h(2 * (curve.leftToRight.index() / 2));
3547 if (fn(h, frontier)) {
3552 low.node = node.low;
3553 offer(std::move(low));
3556 high.node = node.high;
3557 offer(std::move(high));
3564 static bool less(
const Bound& left,
const Bound& right) {
3565 if (left.infinity != right.infinity) {
3566 return left.infinity < right.infinity;
3568 return left.infinity == 0 && left.wall < right.wall;
3571 static Bound maximum(
const Bound& left,
const Bound& right) {
3572 return less(left, right) ? right : left;
3575 static Bound minimum(
const Bound& left,
const Bound& right) {
3576 return less(left, right) ? left : right;
3579 [[nodiscard]] Bound wallBound(
const PointType& point,
3580 std::int8_t epsInfinity)
const {
3581 const Abscissa key{point, epsInfinity};
3583 std::lower_bound(wallXs_.begin(), wallXs_.end(), key, abscissaLess);
3584 assert(found != wallXs_.end() && !abscissaLess(key, *found));
3585 return Bound{0,
static_cast<std::uint32_t
>(found - wallXs_.begin())};
3590 [[nodiscard]]
int againstWall(
const PointType& point, std::uint32_t wall)
const {
3591 const Abscissa& abscissa = wallXs_[wall];
3592 if (point.x() != abscissa.point.x()) {
3593 return point.x() < abscissa.point.x() ? -1 : 1;
3595 if (abscissa.epsInfinity != 0) {
3596 return abscissa.epsInfinity > 0 ? -1 : 1;
3598 if (point.y() != abscissa.point.y()) {
3599 return point.y() < abscissa.point.y() ? -1 : 1;
3607 [[nodiscard]]
int againstWall(
const DualPoint& point, std::uint32_t wall)
const {
3608 const Abscissa& abscissa = wallXs_[wall];
3609 const WorkNumber
x(abscissa.point.x());
3610 if (point.value.x() != x) {
3611 return point.value.x() <
x ? -1 : 1;
3613 if (abscissa.epsInfinity != 0) {
3614 return abscissa.epsInfinity > 0 ? -1 : 1;
3616 const WorkNumber
y(abscissa.point.y());
3617 const WorkNumber first = point.value.y() + point.epsilon.x();
3619 return first <
y ? -1 : 1;
3621 if (point.epsilon.y() != WorkNumber(0)) {
3622 return point.epsilon.y() < WorkNumber(0) ? -1 : 1;
3627 void makeVertexIndex(
const Arrangement& arrangement) {
3628 vertices_.reserve(arrangement.points_.size());
3629 for (std::uint32_t v = 0; v < arrangement.points_.size(); ++v) {
3630 vertices_.push_back(VertexId(v));
3632 std::sort(vertices_.begin(), vertices_.end(), [&](VertexId left, VertexId right) {
3633 return arrangement.points_[left.index()] < arrangement.points_[right.index()];
3637 void makeWalls(
const Arrangement& arrangement) {
3638 wallXs_.reserve(arrangement.points_.size());
3639 for (
const PointType& point : arrangement.points_) {
3640 wallXs_.push_back(Abscissa{point, 0});
3645 for (
const EdgeGeometry& geometry : arrangement.edgeGeometry_) {
3646 if (geometry.kind == EdgeKind::segment ||
3647 geometry.a.x() != geometry.b.x()) {
3650 const bool upward = geometry.a.y() < geometry.b.y();
3651 if (geometry.kind == EdgeKind::line || upward) {
3652 wallXs_.push_back(Abscissa{geometry.a, 1});
3654 if (geometry.kind == EdgeKind::line || !upward) {
3655 wallXs_.push_back(Abscissa{geometry.a, -1});
3658 std::sort(wallXs_.begin(), wallXs_.end(), abscissaLess);
3659 wallXs_.erase(std::unique(wallXs_.begin(), wallXs_.end(),
3660 [](
const Abscissa& left,
const Abscissa& right) {
3661 return !abscissaLess(left, right) &&
3662 !abscissaLess(right, left);
3665 walls_.resize(wallXs_.size());
3668 void makeCurves(
const Arrangement& arrangement) {
3669 curves_.reserve(arrangement.edgeGeometry_.size());
3670 constructionLines_.reserve(arrangement.edgeGeometry_.size());
3671 for (std::uint32_t edge = 0;
edge < arrangement.edgeGeometry_.size(); ++
edge) {
3672 const EdgeGeometry& geometry = arrangement.edgeGeometry_[
edge];
3673 WorkNumber originalDx = WorkNumber(geometry.b.x()) - WorkNumber(geometry.a.x());
3674 WorkNumber originalDy = WorkNumber(geometry.b.y()) - WorkNumber(geometry.a.y());
3677 const bool forward = geometry.a < geometry.b;
3679 originalDx = -originalDx;
3680 originalDy = -originalDy;
3683 Bound left = Bound::negativeInfinity();
3684 Bound right = Bound::positiveInfinity();
3685 if (geometry.kind == EdgeKind::segment) {
3686 left = wallBound(forward ? geometry.a : geometry.b, 0);
3687 right = wallBound(forward ? geometry.b : geometry.a, 0);
3692 const bool vertical = geometry.a.x() == geometry.b.x();
3693 if (geometry.kind == EdgeKind::ray) {
3694 (forward ? left : right) = wallBound(geometry.a, 0);
3696 (forward ? right : left) =
3697 wallBound(geometry.a, forward ? 1 : -1);
3699 }
else if (vertical) {
3700 left = wallBound(geometry.a, -1);
3701 right = wallBound(geometry.a, 1);
3705 WorkPoint originalA(WorkNumber(geometry.a.x()), WorkNumber(geometry.a.y()));
3706 WorkPoint originalB(WorkNumber(geometry.b.x()), WorkNumber(geometry.b.y()));
3708 std::swap(originalA, originalB);
3712 StoredLine queryLine(std::in_place_type<WorkLine>, originalA, originalB);
3713 if constexpr (is_Rational_v<WorkNumber>) {
3714 if (
auto integral = WorkLine(originalA, originalB)
3715 .
template integralLine<std::int64_t>()) {
3716 queryLine = std::move(*integral);
3719 curves_.push_back(Curve{std::move(queryLine), left, right,
3720 HalfedgeId(2 * edge + (forward ? 0 : 1)),
3721 originalDx, originalDy});
3722 constructionLines_.emplace_back(originalA, originalB);
3729 [[nodiscard]] std::partial_ordering constructionSideOf(
3730 std::uint32_t curve,
const DualPoint& point)
const {
3731 const std::partial_ordering side = std::visit(
3732 [&](
const auto& line) {
3735 curves_[curve].queryLine);
3739 const WorkNumber cross = curves_[curve].originalDx * point.epsilon.y() -
3740 curves_[curve].originalDy * point.epsilon.x();
3741 return cross <=> WorkNumber(0);
3744 [[nodiscard]]
static std::partial_ordering sideOf(
const Curve& curve,
3745 const Query& query) {
3747 [&](
const auto& line) {
3748 using Line = std::remove_cvref_t<
decltype(line)>;
3749 if constexpr (std::same_as<Line, IntegralLine>) {
3750 if (query.integralPoint) {
3752 *query.integralPoint);
3754 if (query.wideIntegralPoint) {
3756 *query.wideIntegralPoint);
3764 [[nodiscard]] Query makeQuery(
const PointType& point)
const {
3765 Query query{&point, std::nullopt, std::nullopt};
3766 classifyQuery(query, point);
3770 void classifyQuery(Query& query,
const PointType& original)
const {
3771 const auto storeIntegral = [&](QueryInteger
x, QueryInteger
y) {
3772 if (detail::representableAs<std::int64_t>(x) &&
3773 detail::representableAs<std::int64_t>(y)) {
3774 query.integralPoint.emplace(detail::narrowTo<std::int64_t>(x),
3775 detail::narrowTo<std::int64_t>(y));
3777 query.wideIntegralPoint.emplace(std::move(x), std::move(y));
3784 if constexpr (is_Rational_v<NumberType>) {
3785 if (original.x().isInteger() && original.y().isInteger()) {
3787 QueryInteger(
static_cast<rational_int_t<NumberType>
>(
3789 QueryInteger(
static_cast<rational_int_t<NumberType>
>(
3792 }
else if constexpr (detail::extended_integral<NumberType> ||
3793 std::same_as<NumberType, BigInt>) {
3794 storeIntegral(QueryInteger(original.x()), QueryInteger(original.y()));
3801 [[nodiscard]] SampleAbscissa sample(
const Bound& left,
const Bound& right)
const {
3802 assert(less(left, right));
3803 const Abscissa* low = left.infinity == 0 ? &wallXs_[left.wall] :
nullptr;
3804 const Abscissa* high = right.infinity == 0 ? &wallXs_[right.wall] :
nullptr;
3805 if (low ==
nullptr && high ==
nullptr) {
3806 return SampleAbscissa{WorkNumber(0), WorkNumber(0)};
3808 if (low ==
nullptr) {
3809 return SampleAbscissa{WorkNumber(high->point.x()) - WorkNumber(1),
3812 if (high ==
nullptr) {
3813 return SampleAbscissa{WorkNumber(low->point.x()) + WorkNumber(1),
3816 const WorkNumber lowX(low->point.x());
3817 const WorkNumber highX(high->point.x());
3818 if (lowX != highX) {
3819 return SampleAbscissa{(lowX + highX) / WorkNumber(2), WorkNumber(0)};
3823 if (low->epsInfinity < 0) {
3824 return SampleAbscissa{lowX, high->epsInfinity > 0
3826 : WorkNumber(high->point.
y()) -
3829 if (high->epsInfinity > 0) {
3830 return SampleAbscissa{lowX, WorkNumber(low->point.y()) + WorkNumber(1)};
3832 return SampleAbscissa{lowX, (WorkNumber(low->point.y()) +
3833 WorkNumber(high->point.y())) /
3841 [[nodiscard]] DualPoint pointAt(std::uint32_t curve,
3842 const SampleAbscissa& x)
const {
3843 const WorkPoint& source = constructionLines_[curve].source();
3844 const WorkNumber& dx = curves_[curve].originalDx;
3845 const WorkNumber& dy = curves_[curve].originalDy;
3846 const WorkPoint zero(WorkNumber(0), WorkNumber(0));
3847 if (dx == WorkNumber(0)) {
3849 assert(
x.a == source.x());
3850 return DualPoint{WorkPoint(source.x(),
x.b), zero};
3852 const WorkNumber along = (
x.a - source.x()) / dx;
3853 const WorkNumber
y = source.y() + along * dy;
3856 const WorkNumber slide =
x.b -
y;
3857 if (slide == WorkNumber(0)) {
3858 return DualPoint{WorkPoint(
x.a, y), zero};
3860 return DualPoint{WorkPoint(
x.a, y), WorkPoint(slide, slide * dy / dx)};
3863 [[nodiscard]]
bool crosses(std::uint32_t curveId,
3864 const Trapezoid& trapezoid)
const {
3865 const Curve& curve = curves_[curveId];
3866 const Bound left = maximum(curve.left, trapezoid.left);
3867 const Bound right = minimum(curve.right, trapezoid.right);
3868 if (!less(left, right)) {
3871 const DualPoint point = pointAt(curveId, sample(left, right));
3872 if (trapezoid.bottom != none &&
3873 constructionSideOf(trapezoid.bottom, point) <= 0) {
3876 if (trapezoid.top != none &&
3877 constructionSideOf(trapezoid.top, point) >= 0) {
3883 [[nodiscard]] std::uint32_t newLeaf(std::uint32_t trapezoid) {
3884 const std::uint32_t
id =
static_cast<std::uint32_t
>(nodes_.size());
3885 nodes_.push_back(Node{NodeKind::leaf, trapezoid, none, none});
3889 [[nodiscard]] std::uint32_t newTrapezoid(Bound left, Bound right,
3890 std::uint32_t bottom,
3891 std::uint32_t top) {
3892 if (trapezoids_.size() >= none || nodes_.size() >= none) {
3893 throw std::length_error(
"Arrangement point-location index exceeds 32-bit capacity");
3895 const std::uint32_t
id =
static_cast<std::uint32_t
>(trapezoids_.size());
3896 trapezoids_.push_back(Trapezoid{std::move(left), std::move(right), bottom, top,
3897 none, FaceId(),
true});
3898 if (trapezoids_.back().left.infinity == 0) {
3899 walls_[trapezoids_.back().left.wall].starts.push_back(
id);
3901 trapezoids_.back().leaf = newLeaf(
id);
3905 [[nodiscard]] std::uint32_t newNode(Node node) {
3906 if (nodes_.size() >= none) {
3907 throw std::length_error(
"Arrangement point-location index exceeds 32-bit capacity");
3909 const std::uint32_t
id =
static_cast<std::uint32_t
>(nodes_.size());
3910 nodes_.push_back(std::move(node));
3914 [[nodiscard]] Node curveNode(std::uint32_t curve, std::uint32_t below,
3915 std::uint32_t above)
const {
3916 return Node{NodeKind::curve, curve, below, above};
3919 [[nodiscard]] Node xNode(
const Bound& bound, std::uint32_t left,
3920 std::uint32_t right)
const {
3921 assert(bound.infinity == 0);
3922 return Node{NodeKind::x, bound.wall, left, right};
3928 [[nodiscard]] SampleAbscissa probeRightOf(
const Bound& left,
3929 const Bound& right)
const {
3930 const std::size_t following =
3931 left.infinity < 0 ? 0 :
static_cast<std::size_t
>(left.wall) + 1;
3932 Bound next = Bound::positiveInfinity();
3933 if (following < wallXs_.size()) {
3934 next = Bound{0,
static_cast<std::uint32_t
>(following)};
3936 return sample(left, minimum(next, right));
3939 [[nodiscard]] std::uint32_t locateTrapezoid(
const DualPoint& point)
const {
3940 std::uint32_t nodeId = root_;
3941 while (nodes_[nodeId].kind != NodeKind::leaf) {
3942 const Node& node = nodes_[nodeId];
3943 if (node.kind == NodeKind::x) {
3944 const int side = againstWall(point, node.value);
3946 nodeId = side < 0 ? node.low : node.high;
3949 const auto side = constructionSideOf(node.value, point);
3951 nodeId = side < 0 ? node.low : node.high;
3953 const std::uint32_t trapezoid = nodes_[nodeId].value;
3954 assert(trapezoids_[trapezoid].active);
3962 [[nodiscard]] std::vector<std::uint32_t> crossedByWalk(
3963 std::uint32_t curveId)
const {
3964 const Curve& curve = curves_[curveId];
3965 const SampleAbscissa probe = probeRightOf(curve.left, curve.right);
3966 std::uint32_t current = locateTrapezoid(pointAt(curveId, probe));
3967 std::vector<std::uint32_t> crossed;
3970 const Trapezoid& trapezoid = trapezoids_[current];
3971 assert(trapezoid.active && crosses(curveId, trapezoid));
3972 crossed.push_back(current);
3974 if (!less(trapezoid.right, curve.right)) {
3977 assert(trapezoid.right.infinity == 0);
3979 std::uint32_t next = none;
3980 for (
const std::uint32_t candidate :
3981 walls_[trapezoid.right.wall].starts) {
3982 const Trapezoid& adjacent = trapezoids_[candidate];
3983 if (adjacent.active && adjacent.left == trapezoid.right &&
3984 crosses(curveId, adjacent)) {
3985 assert(next == none);
3990 throw std::logic_error(
3991 "arrangement edge lost its adjacent trapezoid");
3998 void insertCurve(std::uint32_t curveId) {
3999 const Curve& curve = curves_[curveId];
4000 const std::vector<std::uint32_t> crossed = crossedByWalk(curveId);
4001 if (crossed.empty()) {
4002 throw std::logic_error(
"arrangement edge did not cross its trapezoidal map");
4005 std::uint32_t previousAbove = none;
4006 std::uint32_t previousBelow = none;
4007 for (
const std::uint32_t oldId : crossed) {
4008 const Trapezoid old = trapezoids_[oldId];
4009 const Bound left = maximum(curve.left, old.left);
4010 const Bound right = minimum(curve.right, old.right);
4012 std::uint32_t below;
4013 if (previousBelow != none &&
4014 trapezoids_[previousBelow].bottom == old.bottom &&
4015 trapezoids_[previousBelow].right == left) {
4016 below = previousBelow;
4017 trapezoids_[below].right = right;
4019 below = newTrapezoid(left, right, old.bottom, curveId);
4022 std::uint32_t above;
4023 if (previousAbove != none && trapezoids_[previousAbove].top == old.top &&
4024 trapezoids_[previousAbove].right == left) {
4025 above = previousAbove;
4026 trapezoids_[above].right = right;
4028 above = newTrapezoid(left, right, curveId, old.top);
4031 const bool hasLeftCap = less(old.left, left);
4032 const bool hasRightCap = less(right, old.right);
4033 std::uint32_t leftCap = none;
4034 std::uint32_t rightCap = none;
4036 leftCap = newTrapezoid(old.left, left, old.bottom, old.top);
4039 rightCap = newTrapezoid(right, old.right, old.bottom, old.top);
4042 Node replacement = curveNode(curveId, trapezoids_[below].leaf,
4043 trapezoids_[above].leaf);
4045 const std::uint32_t split = newNode(std::move(replacement));
4046 replacement = xNode(right, split, trapezoids_[rightCap].leaf);
4049 const std::uint32_t split = newNode(std::move(replacement));
4050 replacement = xNode(left, trapezoids_[leftCap].leaf, split);
4052 nodes_[old.leaf] = std::move(replacement);
4053 trapezoids_[oldId].active =
false;
4054 previousBelow = below;
4055 previousAbove = above;
4068 [[nodiscard]] FaceId faceAboveStrip(
const Arrangement& arrangement,
4069 const WorkNumber& abscissa)
const {
4070 const auto beforeStrip = [&](std::uint32_t halfedge) {
4071 const FanDirection end = arrangement.fanDirection(halfedge);
4072 if (directionHalf(end) != 0) {
4075 if (end.dx != NumberType(0)) {
4076 return end.dx > NumberType(0);
4078 return abscissa < WorkNumber(end.anchor.x());
4082 const auto after = std::partition_point(
4083 arrangement.infinityFan_.begin(), arrangement.infinityFan_.end(),
4084 [&](std::uint32_t halfedge) { return !beforeStrip(halfedge); });
4085 const std::uint32_t nextDirection =
4086 after == arrangement.infinityFan_.end() ? arrangement.infinityFan_.front() : *after;
4087 return arrangement.face(HalfedgeId(nextDirection ^ 1));
4100 [[nodiscard]] FaceId emptyFace(
const Arrangement& arrangement,
4101 const Trapezoid& trapezoid)
const {
4102 if (!arrangement.infinity_.valid()) {
4105 return faceAboveStrip(arrangement, sample(trapezoid.left, trapezoid.right).a);
4108 void labelTrapezoids(
const Arrangement& arrangement) {
4109 for (Trapezoid& trapezoid : trapezoids_) {
4110 if (!trapezoid.active) {
4113 if (trapezoid.bottom != none) {
4114 trapezoid.face = arrangement.face(curves_[trapezoid.bottom].leftToRight);
4115 if (trapezoid.top != none) {
4116 assert(trapezoid.face == arrangement.face(
4117 arrangement.twin(curves_[trapezoid.top].leftToRight)));
4119 }
else if (trapezoid.top != none) {
4120 trapezoid.face = arrangement.face(
4121 arrangement.twin(curves_[trapezoid.top].leftToRight));
4123 trapezoid.face = emptyFace(arrangement, trapezoid);
4128 std::uint32_t root_ = none;
4129 std::vector<VertexId> vertices_;
4130 std::vector<Abscissa> wallXs_;
4131 std::vector<Wall> walls_;
4132 std::vector<Curve> curves_;
4135 std::vector<WorkLine> constructionLines_;
4136 std::vector<Trapezoid> trapezoids_;
4137 std::vector<Node> nodes_;
4140 std::vector<PointType> points_;
4143 std::vector<detail::ApproximatePoint> vertexApproximations_;
4145 std::vector<HalfedgeId> outgoing_;
4146 std::vector<std::uint32_t> origin_;
4147 std::vector<std::uint32_t> next_;
4148 std::vector<std::uint32_t> face_;
4149 std::vector<TLabel> edgeLabel_;
4150 std::vector<EdgeGeometry> edgeGeometry_;
4151 std::vector<std::uint32_t> originOffset_;
4152 std::vector<std::uint32_t> originIndex_;
4153 std::vector<HalfedgeId> outerCycle_;
4154 std::vector<std::uint32_t> innerOffset_;
4155 std::vector<HalfedgeId> innerCycle_;
4156 std::vector<TLabel> faceLabel_;
4157 std::vector<bool> unboundedFace_;
4158 std::vector<std::uint32_t> infinityFan_;
4159 std::shared_ptr<const TrapezoidPointLocation> pointLocation_;