593template <
class Po
intType,
class LabelType>
594template<TriangleConcept OtherTriangle>
600template <
class Po
intType,
class LabelType>
601template<ConvexConcept OtherConvex>
603 if (other.isDegenerate()) {
607 if (
const auto spanned = other.getIfSegment()) {
616 return spanned->separates(other);
621 int interior_vertex_count = 0;
623 if (other.interiorContains(
vertex)) {
624 interior_vertex_count++;
625 if (interior_vertex_count >= 2) {
630 if (interior_vertex_count == 1) {
632 if (
edge.separates(other)) {
639 int separating_edge_count = 0;
641 if (
edge.separates(other)) {
642 separating_edge_count++;
645 return separating_edge_count >= 2;
659template <
class Po
intType,
class LabelType>
660template<DiskConcept OtherDisk>
662 if (other.isDegenerate()) {
669 return spanned->separates(other);
674 int interior_vertex_count = 0;
676 interior_vertex_count += other.interiorContains(
vertex) ? 1 : 0;
678 if (interior_vertex_count >= 2) {
682 const int needed = interior_vertex_count == 0 ? 2 : 1;
683 int separating_edge_count = 0;
685 if (
edge.separates(other) && ++separating_edge_count >= needed) {
692template <
class Po
intType,
class LabelType>
695 [
this](
const auto& value) {
707template <
class Po
intType,
class LabelType>
708template<Po
intConcept OtherPo
int>
710 return this->
asSegment().separates(other);
713template <
class Po
intType,
class LabelType>
714template<SegmentConcept OtherSegment>
716 return this->
asSegment().separates(other);
719template <
class Po
intType,
class LabelType>
720template<OrientedSegmentConcept OtherOrientedSegment>
722 return this->
asSegment().separates(other.asSegment());
725template <
class Po
intType,
class LabelType>
726template<LineConcept OtherLine>
728 return this->
asSegment().separates(other);
731template <
class Po
intType,
class LabelType>
732template<OrientedLineConcept OtherOrientedLine>
734 return this->
asSegment().separates(other);
737template <
class Po
intType,
class LabelType>
738template<RayConcept OtherRay>
740 return this->
asSegment().separates(other);
743template <
class Po
intType,
class LabelType>
744template<RectangleConcept OtherRectangle>
750 return this->
asSegment().separates(other);
753template <
class Po
intType,
class LabelType>
754template<TriangleConcept OtherTriangle>
756 return this->
asSegment().separates(other);
759template <
class Po
intType,
class LabelType>
760template<HalfplaneConcept OtherHalfplane>
762 return this->
asSegment().separates(other);
765template <
class Po
intType,
class LabelType>
766template<ConvexConcept OtherConvex>
768 return this->
asSegment().separates(other);
771template <
class Po
intType,
class LabelType>
772template<DiskConcept OtherDisk>
774 return this->
asSegment().separates(other);
777template <
class Po
intType,
class LabelType>
780 [
this](
const auto& value) {
792template <
class Po
intType,
class LabelType>
793template<Po
intConcept OtherPo
int>
798template <
class Po
intType,
class LabelType>
799template<SegmentConcept OtherSegment>
806 return first_side != 0 && second_side != 0 && first_side != second_side;
809template <
class Po
intType,
class LabelType>
810template<OrientedSegmentConcept OtherOrientedSegment>
815template <
class Po
intType,
class LabelType>
816template<LineConcept OtherLine>
822template <
class Po
intType,
class LabelType>
823template<OrientedLineConcept OtherOrientedLine>
828template <
class Po
intType,
class LabelType>
829template<RayConcept OtherRay>
836 if (source_side == 0) {
839 return target_side == 0 || target_side != source_side;
842template <
class Po
intType,
class LabelType>
843template<RectangleConcept OtherRectangle>
849 return other.interiorsIntersect(*
this);
852template <
class Po
intType,
class LabelType>
853template<TriangleConcept OtherTriangle>
858 return other.interiorsIntersect(*
this);
861template <
class Po
intType,
class LabelType>
862template<HalfplaneConcept OtherHalfplane>
864 return other.separates(*
this);
867template <
class Po
intType,
class LabelType>
868template<ConvexConcept OtherConvex>
873 return other.interiorsIntersect(*
this);
876template <
class Po
intType,
class LabelType>
877template<DiskConcept OtherDisk>
882 return other.interiorsIntersect(*
this);
885template <
class Po
intType,
class LabelType>
888 [
this](
const auto& value) {
900template <
class Po
intType,
class LabelType>
901template<Po
intConcept OtherPo
int>
903 return this->
asLine().separates(other);
906template <
class Po
intType,
class LabelType>
907template<SegmentConcept OtherSegment>
909 return this->
asLine().separates(other);
912template <
class Po
intType,
class LabelType>
913template<OrientedSegmentConcept OtherOrientedSegment>
915 return this->
asLine().separates(other);
918template <
class Po
intType,
class LabelType>
919template<LineConcept OtherLine>
921 return this->
asLine().separates(other);
924template <
class Po
intType,
class LabelType>
925template<OrientedLineConcept OtherOrientedLine>
927 return this->
asLine().separates(other);
930template <
class Po
intType,
class LabelType>
931template<RayConcept OtherRay>
933 return this->
asLine().separates(other);
936template <
class Po
intType,
class LabelType>
937template<RectangleConcept OtherRectangle>
943 return this->
asLine().separates(other);
946template <
class Po
intType,
class LabelType>
947template<TriangleConcept OtherTriangle>
949 return this->
asLine().separates(other);
952template <
class Po
intType,
class LabelType>
953template<HalfplaneConcept OtherHalfplane>
955 return this->
asLine().separates(other);
958template <
class Po
intType,
class LabelType>
959template<ConvexConcept OtherConvex>
961 return this->
asLine().separates(other);
964template <
class Po
intType,
class LabelType>
965template<DiskConcept OtherDisk>
967 return this->
asLine().separates(other);
970template <
class Po
intType,
class LabelType>
973 [
this](
const auto& value) {
985template <
class Po
intType,
class LabelType>
986template<Po
intConcept OtherPo
int>
991template <
class Po
intType,
class LabelType>
992template<SegmentConcept OtherSegment>
1000 if (first_side == 0 && second_side == 0) {
1003 if (other.interiorContains(
source())) {
1006 if (first_side == 0 || second_side == 0 || first_side == second_side) {
1016template <
class Po
intType,
class LabelType>
1017template<OrientedSegmentConcept OtherOrientedSegment>
1022template <
class Po
intType,
class LabelType>
1023template<LineConcept OtherLine>
1029template <
class Po
intType,
class LabelType>
1030template<OrientedLineConcept OtherOrientedLine>
1035template <
class Po
intType,
class LabelType>
1036template<RayConcept OtherRay>
1044 if (other.interiorContains(
source())) {
1050 if (other_source_side == 0) {
1053 if (other_target_side != 0 && other_target_side == other_source_side) {
1059 return source_side != 0 && (target_side == 0 || target_side != source_side);
1062template <
class Po
intType,
class LabelType>
1063template<HalfplaneConcept OtherHalfplane>
1065 return other.separates(*
this);
1068template <
class Po
intType,
class LabelType>
1069template<RectangleConcept OtherRectangle>
1071 if (other.empty()) {
1075 return !other.interiorContains(
source()) && other.interiorsIntersect(*
this);
1078template <
class Po
intType,
class LabelType>
1079template<TriangleConcept OtherTriangle>
1085 const auto triangle_edges = other.edges();
1086 for (
const auto&
edge : triangle_edges) {
1092 int boundary_contact_count = 0;
1093 if (other.boundaryContains(
source())) {
1094 ++boundary_contact_count;
1096 const auto triangle_vertices = other.vertices();
1097 for (
const auto&
vertex : triangle_vertices) {
1099 ++boundary_contact_count;
1103 for (
const auto&
edge : triangle_edges) {
1105 ++boundary_contact_count;
1109 return boundary_contact_count >= 2;
1112template <
class Po
intType,
class LabelType>
1113template<ConvexConcept OtherConvex>
1115 if (other.isDegenerate()) {
1119 if (
const auto spanned = other.getIfSegment()) {
1125 if (other.interiorContains(
source())) {
1128 return other.interiorsIntersect(*
this);
1131template <
class Po
intType,
class LabelType>
1132template<DiskConcept OtherDisk>
1139 if (other.isDegenerate()) {
1142 if (other.interiorContains(
source())) {
1145 return other.interiorsIntersect(*
this);
1148template <
class Po
intType,
class LabelType>
1149template<PolygonConcept OtherPolygon>
1157 return detail::lineSectionSeparatesPolygon(
source(),
target(), other,
false);
1160template <
class Po
intType,
class LabelType>
1163 [
this](
const auto& value) {
1175template <
class Po
intType,
class LabelType>
1176template<RectangleConcept OtherRectangle>
1183 const bool splits_horizontally =
1184 other.min().x() <
min().x() &&
1185 max().x() < other.max().x() &&
1186 !(other.min().
y() <
min().
y()) &&
1187 !(
max().y() < other.max().y());
1188 if (splits_horizontally) {
1189 return !
empty() && !other.empty();
1191 const bool splits_vertically =
1192 other.min().y() <
min().y() &&
1193 max().y() < other.max().y() &&
1194 !(other.min().
x() <
min().
x()) &&
1195 !(
max().x() < other.max().x());
1196 return splits_vertically && !
empty() && !other.empty();
1199template <
class Po
intType,
class LabelType>
1200template<Po
intConcept OtherPo
int>
1205template <
class Po
intType,
class LabelType>
1206template<LineConcept OtherLine>
1215template <
class Po
intType,
class LabelType>
1216template<OrientedLineConcept OtherOrientedLine>
1225template <
class Po
intType,
class LabelType>
1226template<SegmentConcept OtherSegment>
1237template <
class Po
intType,
class LabelType>
1238template<OrientedSegmentConcept OtherOrientedSegment>
1247template <
class Po
intType,
class LabelType>
1248template<RayConcept OtherRay>
1254 return !other.isDegenerate() &&
1259template <
class Po
intType,
class LabelType>
1260template<HalfplaneConcept OtherHalfplane>
1270template <
class Po
intType,
class LabelType>
1271template<TriangleConcept OtherTriangle>
1277 if (other.isDegenerate()) {
1280 if (
const auto spanned = other.getIfSegment()) {
1286 const auto target_edges = other.edges();
1297 int separated_edges = 0;
1298 for (
const auto&
edge : target_edges) {
1301 return separated_edges >= 2;
1304template <
class Po
intType,
class LabelType>
1305template<ConvexConcept OtherConvex>
1311 return asConvex().separates(other);
1322template <
class Po
intType,
class LabelType>
1323template<DiskConcept OtherDisk>
1329 if (other.isDegenerate()) {
1336 return spanned->separates(other);
1341 int interior_vertex_count = 0;
1343 interior_vertex_count += other.interiorContains(
vertex) ? 1 : 0;
1345 if (interior_vertex_count >= 3) {
1349 const int needed = interior_vertex_count == 0 ? 2 : 1;
1350 int separating_edge_count = 0;
1352 if (
edge.separates(other) && ++separating_edge_count >= needed) {
1359template <
class Po
intType,
class LabelType>
1362 [
this](
const auto& value) {
1374template <
class Po
intType,
class LabelType>
1375template<Po
intConcept OtherPo
int>
1380template <
class Po
intType,
class LabelType>
1381template<LineConcept OtherLine>
1387template <
class Po
intType,
class LabelType>
1388template<OrientedLineConcept OtherOrientedLine>
1394template <
class Po
intType,
class LabelType>
1395template<SegmentConcept OtherSegment>
1401template <
class Po
intType,
class LabelType>
1402template<OrientedSegmentConcept OtherOrientedSegment>
1408template <
class Po
intType,
class LabelType>
1409template<RayConcept OtherRay>
1415template <
class Po
intType,
class LabelType>
1416template<RectangleConcept OtherRectangle>
1418 if (other.empty()) {
1426template <
class Po
intType,
class LabelType>
1427template<HalfplaneConcept OtherHalfplane>
1433template <
class Po
intType,
class LabelType>
1434template<TriangleConcept OtherTriangle>
1440template <
class Po
intType,
class LabelType>
1441template<ConvexConcept OtherConvex>
1446template <
class Po
intType,
class LabelType>
1447template<DiskConcept OtherDisk>
1456template <
class Po
intType,
class LabelType>
1457template<PolygonConcept OtherPolygon>
1463 if (other.size() < 3) {
1467 const ptrdiff_t m = other.size();
1469 ptrdiff_t start = 0;
1470 for (; start < m && !
contains(other[start]) ; ++start) {
1482 bool prev_in =
true;
1484 for (ptrdiff_t i = start; i < start+m; ++i) {
1485 const bool cur_in =
contains(other.get(i));
1486 if (prev_in && !cur_in) {
1490 if (cur_in && !prev_in) {
1507template <
class Po
intType,
class LabelType>
1510 [
this](
const auto& value) {
1520template <
class Po
intType,
class LabelType>
1521template<Po
intConcept OtherPo
int>
1526template <
class Po
intType,
class LabelType>
1527template<SegmentConcept OtherSegment>
1535template <
class Po
intType,
class LabelType>
1536template<OrientedSegmentConcept OtherOrientedSegment>
1541template <
class Po
intType,
class LabelType>
1542template<LineConcept OtherLine>
1544 return !other.isDegenerate() &&
intersects(other);
1547template <
class Po
intType,
class LabelType>
1548template<OrientedLineConcept OtherOrientedLine>
1553template <
class Po
intType,
class LabelType>
1554template<RayConcept OtherRay>
1560template <
class Po
intType,
class LabelType>
1561template<HalfplaneConcept OtherHalfplane>
1566template <
class Po
intType,
class LabelType>
1567template<RectangleConcept OtherRectangle>
1569 if (other.empty()) {
1576template <
class Po
intType,
class LabelType>
1577template<TriangleConcept OtherTriangle>
1582template <
class Po
intType,
class LabelType>
1583template<ConvexConcept OtherConvex>
1585 if (other.isDegenerate()) {
1589 if (
const auto spanned = other.getIfSegment()) {
1598 return spanned->separates(other);
1606 if (other.size() <= 2*
size()) {
1607 const ptrdiff_t m = other.size();
1609 bool prev_in =
contains(other[m - 1]);
1610 for (ptrdiff_t i = 0; i < m; ++i) {
1611 const bool cur_in =
contains(other[i]);
1612 if (!prev_in && !cur_in) {
1622 else if (prev_in && !cur_in) {
1634 const ptrdiff_t n =
size();
1636 bool prev_in = other.interiorContains((*
this)[n - 1]);
1637 for (ptrdiff_t i = 0; i < n; ++i) {
1638 const bool cur_in = other.interiorContains((*
this)[i]);
1639 if (!prev_in && !cur_in) {
1642 if (
edge.separates(other)) {
1649 else if (prev_in && !cur_in) {
1664template <
class Po
intType,
class LabelType>
1665template<DiskConcept OtherDisk>
1667 if (other.isDegenerate()) {
1674 return spanned->separates(other);
1690 const bool min_inside = other.interiorContains(
edge.min());
1691 const bool max_inside = other.interiorContains(
edge.max());
1692 if (min_inside != max_inside) {
1694 }
else if (!min_inside &&
edge.separates(other)) {
1697 if (crossings >= 4) {
1710template <
class Po
intType,
class LabelType>
1711template<Po
intConcept OtherPo
int>
1716template <
class Po
intType,
class LabelType>
1717template <SegmentConcept OtherSegment>
1725template <
class Po
intType,
class LabelType>
1726template <OrientedSegmentConcept OtherOrientedSegment>
1731template <
class Po
intType,
class LabelType>
1732template <LineConcept OtherLine>
1740template <
class Po
intType,
class LabelType>
1741template <OrientedLineConcept OtherOrientedLine>
1746template <
class Po
intType,
class LabelType>
1747template <ConvexConcept OtherConvex>
1749 if (other.isDegenerate()) {
1753 if (
const auto spanned = other.getIfSegment()) {
1762 const ptrdiff_t m = other.size();
1764 bool prev_in =
contains(other[m - 1]);
1765 for (ptrdiff_t i = 0; i < m; ++i) {
1766 const bool cur_in =
contains(other[i]);
1767 if (!prev_in && !cur_in) {
1777 else if (prev_in && !cur_in) {
1790template <
class Po
intType,
class LabelType>
1791template<RayConcept OtherRay>
1796template <
class Po
intType,
class LabelType>
1797template<HalfplaneConcept OtherHalfplane>
1802template <
class Po
intType,
class LabelType>
1803template<RectangleConcept OtherRectangle>
1805 if (other.empty()) {
1810 for (
int i = 0; i < 4; i++) {
1814 typename OtherRectangle::PointType opposite1 = other.get(i+2);
1815 typename OtherRectangle::PointType opposite2 = other.get(i+3);
1824template <
class Po
intType,
class LabelType>
1825template<TriangleConcept OtherTriangle>
1828 for (
int i = 0; i < 3; i++) {
1832 typename OtherTriangle::PointType opposite = other.get(i+2);
1841template <
class Po
intType,
class LabelType>
1842template<DiskConcept OtherDisk>
1847template <
class Po
intType,
class LabelType>
1848template <Po
intConcept OtherPo
int>
1851 [
this](
const auto& value) {
1862template <
class Po
intType,
class LabelType>
1863template<Po
intConcept OtherPo
int>
1868template <
class Po
intType,
class LabelType>
1869template<SegmentConcept OtherSegment>
1875 std::optional<pgl::OrientedSegment<PointType>> minSeg, maxSeg;
1880 if (gte && !lte)
return std::partial_ordering::greater;
1881 if (lte && !gte)
return std::partial_ordering::less;
1882 return std::partial_ordering::equivalent;
1885 for (ptrdiff_t i = 0; i < (ptrdiff_t)
size(); ++i) {
1887 if (
edge.separates(other) && !
edge.collinear(other)) {
1888 auto h =
edge.leftHalfplane();
1889 if (h.contains(other[0])) {
1890 if (!maxSeg || crossingOrder(other, *maxSeg,
edge) < 0) {
1891 if (other.contains(
edge[0])) {
1893 if (previous.
collinear(other) && !h.contains(previous))
continue;
1895 if (!previous.
collinear(other) && hprevious.contains(other[1]) && !hprevious.contains(
edge[1]))
continue;
1897 else if (other.contains(
edge[1])) {
1899 if (next.
collinear(other) && !h.contains(next))
continue;
1901 if (!next.
collinear(other) && hnext.contains(other[1]) && !hnext.contains(
edge[0]))
continue;
1904 if (minSeg && maxSeg && crossingOrder(other, *minSeg, *maxSeg) <= 0) {
1910 if (!minSeg || crossingOrder(other, *minSeg,
edge) > 0) {
1911 if (other.contains(
edge[0])) {
1913 if (previous.
collinear(other) && !h.contains(previous))
continue;
1915 if (!previous.
collinear(other) && hprevious.contains(other[0]) && !hprevious.contains(
edge[1]))
continue;
1917 else if (other.contains(
edge[1])) {
1919 if (next.
collinear(other) && !h.contains(next))
continue;
1921 if (!next.
collinear(other) && hnext.contains(other[0]) && !hnext.contains(
edge[0]))
continue;
1924 if (minSeg && maxSeg && crossingOrder(other, *minSeg, *maxSeg) <= 0) {
1934template <
class Po
intType,
class LabelType>
1935template<RayConcept OtherRay>
1943 for (ptrdiff_t i = 0; i < (ptrdiff_t)
size(); ++i) {
1945 if (
edge.separates(other) && !
edge.collinear(other)) {
1946 auto h =
edge.leftHalfplane();
1947 if (!h.contains(other.source())) {
1948 if (other.contains(
edge[0])) {
1950 if (previous.
collinear(other) && !h.contains(previous))
continue;
1952 if (!previous.
collinear(other) && hprevious.contains(other[0]) && !hprevious.contains(
edge[1]))
continue;
1954 else if (other.contains(
edge[1])) {
1956 if (next.
collinear(other) && !h.contains(next))
continue;
1958 if (!next.
collinear(other) && hnext.contains(other[0]) && !hnext.contains(
edge[0]))
continue;
1967template <
class Po
intType,
class LabelType>
1968template<LineConcept OtherLine>
1976template <
class Po
intType,
class LabelType>
1977template<OrientedSegmentConcept OtherOrientedSegment>
1982template <
class Po
intType,
class LabelType>
1983template<OrientedLineConcept OtherOrientedLine>
1988template <
class Po
intType,
class LabelType>
1989template<HalfplaneConcept OtherHalfplane>
1995template <
class Po
intType,
class LabelType>
1996template<PolygonConcept OtherPolygon>
1998 return this->
asSegment().separates(other);
2001template <
class Po
intType,
class LabelType>
2002template<PolygonConcept OtherPolygon>
2004 return this->
asLine().separates(other);
2007template <
class Po
intType,
class LabelType>
2008template<PolygonConcept OtherPolygon>
2013 return other.interiorsIntersect(*
this);
2016template <
class Po
intType,
class LabelType>
2017template<PolygonConcept OtherPolygon>
2023 return asConvex().separates(other);
2026template <
class Po
intType,
class LabelType>
2027template<PolygonConcept OtherPolygon>
2029 return asConvex().separates(other);
2032template <
class Po
intType,
class LabelType>
2033template<PolygonConcept OtherPolygon>
2035 if (other.isDegenerate()) {
2038 if (
const auto spanned = other.getIfSegment()) {
2047 return spanned->separates(other);
2074 using CommonNumber = std::common_type_t<
typename PointType::NumberType,
2075 typename OtherPolygon::NumberType>;
2079 const std::ptrdiff_t n =
static_cast<std::ptrdiff_t
>(other.size());
2080 const std::ptrdiff_t m =
static_cast<std::ptrdiff_t
>(
size());
2082 const auto common = [](
const auto& p) {
2083 return CommonPoint(
static_cast<CommonNumber
>(p.x()),
2084 static_cast<CommonNumber
>(p.y()));
2089 const auto perpendicularAt = [&](
const CommonPoint& at, std::ptrdiff_t k) {
2090 const CommonPoint a = common(
get(k));
2091 const CommonPoint b = common(
get(k + 1));
2092 return PosLine(at, CommonPoint(at.x() - (b.y() - a.y()),
2093 at.y() + (b.x() - a.x())));
2097 std::ptrdiff_t cedge;
2100 CommonPoint a, b, c;
2103 std::vector<Event> events;
2111 const auto extremeContact = [&](
const typename OtherPolygon::PointType& pa,
const typename OtherPolygon::PointType& pb,
2112 bool first,
bool skipAtRunVertex)
2113 -> std::optional<std::pair<std::ptrdiff_t, PosLine>> {
2114 const CommonPoint a = common(pa);
2115 const CommonPoint b = common(pb);
2118 std::optional<std::pair<std::ptrdiff_t, PosLine>> best;
2119 std::optional<PosLine> bestAlong;
2120 const auto consider = [&](std::ptrdiff_t k,
const PosLine& where,
2121 const PosLine& along) {
2124 const bool improves =
2125 first ? order == std::partial_ordering::less
2126 : order == std::partial_ordering::greater;
2131 best.emplace(k, where);
2134 for (std::ptrdiff_t k = 0; k < m; ++k) {
2136 if (!
edge.intersects(s)) {
2140 const CommonPoint lo = std::max(s.
min(),
edge.min());
2141 const CommonPoint hi = std::min(s.
max(),
edge.max());
2142 const CommonPoint at = (first == (a < b)) ? lo : hi;
2143 const PosLine across = perpendicularAt(at, k);
2144 consider(k, across, across);
2146 consider(k, PosLine(a, b),
2147 PosLine(common(
get(k)), common(
get(k + 1))));
2150 if (best && skipAtRunVertex) {
2151 const CommonPoint inVertex = first ? b : a;
2152 const PosLine atInVertex(
2153 inVertex, CommonPoint(inVertex.x() - (b.y() - a.y()),
2154 inVertex.y() + (b.x() - a.x())));
2156 std::partial_ordering::equivalent) {
2157 return std::nullopt;
2163 std::vector<char> in(
static_cast<std::size_t
>(n));
2164 std::ptrdiff_t start = -1;
2165 for (std::ptrdiff_t i = 0; i < n; ++i) {
2166 in[
static_cast<std::size_t
>(i)] =
contains(other.get(i));
2167 if (!in[
static_cast<std::size_t
>(i)]) {
2180 const auto addVertexContact = [&](std::ptrdiff_t i) {
2181 const typename OtherPolygon::PointType
vertex = other.get(i);
2185 const CommonPoint v = common(
vertex);
2186 for (std::ptrdiff_t k = 0; k < m; ++k) {
2188 if (
edge.contains(v)) {
2189 const PosLine across = perpendicularAt(v, k);
2190 const Event
event{k, across, component,
2191 common(other.get(i - 1)), v,
2192 common(other.get(i + 1)),
true};
2193 events.push_back(event);
2194 events.push_back(event);
2200 bool previous =
false;
2201 for (std::ptrdiff_t i = start + 1; i <= start + n; ++i) {
2202 const bool current = in[
static_cast<std::size_t
>(i % n)];
2203 const auto u = other.get(i - 1);
2204 const auto v = other.get(i);
2205 if (current && !previous) {
2207 if (
const auto contact = extremeContact(u, v,
true,
true)) {
2208 events.push_back({contact->first, contact->second, component,
2209 common(u), common(v), common(v),
false});
2211 }
else if (!current && previous) {
2212 if (
const auto contact = extremeContact(u, v,
false,
true)) {
2213 events.push_back({contact->first, contact->second, component,
2214 common(u), common(v), common(v),
false});
2220 if (
const auto contact = extremeContact(u, v,
true,
false)) {
2221 events.push_back({contact->first, contact->second, component,
2222 common(u), common(v), common(v),
false});
2224 if (
const auto contact = extremeContact(u, v,
false,
false)) {
2225 events.push_back({contact->first, contact->second, component,
2226 common(u), common(v), common(v),
false});
2230 addVertexContact(i);
2235 if (events.empty()) {
2239 std::sort(events.begin(), events.end(),
2240 [&](
const Event&
x,
const Event&
y) {
2241 if (x.cedge != y.cedge) {
2242 return x.cedge < y.cedge;
2245 common(
get(
x.cedge + 1)));
2247 std::partial_ordering::less;
2252 const auto entersInterior = [&](
const Event& event) {
2253 const std::ptrdiff_t k =
event.cedge;
2255 common(get(k + 1)));
2256 const bool atEdgeEnd =
2257 edgeLine.crossingOrder(event.where,
2258 perpendicularAt(common(get(k + 1)), k)) ==
2259 std::partial_ordering::equivalent;
2260 const CommonPoint from = common(get(k + (atEdgeEnd ? 1 : 0)));
2261 const CommonPoint to = common(get(k + (atEdgeEnd ? 2 : 1)));
2262 const CommonNumber dx = to.x() - from.x();
2263 const CommonNumber dy = to.y() - from.y();
2264 if (!event.atVertex) {
2268 CommonPoint(event.a.x() + dx,
2269 event.a.y() + dy)) > 0;
2273 const CommonPoint probe(event.b.x() + dx, event.b.y() + dy);
2277 ? (sideIn > 0 && sideOut > 0)
2278 : (sideIn > 0 || sideOut > 0);
2281 const std::size_t count = events.size();
2282 for (std::size_t t = 0; t < count; ++t) {
2283 const Event& current = events[t];
2284 const Event& next = events[(t + 1) % count];
2285 if (current.component != next.component && entersInterior(current)) {
2292template <
class Po
intType,
class LabelType>
2293template<PolygonConcept OtherPolygon>
2304 if (other.isConvex()) {
2305 const std::ptrdiff_t m =
static_cast<std::ptrdiff_t
>(other.size());
2307 bool prev_in =
contains(other[m - 1]);
2308 for (std::ptrdiff_t i = 0; i < m; ++i) {
2309 const bool cur_in =
contains(other[i]);
2310 if (!prev_in && !cur_in) {
2315 }
else if (prev_in && !cur_in) {
2350 const auto mesh = other.triangulation();
2351 using Mesh = std::decay_t<
decltype(mesh)>;
2352 using TriId =
typename Mesh::TriId;
2353 using VertexId =
typename Mesh::VertexId;
2356 std::vector<std::size_t> parent;
2357 const auto findRoot = [&parent](std::size_t
x) {
2358 while (parent[
x] !=
x) {
2359 parent[
x] = parent[parent[
x]];
2364 const auto unite = [&](std::size_t
a, std::size_t
b) {
2365 parent[findRoot(
a)] = findRoot(
b);
2370 std::vector<char> swallowed(mesh.vertexIndexBound(), 0);
2371 for (
const VertexId v : mesh.vertexIds()) {
2372 swallowed[v.index()] =
contains(mesh.getShape(v)) ? 1 : 0;
2379 enum :
int { WHOLE = 0, NEAR_LOW = 1, NEAR_HIGH = 2 };
2380 using EdgeKey = std::pair<VertexId, VertexId>;
2381 std::map<EdgeKey, std::array<std::ptrdiff_t, 3>> pieceSlots;
2382 const auto slotOf = [&](VertexId
a, VertexId
b,
int piece) {
2383 const EdgeKey key =
a <
b ? EdgeKey{
a,
b} : EdgeKey{
b,
a};
2384 auto [it, inserted] =
2385 pieceSlots.try_emplace(key, std::array<std::ptrdiff_t, 3>{-1, -1, -1});
2386 std::ptrdiff_t& slot = it->second[
static_cast<std::size_t
>(piece)];
2388 slot =
static_cast<std::ptrdiff_t
>(parent.size());
2389 parent.push_back(parent.size());
2391 return static_cast<std::size_t
>(slot);
2394 constexpr std::size_t NO_PIECE = std::numeric_limits<std::size_t>::max();
2395 mesh.visitTriangles([&](TriId t) {
2399 std::size_t firstPiece = NO_PIECE;
2400 std::size_t lastPiece = NO_PIECE;
2401 bool gapBeforeFirst =
false;
2403 const auto emit = [&](std::size_t slot) {
2404 if (lastPiece == NO_PIECE) {
2406 gapBeforeFirst = gap;
2408 unite(lastPiece, slot);
2416 const auto v = mesh.vertices(t);
2417 const bool in[3] = {swallowed[v[0].index()] != 0, swallowed[v[1].index()] != 0,
2418 swallowed[v[2].index()] != 0};
2419 for (
int i = 0; i < 3; ++i) {
2420 const VertexId
a = v[i];
2421 const VertexId
b = v[(i + 1) % 3];
2422 const int nearA =
a <
b ? NEAR_LOW : NEAR_HIGH;
2423 const int nearB = nearA == NEAR_LOW ? NEAR_HIGH : NEAR_LOW;
2424 if (in[i] && in[(i + 1) % 3]) {
2428 emit(slotOf(
a,
b, nearB));
2429 }
else if (in[(i + 1) % 3]) {
2430 emit(slotOf(
a,
b, nearA));
2432 }
else if (
intersects(Seg(mesh.getShape(
a), mesh.getShape(
b)))) {
2433 emit(slotOf(
a,
b, nearA));
2435 emit(slotOf(
a,
b, nearB));
2437 emit(slotOf(
a,
b, WHOLE));
2440 if (firstPiece != NO_PIECE && !gap && !gapBeforeFirst) {
2441 unite(lastPiece, firstPiece);
2445 std::size_t classes = 0;
2446 for (std::size_t i = 0; i < parent.size(); ++i) {
2447 if (parent[i] == i && ++classes >= 2) {
2454template <
class Po
intType,
class LabelType>
2455template<RectangleConcept OtherRectangle>
2457 if (other.empty()) {
2464template <
class Po
intType,
class LabelType>
2465template<TriangleConcept OtherTriangle>
2470template <
class Po
intType,
class LabelType>
2471template<DiskConcept OtherDisk>
2473 if (other.isDegenerate()) {
2480 return spanned->separates(other);
2494 const bool min_inside = other.interiorContains(
edge.min());
2495 const bool max_inside = other.interiorContains(
edge.max());
2496 if (min_inside != max_inside) {
2498 }
else if (!min_inside &&
edge.separates(other)) {
2501 if (crossings >= 4) {
2529 using Common = std::common_type_t<
typename PointType::NumberType,
2530 typename OtherDisk::NumberType>;
2535 const auto dbox = other.
bbox();
2536 Common xlo =
static_cast<Common
>(dbox.min().
x());
2537 Common ylo =
static_cast<Common
>(dbox.min().
y());
2538 Common xhi =
static_cast<Common
>(dbox.max().
x());
2539 Common yhi =
static_cast<Common
>(dbox.max().
y());
2540 for (std::size_t i = 0; i <
size(); ++i) {
2541 const auto v = (*this)[i];
2542 const Common vx =
static_cast<Common
>(v.x());
2543 const Common vy =
static_cast<Common
>(v.y());
2544 xlo = vx < xlo ? vx : xlo;
2545 ylo = vy < ylo ? vy : ylo;
2546 xhi = vx > xhi ? vx : xhi;
2547 yhi = vy > yhi ? vy : yhi;
2549 xlo = xlo - Common{1};
2550 ylo = ylo - Common{1};
2551 xhi = xhi + Common{1};
2552 yhi = yhi + Common{1};
2554 CPoint(xlo, ylo), CPoint(xhi, ylo), CPoint(xhi, yhi), CPoint(xlo, yhi)});
2559 std::vector<CSeg> constraints;
2560 constraints.reserve(
size());
2561 for (std::size_t i = 0; i <
size(); ++i) {
2562 const auto u = (*this)[i];
2563 const auto w =
get(
static_cast<std::ptrdiff_t
>(i) + 1);
2564 constraints.emplace_back(
2565 CPoint(detail::asNumber<Common>(u.x()), detail::asNumber<Common>(u.y())),
2566 CPoint(detail::asNumber<Common>(w.x()), detail::asNumber<Common>(w.y())));
2573 using TriId =
typename std::decay_t<
decltype(mesh)>::TriId;
2578 const std::size_t n = mesh.triangleIndexBound();
2581 std::vector<signed char> state(n, -1);
2582 std::vector<TriId> pending;
2583 for (
const TriId t : mesh.incidentTriangles(mesh.getId(CPoint(xlo, ylo)))) {
2584 if (state[t.index()] < 0) {
2585 state[t.index()] = 1;
2586 pending.push_back(t);
2589 while (!pending.empty()) {
2590 const TriId t = pending.back();
2592 const std::size_t i = t.index();
2593 for (
int k = 0; k < 3; ++k) {
2594 const auto nb = mesh.otherTriangle(t, k);
2598 const std::size_t j = nb->index();
2603 state[j] = mesh.isConstrained(t, k)
2604 ?
static_cast<signed char>(1 - state[i])
2606 pending.push_back(*nb);
2611 std::vector<std::size_t> parent(n);
2612 for (std::size_t i = 0; i < n; ++i) {
2615 const auto findRoot = [&parent](std::size_t
x) {
2616 while (parent[
x] !=
x) {
2617 parent[
x] = parent[parent[
x]];
2623 mesh.visitTriangles([&](TriId t) {
2624 const std::size_t i = t.index();
2625 if (state[i] != 1) {
2628 const auto tri = mesh.getShape(t);
2629 for (
int k = 0; k < 3; ++k) {
2630 const auto nb = mesh.otherTriangle(t, k);
2634 const std::size_t j = nb->index();
2635 if (j < i || state[j] != 1) {
2638 const CSeg e(tri.get(k), tri.get(k + 1));
2639 if (other.intersects(e) &&
2640 (other.interiorsIntersect(e) ||
2641 (!other.contains(e.min()) && !other.contains(e.max())))) {
2642 parent[findRoot(i)] = findRoot(j);
2649 std::size_t classes = 0;
2650 std::vector<char> counted(n, 0);
2651 return mesh.visitTriangles([&](TriId t) {
2652 const std::size_t i = t.index();
2653 if (state[i] != 1 || !other.interiorsIntersect(mesh.getShape(t))) {
2656 const std::size_t root = findRoot(i);
2657 if (counted[root]) {
2661 return ++classes >= 2;
2665template <
class Po
intType,
class LabelType>
2666template<ConvexConcept OtherConvex>
2671template <
class Po
intType,
class LabelType>
2672template<PolygonConcept OtherPolygon>
2683 if (other.isDegenerate()) {
2686 if (
const auto spanned = other.getIfSegment()) {
2695 return spanned->separates(other);
2717 const auto toR = [](
const auto& p) {
2718 return RPoint(Number(p.x()), Number(p.y()));
2724 std::vector<RPolygon> pieces;
2726 if (std::holds_alternative<RPolygon>(piece)) {
2727 pieces.push_back(std::get<RPolygon>(piece));
2730 if (pieces.empty()) {
2738 const int k =
static_cast<int>(pieces.size());
2739 std::vector<int> parent(k);
2740 for (
int i = 0; i < k; ++i) {
2743 const auto find = [&parent](
int x) {
2744 while (parent[
x] !=
x) {
2745 parent[
x] = parent[parent[
x]];
2750 std::vector<std::pair<RPoint, int>> vertexOwner;
2751 for (
int i = 0; i < k; ++i) {
2752 for (
const auto& v : pieces[i].
vertices()) {
2753 vertexOwner.emplace_back(v, i);
2756 std::sort(vertexOwner.begin(), vertexOwner.end(),
2757 [](
const auto& a,
const auto& b) { return a.first < b.first; });
2758 for (std::size_t i = 1; i < vertexOwner.size(); ++i) {
2759 if (vertexOwner[i].first == vertexOwner[i - 1].first) {
2760 parent[find(vertexOwner[i].second)] = find(vertexOwner[i - 1].second);
2766 const auto rootsAt = [&](
const RPoint& q) {
2767 std::vector<int> roots;
2768 for (
int i = 0; i < k; ++i) {
2770 const int r = find(i);
2771 if (std::find(roots.begin(), roots.end(), r) == roots.end()) {
2783 std::vector<RPoint> breakpoints;
2784 for (
const auto& piece : pieces) {
2785 for (
const auto& v : piece.vertices()) {
2786 breakpoints.push_back(v);
2794 std::vector<std::vector<int>> elements;
2795 const std::ptrdiff_t n =
static_cast<std::ptrdiff_t
>(other.size());
2796 for (std::ptrdiff_t i = 0; i < n; ++i) {
2797 const RPoint a = toR(other.get(i));
2798 const RPoint b = toR(other.get(i + 1));
2800 std::vector<RPoint> cuts;
2801 const RSegment
edge(a, b);
2802 for (
const auto& bp : breakpoints) {
2803 if (
edge.interiorContains(bp)) {
2807 std::sort(cuts.begin(), cuts.end(), [&](
const RPoint& u,
const RPoint& v) {
2808 return dotSign(u, v, a, b) > 0;
2810 cuts.erase(std::unique(cuts.begin(), cuts.end()), cuts.end());
2812 std::vector<RPoint> chain;
2814 chain.insert(chain.end(), cuts.begin(), cuts.end());
2817 elements.push_back(rootsAt(a));
2818 for (std::size_t j = 0; j + 1 < chain.size(); ++j) {
2819 const RPoint mid((chain[j].
x() + chain[j + 1].
x()) / Number(2),
2820 (chain[j].
y() + chain[j + 1].
y()) / Number(2));
2821 elements.push_back(rootsAt(mid));
2822 if (j + 1 + 1 < chain.size()) {
2823 elements.push_back(rootsAt(chain[j + 1]));
2830 const std::size_t total = elements.size();
2831 const auto has = [](
const std::vector<int>& s,
int r) {
2832 return std::find(s.begin(), s.end(), r) != s.end();
2834 std::vector<int> roots;
2835 for (
const auto& s : elements) {
2837 if (std::find(roots.begin(), roots.end(), r) == roots.end()) {
2842 for (
int r : roots) {
2844 for (std::size_t i = 0; i < total; ++i) {
2845 const std::size_t prev = (i + total - 1) % total;
2846 if (has(elements[i], r) && !has(elements[prev], r)) {
2869template <
class Po
intType,
class LabelType,
class Storage>
2870template <
class OtherShape,
class TouchesBoundary>
2871constexpr bool MonotoneChain<PointType, LabelType, Storage>::separatesOneDimensional(
2872 const OtherShape& other, TouchesBoundary touchesBoundary)
const {
2873 if (points_.empty()) {
2876 if (points_.size() == 1) {
2878 return other.interiorContains((*
this)[0]);
2884 bool active =
false;
2885 bool touched =
false;
2886 for (std::size_t i = 0; i + 1 < points_.size(); ++i) {
2887 const auto edge = this->
template boundaryAt<false>(i);
2888 if (
edge.intersects(other)) {
2889 const bool connected = active && other.contains((*
this)[i]);
2891 if (active && !touched) {
2897 if (touchesBoundary(edge)) {
2901 if (active && !touched) {
2908 return active && !touched;
2911template <
class Po
intType,
class LabelType,
class Storage>
2912template <
bool OtherIsConvex,
class OtherShape>
2913constexpr bool MonotoneChain<PointType, LabelType, Storage>::separatesTwoDimensional(
const OtherShape& other)
const {
2914 if (points_.size() <= 1) {
2934 bool a_check =
false;
2935 bool b_check =
false;
2936 bool c_check = !other.interiorContains((*
this)[size() - 1]);
2937 const auto pattern_found = [&](
bool stop_is_interior) {
2939 a_check = !stop_is_interior;
2941 else if (!b_check) {
2942 b_check = stop_is_interior;
2944 else if (!c_check) {
2945 c_check = !stop_is_interior;
2947 return a_check && b_check && c_check;
2950 bool prev_interior = other.interiorContains((*
this)[0]);
2951 pattern_found(prev_interior);
2952 for (std::size_t i = 1; i < size(); ++i) {
2953 const Segment<PointType>
edge((*
this)[i - 1], (*
this)[i]);
2954 if (
edge.separates(other)) {
2957 const bool cur_interior = other.interiorContains((*
this)[i]);
2958 if constexpr (!OtherIsConvex) {
2959 if (prev_interior && cur_interior && !other.interiorContains(edge) &&
2960 pattern_found(
false)) {
2964 if (pattern_found(cur_interior)) {
2967 prev_interior = cur_interior;
2972template <
class Po
intType,
class LabelType,
class Storage>
2973template<SegmentConcept OtherSegment>
2975 return separatesOneDimensional(other, [&other](
const auto&
edge) {
2976 return edge.contains(other.min()) ||
edge.contains(other.max());
2980template <
class Po
intType,
class LabelType,
class Storage>
2981template<OrientedSegmentConcept OtherOrientedSegment>
2986template <
class Po
intType,
class LabelType,
class Storage>
2987template<LineConcept OtherLine>
2994template <
class Po
intType,
class LabelType,
class Storage>
2995template<OrientedLineConcept OtherOrientedLine>
3000template <
class Po
intType,
class LabelType,
class Storage>
3001template<RayConcept OtherRay>
3003 return separatesOneDimensional(other, [&other](
const auto&
edge) {
3004 return edge.contains(other.source());
3008template <
class Po
intType,
class LabelType,
class Storage>
3009template<HalfplaneConcept OtherHalfplane>
3015 return separatesTwoDimensional(other);
3018template <
class Po
intType,
class LabelType,
class Storage>
3019template<RectangleConcept OtherRectangle>
3021 if (other.empty()) {
3031 return separatesTwoDimensional(other);
3034template <
class Po
intType,
class LabelType,
class Storage>
3035template<TriangleConcept OtherTriangle>
3043 return separatesTwoDimensional(other);
3046template <
class Po
intType,
class LabelType,
class Storage>
3047template<DiskConcept OtherDisk>
3052 return separatesTwoDimensional(other);
3055template <
class Po
intType,
class LabelType,
class Storage>
3056template<ConvexConcept OtherConvex>
3064 return separatesTwoDimensional(other);
3067template <
class Po
intType,
class LabelType,
class Storage>
3068template<PolygonConcept OtherPolygon>
3078 return separatesTwoDimensional<false>(other);
3081template <
class Po
intType,
class LabelType,
class Storage>
3082template<MonotoneChainConcept OtherChain>
3099 bool a_check =
false;
3100 bool b_check =
false;
3101 bool c_check = !
contains(other.get(-1));
3103 for (std::size_t i = 1; i < other.size(); ++i) {
3114 else if (!b_check) {
3117 else if (!c_check) {
3121 if (a_check && b_check && c_check) {
3130template <
class Po
intType,
class LabelType,
class Storage>
3131template<Po
intConcept OtherPo
int>
3134 [
this](
const auto& value) {
3140template <
class Number,
class Label>
3141template<MonotoneChainConcept OtherChain>
3144 return other.interiorContains(*
this);
3147template <
class Po
intType,
class LabelType>
3148template<MonotoneChainConcept OtherChain>
3150 return detail::separatesChain(*
this, other);
3153template <
class Po
intType,
class LabelType>
3154template<MonotoneChainConcept OtherChain>
3156 return detail::separatesChain(
asSegment(), other);
3159template <
class Po
intType,
class LabelType>
3160template<MonotoneChainConcept OtherChain>
3162 return detail::separatesChain(*
this, other);
3165template <
class Po
intType,
class LabelType>
3166template<MonotoneChainConcept OtherChain>
3168 return detail::separatesChain(*
this, other);
3171template <
class Po
intType,
class LabelType>
3172template<MonotoneChainConcept OtherChain>
3174 return detail::separatesChain(*
this, other);
3177template <
class Po
intType,
class LabelType>
3178template<MonotoneChainConcept OtherChain>
3180 return detail::separatesChain(*
this, other);
3183template <
class Po
intType,
class LabelType>
3184template<MonotoneChainConcept OtherChain>
3190 return detail::separatesChain(*
this, other);
3193template <
class Po
intType,
class LabelType>
3194template<MonotoneChainConcept OtherChain>
3196 return detail::separatesChain(*
this, other);
3199template <
class Po
intType,
class LabelType>
3200template<MonotoneChainConcept OtherChain>
3202 return detail::separatesChain(*
this, other);
3205template <
class Po
intType,
class LabelType>
3206template<MonotoneChainConcept OtherChain>
3208 return detail::separatesChain(*
this, other);
3211template <
class Po
intType,
class LabelType>
3212template<MonotoneChainConcept OtherChain>
3229 bool a_check =
false;
3230 bool b_check =
false;
3231 bool c_check = !
contains(other.get(-1));
3233 for (std::size_t i = 1; i < other.size(); ++i) {
3244 else if (!b_check) {
3247 else if (!c_check) {
3251 if (a_check && b_check && c_check) {
3291template <
class Remover,
class TargetEdgeRange>
3292bool separates1DSet(
const Remover& remover,
const TargetEdgeRange& targetEdges) {
3293 using TargetEdge = std::remove_cvref_t<std::ranges::range_value_t<TargetEdgeRange>>;
3294 using TargetNumber =
typename TargetEdge::PointType::NumberType;
3295 using RemoverNumber =
typename Remover::NumberType;
3296 constexpr bool approximate = std::is_floating_point_v<TargetNumber> ||
3297 std::is_floating_point_v<RemoverNumber>;
3298 using ExactNumber = std::conditional_t<approximate, double, Rational<BigInt>>;
3311 std::vector<Piece> pieces;
3313 for (
const auto& edge : targetEdges) {
3314 if (edge.min() == edge.max()) {
3319 std::vector<std::pair<ExactPoint, ExactPoint>> removed;
3320 const auto addRemoved = [&removed](
const auto& optionalPiece) {
3321 if (!optionalPiece) {
3324 if (
const auto* point = std::get_if<0>(&*optionalPiece)) {
3325 removed.emplace_back(ExactPoint(*point), ExactPoint(*point));
3327 const auto& overlap = std::get<1>(*optionalPiece);
3328 removed.emplace_back(ExactPoint(overlap.min()), ExactPoint(overlap.max()));
3331 if constexpr (is_point_v<Remover>) {
3332 if (
edge.contains(remover)) {
3333 removed.emplace_back(ExactPoint(remover), ExactPoint(remover));
3335 }
else if constexpr (is_segment_v<Remover>) {
3336 addRemoved(
edge.template intersection<ExactNumber>(remover));
3337 }
else if constexpr (is_halfplane_intersection_v<Remover>) {
3340 addRemoved(remover.template intersection<ExactNumber>(edge));
3341 }
else if constexpr (is_polygon_v<Remover>) {
3344 for (
const auto& piece : remover.template intersection<ExactNumber>(edge)) {
3345 if (
const auto* point = std::get_if<0>(&piece)) {
3346 removed.emplace_back(ExactPoint(*point), ExactPoint(*point));
3348 const auto& overlap = std::get<1>(piece);
3349 removed.emplace_back(ExactPoint(overlap.min()), ExactPoint(overlap.max()));
3353 static_assert(is_polyline_v<Remover> || is_monotone_chain_v<Remover>,
3354 "unsupported remover kind");
3355 if (remover.size() == 1) {
3356 if (
edge.contains(remover[0])) {
3357 removed.emplace_back(ExactPoint(remover[0]), ExactPoint(remover[0]));
3360 for (
const auto& removerEdge : remover.edgesView()) {
3361 addRemoved(
edge.template intersection<ExactNumber>(removerEdge));
3365 std::sort(removed.begin(), removed.end());
3369 ExactPoint cursor(
edge.min());
3370 bool cursorOpen =
false;
3372 while (i < removed.size()) {
3373 const ExactPoint blockLo = removed[i].first;
3374 ExactPoint blockHi = removed[i].second;
3375 for (++i; i < removed.size() && !(blockHi < removed[i].first); ++i) {
3376 if (blockHi < removed[i].second) {
3377 blockHi = removed[i].second;
3380 if (cursor < blockLo) {
3381 pieces.push_back({cursor, blockLo, cursorOpen,
true});
3386 const ExactPoint edgeHi(
edge.max());
3387 if (cursor < edgeHi) {
3388 pieces.push_back({cursor, edgeHi, cursorOpen,
false});
3392 if (pieces.size() < 2) {
3398 std::vector<std::size_t> parent(pieces.size());
3399 for (std::size_t i = 0; i < parent.size(); ++i) {
3402 const auto findRoot = [&parent](std::size_t
x) {
3403 while (parent[x] != x) {
3404 parent[
x] = parent[parent[
x]];
3409 const auto connected = [](
const Piece& a,
const Piece& b) {
3410 const ExactSegment sa(a.lo, a.hi);
3411 const ExactSegment sb(b.lo, b.hi);
3412 if (!sa.intersects(sb)) {
3415 if (sa.collinear(sb) && sa.min() < sb.max() && sb.min() < sa.max()) {
3423 if (a.loOpen && sb.contains(a.lo)) {
3426 if (a.hiOpen && sb.contains(a.hi)) {
3429 if (b.loOpen && sa.contains(b.lo)) {
3432 if (b.hiOpen && sa.contains(b.hi)) {
3437 for (std::size_t i = 0; i < pieces.size(); ++i) {
3438 for (std::size_t j = i + 1; j < pieces.size(); ++j) {
3439 const std::size_t ri = findRoot(i);
3440 const std::size_t rj = findRoot(j);
3441 if (ri != rj && connected(pieces[i], pieces[j])) {
3446 std::size_t components = 0;
3447 for (std::size_t i = 0; i < pieces.size(); ++i) {
3448 if (findRoot(i) == i) {
3452 return components >= 2;
3457template <
class Po
intType,
class LabelType>
3458template<SegmentConcept OtherSegment>
3465 return other.interiorContains((*
this)[0]);
3467 return detail::separates1DSet(*
this, std::array<OtherSegment, 1>{other});
3470template <
class Po
intType,
class LabelType>
3471template<PolylineConcept OtherPolyline>
3473 if (
empty() || other.size() < 2) {
3480 return (*
this)[0].separates(other);
3482 return detail::separates1DSet(*
this, other.edgesView());
3485template <
class Number,
class Label>
3486template<PolylineConcept OtherPolyline>
3491 if (other.size() < 2) {
3494 return detail::separates1DSet(*
this, other.edgesView());
3497template <
class Po
intType,
class LabelType>
3498template<PolylineConcept OtherPolyline>
3500 if (other.size() < 2) {
3503 return detail::separates1DSet(*
this, other.edgesView());
3524template <
class ExactPo
int,
class PolylineType,
class CutterRange>
3525std::vector<Segment<ExactPoint>> arrangedPolylineEdges(
const PolylineType& polyline,
3526 const CutterRange& cutters) {
3528 using ExactNumber =
typename ExactPoint::NumberType;
3530 std::vector<ExactSegment> edges;
3531 for (
const auto& edge : polyline.edgesView()) {
3533 edges.emplace_back(ExactPoint(
edge.min()), ExactPoint(
edge.max()));
3537 std::vector<ExactSegment> result;
3538 std::vector<ExactPoint> cuts;
3539 for (std::size_t i = 0; i < edges.size(); ++i) {
3541 cuts.push_back(edges[i].min());
3542 cuts.push_back(edges[i].max());
3543 const auto addCuts = [&](
const auto& s) {
3544 const auto piece = edges[i].template intersection<ExactNumber>(s);
3548 if (
const auto* point = std::get_if<0>(&*piece)) {
3549 cuts.push_back(ExactPoint(*point));
3551 const auto& overlap = std::get<1>(*piece);
3552 cuts.push_back(ExactPoint(overlap.min()));
3553 cuts.push_back(ExactPoint(overlap.max()));
3556 for (std::size_t j = 0; j < edges.size(); ++j) {
3561 for (
const auto& cutter : cutters) {
3566 std::sort(cuts.begin(), cuts.end());
3567 cuts.erase(std::unique(cuts.begin(), cuts.end()), cuts.end());
3568 for (std::size_t k = 0; k + 1 < cuts.size(); ++k) {
3569 result.emplace_back(cuts[k], cuts[k + 1]);
3572 std::sort(result.begin(), result.end());
3573 result.erase(std::unique(result.begin(), result.end()), result.end());
3595template <
class Remover,
class PolylineType>
3596bool separatesPolylineSet(
const Remover& remover,
const PolylineType& target) {
3597 if (target.size() < 2) {
3607 if constexpr (
requires { remover.bbox(); }) {
3608 if (!remover.bbox().intersects(target.bbox())) {
3612 using ExactNumber = Exact1DNumber<
typename PolylineType::NumberType,
3613 typename Remover::NumberType>;
3614 using ExactPoint = Point<ExactNumber>;
3615 const auto subEdges = arrangedPolylineEdges<ExactPoint>(
3616 target, std::array<Segment<ExactPoint>, 0>{});
3618 std::map<ExactPoint, std::size_t> nodes;
3619 std::vector<std::size_t> parent;
3620 const auto findRoot = [&parent](std::size_t
x) {
3621 while (parent[x] != x) {
3622 parent[
x] = parent[parent[
x]];
3627 const auto nodeOf = [&](
const ExactPoint& p) {
3628 const auto [it, inserted] = nodes.try_emplace(p, parent.size());
3630 parent.push_back(parent.size());
3635 for (
const auto& s : subEdges) {
3638 const bool minSurvives = !remover.contains(s.min());
3639 const bool maxSurvives = !remover.contains(s.max());
3640 std::optional<std::size_t> a;
3641 std::optional<std::size_t> b;
3643 a = nodeOf(s.min());
3646 b = nodeOf(s.max());
3648 if (a && b && !remover.intersects(s)) {
3649 parent[findRoot(*a)] = findRoot(*b);
3653 std::size_t components = 0;
3654 for (std::size_t i = 0; i < parent.size(); ++i) {
3655 if (findRoot(i) == i && ++components >= 2) {
3682template <
class PolylineType,
class Region>
3683bool polylineSeparatesConvexRegion(
const PolylineType& polyline,
const Region& other) {
3684 if (polyline.size() < 2) {
3690 if constexpr (
requires { other.bbox(); }) {
3691 if (!polyline.bbox().intersects(other.bbox())) {
3695 using ExactNumber = Exact1DNumber<
typename PolylineType::NumberType,
3696 typename Region::NumberType>;
3697 using ExactPoint = Point<ExactNumber>;
3698 const auto subEdges = arrangedPolylineEdges<ExactPoint>(
3699 polyline, std::array<Segment<ExactPoint>, 0>{});
3701 std::map<ExactPoint, std::size_t> nodes;
3702 std::vector<std::size_t> parent(1, 0);
3703 const auto findRoot = [&parent](std::size_t
x) {
3704 while (parent[x] != x) {
3705 parent[
x] = parent[parent[
x]];
3710 const auto nodeOf = [&](
const ExactPoint& p) {
3711 const auto [it, inserted] = nodes.try_emplace(p, parent.size());
3713 parent.push_back(parent.size());
3718 for (
const auto& s : subEdges) {
3719 if (!other.interiorsIntersect(s)) {
3722 const std::size_t a = other.interiorContains(s.min()) ? nodeOf(s.min()) : 0;
3723 const std::size_t b = other.interiorContains(s.max()) ? nodeOf(s.max()) : 0;
3724 const std::size_t ra = findRoot(a);
3725 const std::size_t rb = findRoot(b);
3743template <
class PolylineType,
class PolygonType>
3744bool polylineSeparatesPolygon(
const PolylineType& polyline,
const PolygonType& other) {
3745 if (polyline.size() < 2) {
3748 using ExactNumber = Exact1DNumber<
typename PolylineType::NumberType,
3749 typename PolygonType::NumberType>;
3750 using ExactPoint = Point<ExactNumber>;
3751 using ExactSegment = Segment<ExactPoint>;
3753 std::vector<ExactSegment> cutters;
3754 for (
const auto& edge : other.edgesView()) {
3755 cutters.emplace_back(ExactPoint(
edge.min()), ExactPoint(
edge.max()));
3757 const auto subEdges = arrangedPolylineEdges<ExactPoint>(polyline, cutters);
3759 std::map<ExactPoint, std::size_t> nodes;
3760 std::vector<std::size_t> parent(1, 0);
3761 const auto findRoot = [&parent](std::size_t
x) {
3762 while (parent[x] != x) {
3763 parent[
x] = parent[parent[
x]];
3768 const auto nodeOf = [&](
const ExactPoint& p) {
3769 const auto [it, inserted] = nodes.try_emplace(p, parent.size());
3771 parent.push_back(parent.size());
3776 for (
const auto& s : subEdges) {
3777 const ExactPoint mid((s.min().x() + s.max().x()) / ExactNumber(2),
3778 (s.min().y() + s.max().y()) / ExactNumber(2));
3779 if (!other.interiorContains(mid)) {
3784 const std::size_t a = other.boundaryContains(s.min()) ? 0 : nodeOf(s.min());
3785 const std::size_t b = other.boundaryContains(s.max()) ? 0 : nodeOf(s.max());
3786 const std::size_t ra = findRoot(a);
3787 const std::size_t rb = findRoot(b);
3798template <
class Po
intType,
class LabelType>
3799template<OrientedSegmentConcept OtherOrientedSegment>
3804template <
class Po
intType,
class LabelType>
3805template<LineConcept OtherLine>
3812template <
class Po
intType,
class LabelType>
3813template<OrientedLineConcept OtherOrientedLine>
3818template <
class Po
intType,
class LabelType>
3819template<RayConcept OtherRay>
3826 return other.interiorContains((*
this)[0]);
3828 using ExactNumber = detail::Exact1DNumber<NumberType, typename OtherRay::PointType::NumberType>;
3833 const ExactPoint source(other.source());
3834 const ExactPoint target(other.target());
3835 const ExactNumber dx = target.x() - source.x();
3836 const ExactNumber dy = target.y() - source.y();
3837 if (dx == ExactNumber{} && dy == ExactNumber{}) {
3840 const auto& box =
bbox();
3841 ExactNumber scale(1);
3842 const auto extend = [&scale](
const ExactNumber& s,
const ExactNumber& d,
3843 const ExactNumber& lo,
const ExactNumber& hi) {
3844 if (d > ExactNumber{}) {
3845 const ExactNumber needed = (hi - s) / d + ExactNumber(1);
3846 if (scale < needed) {
3849 }
else if (d < ExactNumber{}) {
3850 const ExactNumber needed = (s - lo) / -d + ExactNumber(1);
3851 if (scale < needed) {
3856 extend(source.x(), dx, ExactNumber(box.min().x()), ExactNumber(box.max().x()));
3857 extend(source.y(), dy, ExactNumber(box.min().y()), ExactNumber(box.max().y()));
3858 const ExactPoint beyond(source.x() + scale * dx, source.y() + scale * dy);
3859 return detail::separates1DSet(
3863template <
class Po
intType,
class LabelType>
3864template<HalfplaneConcept OtherHalfplane>
3866 return detail::polylineSeparatesConvexRegion(*
this, other);
3869template <
class Po
intType,
class LabelType>
3870template<RectangleConcept OtherRectangle>
3872 if (other.empty()) {
3882 return detail::polylineSeparatesConvexRegion(*
this, other);
3885template <
class Po
intType,
class LabelType>
3886template<TriangleConcept OtherTriangle>
3894 return detail::polylineSeparatesConvexRegion(*
this, other);
3897template <
class Po
intType,
class LabelType>
3898template<DiskConcept OtherDisk>
3906 return detail::polylineSeparatesConvexRegion(*
this, other);
3909template <
class Po
intType,
class LabelType>
3910template<ConvexConcept OtherConvex>
3918 return detail::polylineSeparatesConvexRegion(*
this, other);
3921template <
class Po
intType,
class LabelType>
3922template<PolygonConcept OtherPolygon>
3930 return detail::polylineSeparatesPolygon(*
this, other);
3933template <
class Po
intType,
class LabelType>
3934template<MonotoneChainConcept OtherChain>
3936 if (
empty() || other.size() < 2) {
3943 return (*
this)[0].separates(other);
3945 return detail::separates1DSet(*
this, other.edgesView());
3948template <
class Po
intType,
class LabelType>
3949template<Po
intConcept OtherPo
int>
3952 [
this](
const auto& value) {
3958template <
class Po
intType,
class LabelType>
3959template<PolylineConcept OtherPolyline>
3964template <
class Po
intType,
class LabelType>
3965template<PolylineConcept OtherPolyline>
3967 return detail::separatesPolylineSet(*
this, other);
3970template <
class Po
intType,
class LabelType>
3971template<PolylineConcept OtherPolyline>
3973 return asLine().separates(other);
3976template <
class Po
intType,
class LabelType>
3977template<PolylineConcept OtherPolyline>
3979 return detail::separatesPolylineSet(*
this, other);
3982template <
class Po
intType,
class LabelType>
3983template<PolylineConcept OtherPolyline>
3985 return detail::separatesPolylineSet(*
this, other);
3988template <
class Po
intType,
class LabelType>
3989template<PolylineConcept OtherPolyline>
3995 return detail::separatesPolylineSet(*
this, other);
3998template <
class Po
intType,
class LabelType>
3999template<PolylineConcept OtherPolyline>
4001 return detail::separatesPolylineSet(*
this, other);
4004template <
class Po
intType,
class LabelType>
4005template<PolylineConcept OtherPolyline>
4007 return detail::separatesPolylineSet(*
this, other);
4010template <
class Po
intType,
class LabelType>
4011template<PolylineConcept OtherPolyline>
4013 return detail::separatesPolylineSet(*
this, other);
4016template <
class Po
intType,
class LabelType,
class Storage>
4017template<PolylineConcept OtherPolyline>
4019 if (
empty() || other.size() < 2) {
4023 return (*
this)[0].separates(other);
4027 return detail::separates1DSet(*
this, other.edgesView());
4030template <
class Po
intType,
class LabelType>
4031template<PolylineConcept OtherPolyline>
4033 if (other.size() < 2) {
4041 return detail::separates1DSet(*
this, other.edgesView());
4048template <
class Po
intType,
class LabelType>
4049template <Po
intConcept OtherPo
int>
4056template <
class Po
intType,
class LabelType>
4057template <SegmentConcept OtherSegment>
4065template <
class Po
intType,
class LabelType>
4066template <OrientedSegmentConcept OtherOrientedSegment>
4071template <
class Po
intType,
class LabelType>
4072template <LineConcept OtherLine>
4078 if (
empty() || halfplanes_.empty()) {
4082 const auto clip = clipLine(along);
4083 return !clip.empty && clip.entry >= 0 && clip.exit >= 0;
4086template <
class Po
intType,
class LabelType>
4087template <OrientedLineConcept OtherOrientedLine>
4092template <
class Po
intType,
class LabelType>
4093template <RayConcept OtherRay>
4098 if (
empty() || halfplanes_.empty() ||
contains(other.source())) {
4102 const auto clip = clipLine(along);
4103 if (clip.empty || clip.exit < 0) {
4108 return constraintSide(
static_cast<std::size_t
>(clip.exit), other.source()) >= 0;
4111template <
class Po
intType,
class LabelType>
4112template <HalfplaneConcept OtherHalfplane>
4121 if (
empty() || halfplanes_.empty() || halfplanes_.size() == 1) {
4128 const SupStatus infimum = supStatus(other.opposite());
4129 if (infimum == SupStatus::below || infimum == SupStatus::on) {
4134 const bool lineTouch =
intersects(other.asLine());
4135 const auto reversed = other.opposite();
4136 int pieces = lineTouch ? 1 : 0;
4140 for (
const auto& arc : recessionArcs()) {
4143 const bool atLeft = detail::arcContainsDirection(arc.first, arc.second, reversed);
4144 const bool atRight = detail::arcContainsDirection(arc.first, arc.second, other);
4145 const bool strictlyInside = detail::directionCross(other, arc.first) > 0 ||
4146 detail::directionCross(other, arc.second) > 0;
4147 if (!(atLeft || atRight || strictlyInside)) {
4150 if (lineTouch && (atLeft || atRight)) {
4163template <
class Po
intType,
class LabelType>
4164template <RectangleConcept OtherRectangle>
4166 if (other.empty()) {
4173 using E = detail::region_exact_number_t<NumberType>;
4175 return std::visit([&other](
const auto& carrier) {
return carrier.separates(other); },
4176 detail::degenerateRegionCarrier(*
this));
4178 const auto clipped = detail::regionClippedToBox(*
this, other.bbox());
4179 if (!clipped.isBounded()) {
4182 return clipped.template
asConvex<E>().separates(other);
4185template <
class Po
intType,
class LabelType>
4186template <TriangleConcept OtherTriangle>
4191 using E = detail::region_exact_number_t<NumberType>;
4193 return std::visit([&other](
const auto& carrier) {
return carrier.separates(other); },
4194 detail::degenerateRegionCarrier(*
this));
4196 const auto clipped = detail::regionClippedToBox(*
this, other.bbox());
4197 if (!clipped.isBounded()) {
4200 return clipped.template
asConvex<E>().separates(other);
4203template <
class Po
intType,
class LabelType>
4204template <ConvexConcept OtherConvex>
4209 if (other.isDegenerate()) {
4213 if (
const auto spanned = other.getIfSegment()) {
4218 using E = detail::region_exact_number_t<NumberType>;
4220 return std::visit([&other](
const auto& carrier) {
return carrier.separates(other); },
4221 detail::degenerateRegionCarrier(*
this));
4223 const auto clipped = detail::regionClippedToBox(*
this, other.bbox());
4224 if (!clipped.isBounded()) {
4227 return clipped.template
asConvex<E>().separates(other);
4230template <
class Po
intType,
class LabelType>
4231template <DiskConcept OtherDisk>
4233 if (
empty() || other.isDegenerate()) {
4236 using E = detail::region_exact_number_t<NumberType>;
4238 return std::visit([&other](
const auto& carrier) {
return carrier.separates(other); },
4239 detail::degenerateRegionCarrier(*
this));
4241 const auto clipped = detail::regionClippedToBox(*
this, other.bbox());
4242 if (!clipped.isBounded()) {
4245 return clipped.template
asConvex<E>().separates(other);
4248template <
class Po
intType,
class LabelType>
4249template <PolygonConcept OtherPolygon>
4251 if (
empty() || other.size() < 3) {
4254 using E = detail::region_exact_number_t<NumberType>;
4256 return std::visit([&other](
const auto& carrier) {
return carrier.separates(other); },
4257 detail::degenerateRegionCarrier(*
this));
4259 const auto clipped = detail::regionClippedToBox(*
this, other.bbox());
4260 if (!clipped.isBounded()) {
4263 return clipped.template
asConvex<E>().separates(other);
4266template <
class Po
intType,
class LabelType>
4267template <MonotoneChainConcept OtherChain>
4275 return detail::separatesChain(*
this, other);
4278template <
class Po
intType,
class LabelType>
4279template <PolylineConcept OtherPolyline>
4281 if (
empty() || other.size() < 2) {
4284 return detail::separates1DSet(*
this, other.edgesView());
4311template <
class Region,
class Remover>
4312bool boundedRemoverSeparatesRegion(
const Region& region,
const Remover& remover) {
4313 using N =
typename Region::NumberType;
4314 using E = region_exact_number_t<N>;
4316 const auto bounds = remover.
bbox();
4318 const E boxLoX(N(bounds.min().x()) - margin);
4319 const E boxLoY(N(bounds.min().y()) - margin);
4320 const E boxHiX(N(bounds.max().x()) + margin);
4321 const E boxHiY(N(bounds.max().y()) + margin);
4322 const auto clipped = regionClippedToBox(region, bounds);
4323 if (!clipped.isBounded()) {
4326 const auto poly = clipped.template asConvex<E>();
4327 const std::size_t n = poly.size();
4333 std::vector<bool> free;
4334 for (std::size_t i = 0; i < n; ++i) {
4336 const EPoint v(poly[(i + 1) % n]);
4338 (u.x() == boxLoX && v.x() == boxLoX) || (u.x() == boxHiX && v.x() == boxHiX) ||
4339 (u.y() == boxLoY && v.y() == boxLoY) || (u.y() == boxHiY && v.y() == boxHiY);
4341 free.push_back(
true);
4344 const Segment<EPoint>
edge(u, v);
4345 if constexpr (is_polygon_v<Remover>) {
4348 const EPoint dir = v - u;
4349 const E edgeLen = dir.
x() * dir.x() + dir.y() * dir.y();
4350 std::vector<std::pair<E, E>> blocks;
4351 for (
const auto& piece : remover.template intersection<E>(edge)) {
4352 if (
const auto* overlap = std::get_if<1>(&piece)) {
4353 const EPoint lo(overlap->min());
4354 const EPoint hi(overlap->max());
4355 E tLo = (lo - u).
x() * dir.x() + (lo - u).
y() * dir.y();
4356 E tHi = (hi - u).
x() * dir.x() + (hi - u).
y() * dir.y();
4358 std::swap(tLo, tHi);
4361 blocks.emplace_back(tLo, tHi);
4365 std::sort(blocks.begin(), blocks.end());
4367 bool wroteAny =
false;
4368 for (
const auto& block : blocks) {
4369 if (cursor < block.first) {
4370 free.push_back(
true);
4372 free.push_back(
false);
4373 cursor = block.second;
4376 if (!wroteAny || cursor < edgeLen) {
4377 free.push_back(
true);
4381 const bool uIn = remover.contains(u);
4382 const bool vIn = remover.contains(v);
4384 free.push_back(
false);
4386 free.push_back(
false);
4387 free.push_back(
true);
4389 free.push_back(
true);
4390 free.push_back(
false);
4391 }
else if (remover.interiorsIntersect(edge)) {
4392 free.push_back(
true);
4393 free.push_back(
false);
4394 free.push_back(
true);
4396 free.push_back(
true);
4400 const std::size_t m = free.size();
4401 bool anyRemoved =
false;
4402 for (
const bool f : free) {
4403 anyRemoved = anyRemoved || !f;
4410 std::size_t start = 0;
4411 while (free[start]) {
4417 if (!free[(start + i) % m]) {
4421 while (i < m && free[(start + i) % m]) {
4426 return components >= 2;
4433template <
class Shape2,
class Region>
4434bool boundedShapeSeparatesRegion(
const Shape2& remover,
const Region& region) {
4435 if (region.empty()) {
4438 if (region.isDegenerate()) {
4439 return std::visit([&remover](
const auto& carrier) {
return remover.separates(carrier); },
4440 degenerateRegionCarrier(region));
4442 using E = region_exact_number_t<typename Region::NumberType>;
4443 if (region.isBounded()) {
4444 return remover.separates(region.template asConvex<E>());
4446 return boundedRemoverSeparatesRegion(region, remover);
4464template <
class RemoverRegion,
class Region>
4465bool regionSeparatesRegion(
const RemoverRegion& remover,
const Region& region) {
4466 using E = region_exact_number_t<typename RemoverRegion::NumberType>;
4467 using EPoint = Point<E, typename RemoverRegion::PointType::LabelType>;
4468 const HalfplaneIntersection<EPoint> removerExact(remover);
4469 const HalfplaneIntersection<EPoint> regionExact(region);
4471 bool started =
false;
4472 E loX{}, loY{}, hiX{}, hiY{};
4473 const auto include = [&](
const EPoint& point) {
4475 loX = hiX = point.x();
4476 loY = hiY = point.y();
4480 loX = std::min(loX, point.x());
4481 loY = std::min(loY, point.y());
4482 hiX = std::max(hiX, point.x());
4483 hiY = std::max(hiY, point.y());
4486 std::vector<Line<EPoint>> lines;
4487 for (
const auto& regionSide : {&removerExact, ®ionExact}) {
4488 for (
const auto& halfplane : *regionSide) {
4489 include(halfplane.source());
4490 include(halfplane.target());
4491 lines.push_back(halfplane.asLine());
4494 for (std::size_t i = 0; i < lines.size(); ++i) {
4495 for (std::size_t j = i + 1; j < lines.size(); ++j) {
4496 const auto crossing = lines[i].template intersection<E>(lines[j]);
4497 if (crossing && crossing->index() == 0) {
4498 include(std::get<0>(*crossing));
4505 const Rectangle<EPoint> bounds(
EPoint(loX, loY),
EPoint(hiX, hiY));
4506 const auto removerPoly = regionClippedToBox(removerExact, bounds).template asConvex<E>();
4507 const auto regionPoly = regionClippedToBox(regionExact, bounds).template asConvex<E>();
4508 return removerPoly.separates(regionPoly);
4513template <
class Po
intType,
class LabelType>
4514template <HalfplaneIntersectionConcept OtherRegion>
4520 if (
empty() || other.empty()) {
4523 using E = detail::region_exact_number_t<NumberType>;
4526 const ERegion otherExact(other);
4528 [&otherExact](
const auto& carrier) {
return carrier.separates(otherExact); },
4529 detail::degenerateRegionCarrier(*
this));
4531 if (other.isDegenerate()) {
4532 const ERegion selfExact(*
this);
4534 [&selfExact](
const auto& carrier) {
return selfExact.separates(carrier); },
4535 detail::degenerateRegionCarrier(other));
4537 return detail::regionSeparatesRegion(*
this, other);
4540template <
class Po
intType,
class LabelType>
4541template <Po
intConcept OtherPo
int>
4544 [
this](
const auto& value) {
4550template <
class Number,
class Label>
4551template <HalfplaneIntersectionConcept OtherRegion>
4554 if (other.empty() || !other.isDegenerate()) {
4557 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4558 detail::degenerateRegionCarrier(other));
4561template <
class Po
intType,
class LabelType>
4562template <HalfplaneIntersectionConcept OtherRegion>
4564 if (other.empty()) {
4567 if (other.isDegenerate()) {
4569 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4570 detail::degenerateRegionCarrier(other));
4580 !other.interiorContains(
max()) && other.interiorsIntersect(*
this);
4583template <
class Po
intType,
class LabelType>
4584template <HalfplaneIntersectionConcept OtherRegion>
4589template <
class Po
intType,
class LabelType>
4590template <HalfplaneIntersectionConcept OtherRegion>
4592 if (other.empty()) {
4595 if (other.isDegenerate()) {
4596 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4597 detail::degenerateRegionCarrier(other));
4601 return detail::regionStrictlyOnBothSides(other, (*
this)[0], (*
this)[1]);
4604template <
class Po
intType,
class LabelType>
4605template <HalfplaneIntersectionConcept OtherRegion>
4607 return asLine().separates(other);
4610template <
class Po
intType,
class LabelType>
4611template <HalfplaneIntersectionConcept OtherRegion>
4613 if (other.empty()) {
4616 if (other.isDegenerate()) {
4617 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4618 detail::degenerateRegionCarrier(other));
4625 other.interiorsIntersect(*
this);
4628template <
class Po
intType,
class LabelType>
4629template <HalfplaneIntersectionConcept OtherRegion>
4637template <
class Po
intType,
class LabelType>
4638template <HalfplaneIntersectionConcept OtherRegion>
4647 return detail::boundedShapeSeparatesRegion(*
this, other);
4650template <
class Po
intType,
class LabelType>
4651template <HalfplaneIntersectionConcept OtherRegion>
4653 return detail::boundedShapeSeparatesRegion(*
this, other);
4656template <
class Po
intType,
class LabelType>
4657template <HalfplaneIntersectionConcept OtherRegion>
4662 return detail::boundedShapeSeparatesRegion(*
this, other);
4665template <
class Po
intType,
class LabelType>
4666template <HalfplaneIntersectionConcept OtherRegion>
4671 return detail::boundedShapeSeparatesRegion(*
this, other);
4674template <
class Po
intType,
class LabelType,
class Storage>
4675template <HalfplaneIntersectionConcept OtherRegion>
4677 if (other.empty()) {
4680 if (other.isDegenerate()) {
4681 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4682 detail::degenerateRegionCarrier(other));
4688 return separatesTwoDimensional(other);
4691template <
class Po
intType,
class LabelType>
4692template <HalfplaneIntersectionConcept OtherRegion>
4694 if (other.empty() ||
size() < 2) {
4697 if (other.isDegenerate()) {
4698 return std::visit([
this](
const auto& carrier) {
return this->
separates(carrier); },
4699 detail::degenerateRegionCarrier(other));
4703 return detail::polylineSeparatesConvexRegion(*
this, other);
4706template <
class Po
intType,
class LabelType>
4707template <HalfplaneIntersectionConcept OtherRegion>
4712 return detail::boundedShapeSeparatesRegion(*
this, other);
4757template <
class ExactPo
int,
class Shape>
4759 const auto add = [&out](
const auto&
edge) {
4760 const ExactPoint lo(
edge.min());
4761 const ExactPoint hi(
edge.max());
4763 out.emplace_back(lo, hi);
4766 if constexpr (is_point_v<Shape>) {
4769 }
else if constexpr (is_segment_v<Shape>) {
4771 }
else if constexpr (
requires { shape.edgesView(); }) {
4772 for (
const auto& edge : shape.edgesView()) {
4779 for (
const auto& edge : shape.edges()) {
4786template <
class ExactPo
int,
class Shape>
4787void appendCutPoints(
const Shape& shape, std::vector<ExactPoint>& out) {
4788 if constexpr (is_point_v<Shape>) {
4789 out.emplace_back(shape);
4790 }
else if constexpr (is_segment_v<Shape>) {
4791 if (shape.min() == shape.max()) {
4792 out.emplace_back(shape.min());
4794 }
else if constexpr (is_monotone_chain_v<Shape> || is_polyline_v<Shape>) {
4795 if (shape.size() == 1) {
4796 out.emplace_back(shape[0]);
4813template <
class Target>
4814bool disconnectedOnItsOwn(
const Target& target) {
4815 if constexpr (is_polygon_set_v<Target>) {
4816 return !target.isConnected();
4851template <
class Target,
class Remover>
4852bool cellSeparates(
const Target& target,
const Remover& remover) {
4853 using ExactNumber = Exact1DNumber<typename Target::NumberType, typename Remover::NumberType>;
4854 using ExactPoint = Point<ExactNumber>;
4855 using ExactSegment = Segment<ExactPoint>;
4861 if (!target.bbox().intersects(remover.bbox())) {
4862 return disconnectedOnItsOwn(target);
4865 std::vector<ExactSegment> cuts;
4866 std::vector<ExactPoint> cutPoints;
4867 appendCutSegments<ExactPoint>(target, cuts);
4868 appendCutSegments<ExactPoint>(remover, cuts);
4869 appendCutPoints<ExactPoint>(target, cutPoints);
4870 appendCutPoints<ExactPoint>(remover, cutPoints);
4879 const Arrangement<ExactPoint> arrangement(cuts, cutPoints);
4880 using FaceHandle =
typename Arrangement<ExactPoint>::FaceId;
4881 using HalfedgeHandle =
typename Arrangement<ExactPoint>::HalfedgeId;
4885 std::vector<std::size_t> parent;
4886 const auto findRoot = [&parent](std::size_t
x) {
4887 while (parent[x] != x) {
4888 parent[
x] = parent[parent[
x]];
4893 constexpr std::size_t dropped = std::numeric_limits<std::size_t>::max();
4894 const auto unite = [&](std::size_t a, std::size_t b) {
4895 if (a != dropped && b != dropped) {
4896 parent[findRoot(a)] = findRoot(b);
4899 const auto cellOf = [&](
const ExactPoint& witness) {
4900 if (!target.contains(witness) || remover.contains(witness)) {
4903 parent.push_back(parent.size());
4904 return parent.size() - 1;
4907 std::vector<std::size_t> faceCell(arrangement.faceCount(), dropped);
4908 for (std::uint32_t i = 0; i < arrangement.faceCount(); ++i) {
4909 const FaceHandle f(i);
4910 if (!arrangement.isUnbounded(f)) {
4911 faceCell[f.index()] = cellOf(arrangement.template witness<ExactNumber>(f));
4919 std::vector<std::size_t> vertexCell(arrangement.vertexCount(), dropped);
4920 for (std::size_t i = 0; i < arrangement.vertexCount(); ++i) {
4921 vertexCell[i] = cellOf(arrangement.vertices()[i]);
4924 std::vector<std::size_t> edgeCell(arrangement.edgeCount(), dropped);
4925 for (std::uint32_t i = 0; i < arrangement.halfedgeCount(); ++i) {
4926 const HalfedgeHandle h(i);
4927 const std::size_t
edge = h.index() / 2;
4928 if (h.index() % 2 == 0) {
4929 edgeCell[
edge] = cellOf(arrangement.template witness<ExactNumber>(h));
4931 const std::size_t side = edgeCell[
edge];
4932 const std::size_t corner = vertexCell[arrangement.source(h).index()];
4933 const std::size_t face = faceCell[arrangement.face(h).index()];
4934 unite(side, corner);
4936 unite(face, corner);
4939 std::size_t components = 0;
4940 for (std::size_t i = 0; i < parent.size(); ++i) {
4941 if (findRoot(i) == i && ++components >= 2) {
4978template <
class ExactPo
int,
class Linear,
class Region>
4979std::optional<Segment<ExactPoint>> clipLinearToRegion(
const Linear& linear,
const Region& region) {
4980 using ExactNumber =
typename ExactPoint::NumberType;
4981 const auto bounds = region.bbox();
4982 const ExactNumber margin(1);
4983 const Rectangle<ExactPoint> box(
4984 ExactPoint(ExactNumber(bounds.min().x()) - margin, ExactNumber(bounds.min().y()) - margin),
4985 ExactPoint(ExactNumber(bounds.max().x()) + margin, ExactNumber(bounds.max().y()) + margin));
4986 const auto piece = box.template intersection<ExactNumber>(linear);
4988 return std::nullopt;
4990 const auto* overlap = std::get_if<1>(&*piece);
4992 return std::nullopt;
5003template <
bool LinearIsTarget,
class Linear,
class Region>
5004bool linearAndRegionSeparate(
const Linear& linear,
const Region& region) {
5005 using ExactNumber = Exact1DNumber<typename Linear::NumberType, typename Region::NumberType>;
5006 using ExactPoint = Point<ExactNumber>;
5007 const auto clip = clipLinearToRegion<ExactPoint>(linear, region);
5011 return LinearIsTarget ? false : disconnectedOnItsOwn(region);
5013 if constexpr (LinearIsTarget) {
5014 return cellSeparates(*clip, region);
5016 return cellSeparates(region, *clip);
5025template <
bool ConvexIsTarget,
class ConvexOperand,
class Region>
5026bool convexAndRegionSeparate(
const ConvexOperand& convex,
const Region& region) {
5027 using ExactNumber = Exact1DNumber<typename ConvexOperand::NumberType, typename Region::NumberType>;
5028 using ExactPoint = Point<ExactNumber>;
5029 HalfplaneIntersection<ExactPoint> clipped(convex);
5030 if (!clipped.isBounded()) {
5031 clipped = regionClippedToBox(clipped, region.bbox());
5033 const auto run = [®ion](
const auto& operand) {
5034 if constexpr (ConvexIsTarget) {
5035 return cellSeparates(operand, region);
5037 return cellSeparates(region, operand);
5040 if (clipped.empty()) {
5043 return ConvexIsTarget ? false : disconnectedOnItsOwn(region);
5045 if (clipped.isDegenerate()) {
5047 [&run](
const auto& carrier) {
5048 using Carrier = std::remove_cvref_t<
decltype(carrier)>;
5049 if constexpr (is_point_v<Carrier> || is_segment_v<Carrier>) {
5050 return run(carrier);
5055 degenerateRegionCarrier(clipped));
5057 return run(clipped.template asConvex<ExactNumber>().asPolygon());
5087template <
class ExactPo
int>
5088std::vector<Segment<ExactPoint>> splitCutSegments(
const std::vector<Segment<ExactPoint>>& cuts) {
5090 const Arrangement<ExactPoint> arrangement(cuts);
5091 return arrangement.boundedEdges();
5109template <
class Region,
class OtherDisk>
5110bool regionSeparatesDisk(
const Region& region,
const OtherDisk& disk) {
5111 using ExactNumber = Exact1DNumber<typename Region::NumberType, typename OtherDisk::NumberType>;
5112 using ExactPoint = Point<ExactNumber>;
5113 using ExactSegment = Segment<ExactPoint>;
5118 if (!region.bbox().intersects(disk.bbox())) {
5122 std::vector<ExactSegment> cuts;
5123 appendCutSegments<ExactPoint>(region, cuts);
5132 const auto regionBox = region.bbox();
5133 const auto diskBox = disk.bbox();
5134 const ExactNumber margin(1);
5135 const ExactNumber loX =
5136 std::min(ExactNumber(regionBox.min().x()), ExactNumber(diskBox.min().x())) - margin;
5137 const ExactNumber loY =
5138 std::min(ExactNumber(regionBox.min().y()), ExactNumber(diskBox.min().y())) - margin;
5139 const ExactNumber hiX =
5140 std::max(ExactNumber(regionBox.max().x()), ExactNumber(diskBox.max().x())) + margin;
5141 const ExactNumber hiY =
5142 std::max(ExactNumber(regionBox.max().y()), ExactNumber(diskBox.max().y())) + margin;
5143 const Polygon<ExactPoint> box(std::vector<ExactPoint>{
5144 ExactPoint(loX, loY), ExactPoint(hiX, loY), ExactPoint(hiX, hiY), ExactPoint(loX, hiY)});
5145 const auto mesh = box.triangulation(splitCutSegments(cuts));
5149 const Disk<ExactPoint> exactDisk(disk);
5151 std::vector<std::size_t> parent;
5152 const auto findRoot = [&parent](std::size_t
x) {
5153 while (parent[x] != x) {
5154 parent[
x] = parent[parent[
x]];
5159 const auto unite = [&](std::size_t a, std::size_t b) {
5160 parent[findRoot(a)] = findRoot(b);
5165 constexpr std::size_t dropped = std::numeric_limits<std::size_t>::max();
5166 constexpr std::size_t unset = dropped - 1;
5167 const auto cellOf = [&](
bool kept) {
5171 parent.push_back(parent.size());
5172 return parent.size() - 1;
5179 using TriId =
typename std::decay_t<
decltype(mesh)>::TriId;
5180 using VertexId =
typename std::decay_t<
decltype(mesh)>::VertexId;
5181 using EdgeKey = std::pair<VertexId, VertexId>;
5182 std::map<EdgeKey, std::size_t> edgeCells;
5183 std::vector<std::size_t> vertexCells(mesh.vertexIndexBound(), unset);
5184 const auto vertexCell = [&](VertexId v) {
5185 std::size_t& cell = vertexCells[v.index()];
5186 if (cell == unset) {
5187 const auto&
vertex = mesh.getShape(v);
5188 cell = cellOf(exactDisk.contains(vertex) && !region.contains(vertex));
5192 const auto edgeCell = [&](VertexId a, VertexId b) {
5193 const EdgeKey key = a < b ? EdgeKey{a, b} : EdgeKey{b, a};
5194 const auto [it, inserted] = edgeCells.try_emplace(key, dropped);
5202 const ExactSegment
edge(mesh.getShape(a), mesh.getShape(b));
5203 const ExactPoint mid((
edge.min().x() +
edge.max().x()) / ExactNumber(2),
5204 (
edge.min().y() +
edge.max().y()) / ExactNumber(2));
5205 const bool met = exactDisk.interiorsIntersect(edge) ||
5206 (exactDisk.intersects(edge) && !exactDisk.contains(
edge.min()) &&
5207 !exactDisk.contains(
edge.max()));
5208 it->second = cellOf(met && !region.contains(mid));
5213 mesh.visitTriangles([&](TriId t) {
5214 const auto triangle = mesh.getShape(t);
5215 const auto v = mesh.vertices(t);
5216 const std::size_t face =
5217 cellOf(exactDisk.interiorsIntersect(triangle) &&
5218 !region.contains(triangle.template pointInside<ExactNumber>()));
5219 for (
int k = 0; k < 3; ++k) {
5222 const std::size_t side = edgeCell(v[k], v[(k + 1) % 3]);
5223 const std::size_t corner = vertexCell(v[k]);
5224 if (face != dropped && side != dropped) {
5227 if (face != dropped && corner != dropped) {
5228 unite(face, corner);
5230 if (side != dropped && corner != dropped) {
5231 unite(side, corner);
5233 const std::size_t next = vertexCell(v[(k + 1) % 3]);
5234 if (side != dropped && next != dropped) {
5240 std::size_t components = 0;
5241 for (std::size_t i = 0; i < parent.size(); ++i) {
5242 if (findRoot(i) == i && ++components >= 2) {
5259template <
class Region>
5260std::vector<Segment<typename Region::PointType>> regionSlits(
const Region& region) {
5261 using RegionPoint =
typename Region::PointType;
5262 using RegionSegment = Segment<RegionPoint>;
5264 const auto edges = region.edges();
5265 std::vector<RegionPoint> vertices = region.vertices();
5266 std::sort(vertices.begin(), vertices.end());
5267 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
5269 std::vector<RegionSegment> slits;
5270 std::vector<RegionPoint> cuts;
5271 for (
const auto& edge : edges) {
5273 cuts.push_back(
edge.min());
5274 cuts.push_back(
edge.max());
5275 for (
const auto& vertex : vertices) {
5276 if (
edge.contains(vertex)) {
5277 cuts.push_back(vertex);
5282 std::sort(cuts.begin(), cuts.end());
5283 cuts.erase(std::unique(cuts.begin(), cuts.end()), cuts.end());
5284 for (std::size_t k = 0; k + 1 < cuts.size(); ++k) {
5285 const RegionSegment piece(cuts[k], cuts[k + 1]);
5287 for (
const auto& cover : edges) {
5288 if (cover.contains(piece.min()) && cover.contains(piece.max()) && ++covers == 2) {
5289 slits.push_back(piece);
5295 std::sort(slits.begin(), slits.end());
5296 slits.erase(std::unique(slits.begin(), slits.end()), slits.end());
5321template <
class OtherDisk,
class Region>
5322bool diskSeparatesRegion(
const OtherDisk& disk,
const Region& region) {
5323 using RegionPoint =
typename Region::PointType;
5324 using RegionSegment = Segment<RegionPoint>;
5329 if (!disk.bbox().intersects(region.bbox())) {
5330 return disconnectedOnItsOwn(region);
5333 std::vector<std::size_t> parent;
5334 const auto findRoot = [&parent](std::size_t
x) {
5335 while (parent[x] != x) {
5336 parent[
x] = parent[parent[
x]];
5341 const auto unite = [&](std::size_t a, std::size_t b) {
5342 parent[findRoot(a)] = findRoot(b);
5348 const auto mesh = region.triangulation();
5349 using TriId =
typename std::decay_t<
decltype(mesh)>::TriId;
5350 using VertexId =
typename std::decay_t<
decltype(mesh)>::VertexId;
5356 constexpr std::size_t none = std::numeric_limits<std::size_t>::max();
5357 std::vector<std::size_t> nodes(mesh.vertexIndexBound(), none);
5358 const auto nodeOf = [&](VertexId v) {
5359 std::size_t& node = nodes[v.index()];
5361 node = parent.size();
5362 parent.push_back(parent.size());
5369 std::vector<char> eatenAt(mesh.vertexIndexBound(), 0);
5370 for (
const VertexId v : mesh.vertexIds()) {
5371 eatenAt[v.index()] = disk.contains(mesh.getShape(v)) ? 1 : 0;
5374 mesh.visitTriangles([&](TriId t) {
5377 const auto v = mesh.vertices(t);
5378 const bool eaten[3] = {eatenAt[v[0].index()] != 0, eatenAt[v[1].index()] != 0,
5379 eatenAt[v[2].index()] != 0};
5380 std::size_t first = none;
5381 std::size_t last = none;
5382 bool gapBeforeFirst =
false;
5384 const auto keep = [&](VertexId
vertex) {
5385 const std::size_t node = nodeOf(vertex);
5388 gapBeforeFirst = gap;
5395 for (
int i = 0; i < 3; ++i) {
5396 const VertexId from = v[i];
5397 const VertexId to = v[(i + 1) % 3];
5398 if (eaten[i] && eaten[(i + 1) % 3]) {
5400 }
else if (eaten[i]) {
5403 }
else if (eaten[(i + 1) % 3]) {
5406 }
else if (disk.intersects(
5407 RegionSegment(mesh.getShape(from), mesh.getShape(to)))) {
5416 if (first != none && !gap && !gapBeforeFirst) {
5421 const auto slits = regionSlits(region);
5422 if (!slits.empty()) {
5427 std::map<RegionPoint, VertexId> vertexAt;
5428 for (
const VertexId v : mesh.vertexIds()) {
5429 vertexAt.emplace(mesh.getShape(v), v);
5431 const auto nodeAtPoint = [&](
const RegionPoint& p) {
5432 const auto it = vertexAt.find(p);
5433 return it == vertexAt.end() ? none : nodeOf(it->second);
5435 for (
const auto& slit : slits) {
5440 const std::size_t low = disk.contains(slit.min()) ? none : nodeAtPoint(slit.min());
5441 const std::size_t high = disk.contains(slit.max()) ? none : nodeAtPoint(slit.max());
5442 if (low != none && high != none && !disk.intersects(slit)) {
5448 std::size_t components = 0;
5449 for (std::size_t i = 0; i < parent.size(); ++i) {
5450 if (findRoot(i) == i && ++components >= 2) {
5465template <
class Po
intType,
class LabelType>
5466template <Po
intConcept OtherPo
int>
5471template <
class Po
intType,
class LabelType>
5472template <SegmentConcept OtherSegment>
5475 return outer_.separates(other);
5477 if (other.isDegenerate()) {
5480 return detail::cellSeparates(other, *
this);
5483template <
class Po
intType,
class LabelType>
5484template <OrientedSegmentConcept OtherOrientedSegment>
5489template <
class Po
intType,
class LabelType>
5490template <LineConcept OtherLine>
5493 return outer_.separates(other);
5495 if (other.isDegenerate()) {
5498 return detail::linearAndRegionSeparate<true>(other, *
this);
5501template <
class Po
intType,
class LabelType>
5502template <OrientedLineConcept OtherOrientedLine>
5507template <
class Po
intType,
class LabelType>
5508template <RayConcept OtherRay>
5511 return outer_.separates(other);
5513 if (other.isDegenerate()) {
5516 return detail::linearAndRegionSeparate<true>(other, *
this);
5519template <
class Po
intType,
class LabelType>
5520template <HalfplaneConcept OtherHalfplane>
5523 return outer_.separates(other);
5525 if (other.isUndefined()) {
5528 return detail::convexAndRegionSeparate<true>(other, *
this);
5531template <
class Po
intType,
class LabelType>
5532template <RectangleConcept OtherRectangle>
5534 if (other.empty()) {
5541template <
class Po
intType,
class LabelType>
5542template <TriangleConcept OtherTriangle>
5547template <
class Po
intType,
class LabelType>
5548template <ConvexConcept OtherConvex>
5553template <
class Po
intType,
class LabelType>
5554template <PolygonConcept OtherPolygon>
5557 return outer_.separates(other);
5559 if (other.isDegenerate()) {
5562 return detail::cellSeparates(other, *
this);
5565template <
class Po
intType,
class LabelType>
5566template <PolygonWithHolesConcept OtherRegion>
5569 return outer_.separates(other);
5571 if (!other.hasHoles()) {
5574 if (other.isDegenerate()) {
5577 return detail::cellSeparates(other, *
this);
5580template <
class Po
intType,
class LabelType>
5581template <MonotoneChainConcept OtherChain>
5584 return outer_.separates(other);
5586 return detail::cellSeparates(other, *
this);
5589template <
class Po
intType,
class LabelType>
5590template <PolylineConcept OtherPolyline>
5593 return outer_.separates(other);
5595 return detail::cellSeparates(other, *
this);
5598template <
class Po
intType,
class LabelType>
5599template <DiskConcept OtherDisk>
5602 return outer_.separates(other);
5604 if (other.isDegenerate()) {
5607 return detail::regionSeparatesDisk(*
this, other);
5610template <
class Po
intType,
class LabelType>
5611template <HalfplaneIntersectionConcept OtherIntersection>
5614 return outer_.separates(other);
5616 return detail::convexAndRegionSeparate<true>(other, *
this);
5628template <
class Number,
class Label>
5629template <PolygonWithHolesConcept OtherRegion>
5631 if (!other.hasHoles()) {
5645template <
class Po
intType,
class LabelType>
5646template <PolygonWithHolesConcept OtherRegion>
5648 if (!other.hasHoles()) {
5652 return min().separates(other);
5654 return detail::cellSeparates(other, *
this);
5657template <
class Po
intType,
class LabelType>
5658template <PolygonWithHolesConcept OtherRegion>
5663template <
class Po
intType,
class LabelType>
5664template <PolygonWithHolesConcept OtherRegion>
5666 if (!other.hasHoles()) {
5672 return detail::linearAndRegionSeparate<false>(*
this, other);
5675template <
class Po
intType,
class LabelType>
5676template <PolygonWithHolesConcept OtherRegion>
5678 return asLine().separates(other);
5681template <
class Po
intType,
class LabelType>
5682template <PolygonWithHolesConcept OtherRegion>
5684 if (!other.hasHoles()) {
5690 return detail::linearAndRegionSeparate<false>(*
this, other);
5693template <
class Po
intType,
class LabelType>
5694template <PolygonWithHolesConcept OtherRegion>
5696 if (!other.hasHoles()) {
5702 return detail::convexAndRegionSeparate<false>(*
this, other);
5705template <
class Po
intType,
class LabelType>
5706template <PolygonWithHolesConcept OtherRegion>
5715template <
class Po
intType,
class LabelType>
5716template <PolygonWithHolesConcept OtherRegion>
5721template <
class Po
intType,
class LabelType>
5722template <PolygonWithHolesConcept OtherRegion>
5727template <
class Po
intType,
class LabelType>
5728template <PolygonWithHolesConcept OtherRegion>
5730 if (!other.hasHoles()) {
5736 return detail::cellSeparates(other, *
this);
5739template <
class Po
intType,
class LabelType,
class Storage>
5740template <PolygonWithHolesConcept OtherRegion>
5742 if (!other.hasHoles()) {
5745 return detail::cellSeparates(other, *
this);
5748template <
class Po
intType,
class LabelType>
5749template <PolygonWithHolesConcept OtherRegion>
5751 if (!other.hasHoles()) {
5754 return detail::cellSeparates(other, *
this);
5757template <
class Po
intType,
class LabelType>
5758template <PolygonWithHolesConcept OtherRegion>
5760 if (!other.hasHoles()) {
5768 return detail::diskSeparatesRegion(*
this, other);
5771template <
class Po
intType,
class LabelType>
5772template <PolygonWithHolesConcept OtherHoledRegion>
5774 if (!other.hasHoles()) {
5780 return detail::convexAndRegionSeparate<false>(*
this, other);
5787template <
class Po
intType,
class LabelType>
5788template <Po
intConcept OtherPo
int>
5791 [
this](
const auto& value) {
5798template <
class Po
intType,
class LabelType>
5803 if (components_.size() < 2) {
5806 std::vector<std::size_t> parent(components_.size());
5807 for (std::size_t i = 0; i < parent.size(); ++i) {
5810 const auto findRoot = [&parent](std::size_t
x) {
5811 while (parent[
x] !=
x) {
5812 parent[
x] = parent[parent[
x]];
5817 std::size_t pieces = components_.size();
5818 for (std::size_t i = 0; i < components_.size(); ++i) {
5819 for (std::size_t j = i + 1; j < components_.size(); ++j) {
5820 if (findRoot(i) == findRoot(j) ||
5824 if (components_[i].
intersects(components_[j])) {
5825 parent[findRoot(i)] = findRoot(j);
5847template <
class Po
intType,
class LabelType>
5848template <detail::SetOperandConcept OtherShape>
5861 if (other.isDegenerate()) {
5864 return detail::linearAndRegionSeparate<true>(other, *
this);
5866 if (other.isUndefined()) {
5869 return detail::convexAndRegionSeparate<true>(other, *
this);
5871 return detail::convexAndRegionSeparate<true>(other, *
this);
5873 if (other.isDegenerate()) {
5876 return detail::regionSeparatesDisk(*
this, other);
5881 return detail::cellSeparates(other, *
this);
5883 if (other.isDegenerate()) {
5886 return detail::cellSeparates(other, *
this);
5890template <
class Po
intType,
class LabelType>
5891template <PolygonSetConcept OtherSet>
5893 return detail::cellSeparates(other, *
this);
5896template <
class Po
intType,
class LabelType>
5897template <Po
intConcept OtherPo
int>
5899 return std::visit([
this](
const auto& value) {
return this->
separates(value); },
5907template <
class Number,
class Label>
5908template <PolygonSetConcept OtherSet>
5910 return detail::cellSeparates(other, *
this);
5913template <
class Po
intType,
class LabelType>
5914template <PolygonSetConcept OtherSet>
5916 return detail::cellSeparates(other, *
this);
5919template <
class Po
intType,
class LabelType>
5920template <PolygonSetConcept OtherSet>
5925template <
class Po
intType,
class LabelType>
5926template <PolygonSetConcept OtherSet>
5929 return min().separates(other);
5931 return detail::linearAndRegionSeparate<false>(*
this, other);
5934template <
class Po
intType,
class LabelType>
5935template <PolygonSetConcept OtherSet>
5937 return asLine().separates(other);
5940template <
class Po
intType,
class LabelType>
5941template <PolygonSetConcept OtherSet>
5944 return source().separates(other);
5946 return detail::linearAndRegionSeparate<false>(*
this, other);
5949template <
class Po
intType,
class LabelType>
5950template <PolygonSetConcept OtherSet>
5956 return detail::disconnectedOnItsOwn(other);
5958 return detail::convexAndRegionSeparate<false>(*
this, other);
5961template <
class Po
intType,
class LabelType>
5962template <PolygonSetConcept OtherSet>
5970template <
class Po
intType,
class LabelType>
5971template <PolygonSetConcept OtherSet>
5976template <
class Po
intType,
class LabelType>
5977template <PolygonSetConcept OtherSet>
5982template <
class Po
intType,
class LabelType>
5983template <PolygonSetConcept OtherSet>
5985 return detail::cellSeparates(other, *
this);
5988template <
class Po
intType,
class LabelType>
5989template <PolygonSetConcept OtherSet>
5991 return detail::cellSeparates(other, *
this);
5994template <
class Po
intType,
class LabelType,
class Storage>
5995template <PolygonSetConcept OtherSet>
5997 return detail::cellSeparates(other, *
this);
6000template <
class Po
intType,
class LabelType>
6001template <PolygonSetConcept OtherSet>
6003 return detail::cellSeparates(other, *
this);
6006template <
class Po
intType,
class LabelType>
6007template <PolygonSetConcept OtherSet>
6010 return detail::cellSeparates(other,
a());
6012 return detail::diskSeparatesRegion(*
this, other);
6015template <
class Po
intType,
class LabelType>
6016template <PolygonSetConcept OtherSet>
6019 return detail::disconnectedOnItsOwn(other);
6021 return detail::convexAndRegionSeparate<false>(*
this, other);