183 using TriId = detail::Handle<TriTag>;
201 template <
class TriangleRange>
204 std::unordered_map<PointType, VertexIndex> vid;
205 const auto idOfPoint = makeVertexInterner(vid);
209 for (
const auto& tr : tris) {
215 syncVertexApproximations();
217 for (VertexIndex i = 0; i < static_cast<VertexIndex>(vertices_.size()); ++i) {
218 vid.emplace(vertices_[i], i);
221 std::vector<std::array<VertexIndex, 3>> triples;
222 std::vector<TriangleLabel> triLabels;
223 for (
const auto& tr : tris) {
224 triples.push_back({vid.at(tr[0]), vid.at(tr[1]), vid.at(tr[2])});
225 triLabels.push_back(detail::copyLabel<TriangleLabel>(tr));
227 buildFromTriples(triples, triLabels);
243 template <
class SegmentRange>
249 std::unordered_map<PointType, VertexIndex> vid;
250 const auto idOfPoint = makeVertexInterner(vid);
251 for (
const auto& s : segs) {
256 syncVertexApproximations();
258 for (VertexIndex i = 0; i < static_cast<VertexIndex>(vertices_.size()); ++i) {
259 vid.emplace(vertices_[i], i);
262 std::vector<std::pair<VertexIndex, VertexIndex>> elist;
263 for (
const auto& s : segs) {
264 VertexIndex a = vid.at(s[0]);
265 VertexIndex b = vid.at(s[1]);
267 elist.emplace_back(a, b);
270 const VertexIndex n =
static_cast<VertexIndex
>(vertices_.size());
273 std::vector<std::vector<VertexIndex>> adj(
static_cast<std::size_t
>(n));
274 std::set<std::pair<VertexIndex, VertexIndex>> seen;
275 for (
auto [a, b] : elist) {
276 auto k = a < b ? std::pair{a, b} : std::pair{b, a};
277 if (seen.insert(k).second) {
284 std::vector<std::unordered_map<VertexIndex, int>> posIn(
static_cast<std::size_t
>(n));
285 for (VertexIndex v = 0; v < n; ++v) {
286 std::vector<PointType> nbr;
287 nbr.reserve(adj[v].size());
288 for (VertexIndex w : adj[v]) {
289 nbr.push_back(vertices_[w]);
293 for (
const auto& p : nbr) {
294 adj[v].push_back(vid.at(p));
296 for (
int i = 0; i < static_cast<int>(adj[v].size()); ++i) {
297 posIn[v][adj[v][i]] = i;
304 const auto nextHE = [&](VertexIndex u, VertexIndex v) -> std::pair<VertexIndex, VertexIndex> {
305 const auto& a = adj[v];
306 const int deg =
static_cast<int>(a.size());
307 const int pu = posIn[v].at(u);
308 return {v, a[(pu + deg - 1) % deg]};
311 std::vector<std::array<VertexIndex, 3>> triples;
312 std::set<std::pair<VertexIndex, VertexIndex>> visited;
313 for (VertexIndex v = 0; v < n; ++v) {
314 for (VertexIndex w : adj[v]) {
315 std::pair<VertexIndex, VertexIndex> h{v, w};
316 if (visited.count(h)) {
319 std::vector<VertexIndex> cycle;
320 while (visited.insert(h).second) {
321 cycle.push_back(h.first);
322 h = nextHE(h.first, h.second);
324 if (cycle.size() == 3 &&
325 orientationSign(vertices_[cycle[0]], vertices_[cycle[1]], vertices_[cycle[2]]) > 0) {
326 triples.push_back({cycle[0], cycle[1], cycle[2]});
330 buildFromTriples(triples, std::vector<TriangleLabel>(triples.size()));
333 if constexpr (detail::has_label_v<SegmentLabel>) {
334 for (
const auto& s : segs) {
335 auto it = segmentMap().find(s);
336 if (it != segmentMap().end()) {
337 it->second.segLabel = detail::copyLabel<SegmentLabel>(s);
355 template <
class Po
intRange>
358 std::unordered_map<PointType, VertexIndex> vid;
359 const auto idOfPoint = makeVertexInterner(vid);
360 for (
const auto& p : pts) {
370 syncVertexApproximations();
371 auto triples = delaunayTriples(vertices_, vertexApproximations_);
372 buildFromTriples(triples, std::vector<TriangleLabel>(triples.size()));
395 template <
class Po
intRange,
class SegmentRange>
399 std::unordered_map<PointType, VertexIndex> vid;
400 const auto idOfPoint = makeVertexInterner(vid);
401 for (
const auto& p : pts) {
404 for (
const auto& s : segments) {
410 syncVertexApproximations();
411 auto triples = delaunayTriples(vertices_, vertexApproximations_);
412 buildFromTriples(triples, std::vector<TriangleLabel>(triples.size()));
419 for (VertexIndex i = 1; i < static_cast<VertexIndex>(vertices_.size()); ++i) {
420 vid.emplace(vertices_[i], i);
422 for (
const auto& s : segments) {
423 const VertexIndex a = vid.at(
PointType(s[0]));
424 const VertexIndex b = vid.at(
PointType(s[1]));
426 insertConstraint(a, b);
429 restoreConstrainedDelaunay();
433 if constexpr (detail::has_label_v<SegmentLabel>) {
434 for (
const auto& s : segments) {
436 if (it != segmentMap().end()) {
437 it->second.segLabel = detail::copyLabel<SegmentLabel>(s);
464 constructConstrained({poly}, std::array<PointType, 0>{}, std::array<SegmentType, 0>{});
472 template <
class Po
intRange>
475 constructConstrained({poly}, points, std::array<SegmentType, 0>{});
484 template <
class SegmentRange>
487 constructConstrained({poly}, std::array<PointType, 0>{}, segments);
497 template <
class Po
intRange,
class SegmentRange>
501 const SegmentRange& segments) {
502 constructConstrained({poly}, points, segments);
527 constructConstrained({region.
outer()}, std::array<PointType, 0>{},
528 std::array<SegmentType, 0>{}, region.
holes());
536 template <
class Po
intRange>
539 constructConstrained({region.
outer()}, points, std::array<SegmentType, 0>{}, region.
holes());
548 template <
class SegmentRange>
551 constructConstrained({region.
outer()}, std::array<PointType, 0>{}, segments, region.
holes());
561 template <
class Po
intRange,
class SegmentRange>
565 const SegmentRange& segments) {
566 constructConstrained({region.
outer()}, points, segments, region.
holes());
588 constructConstrained(setOuters(set), std::array<PointType, 0>{},
589 std::array<SegmentType, 0>{}, setHoles(set));
597 template <
class Po
intRange>
600 constructConstrained(setOuters(set), points, std::array<SegmentType, 0>{}, setHoles(set));
609 template <
class SegmentRange>
612 constructConstrained(setOuters(set), std::array<PointType, 0>{}, segments, setHoles(set));
622 template <
class Po
intRange,
class SegmentRange>
626 const SegmentRange& segments) {
627 constructConstrained(setOuters(set), points, segments, setHoles(set));
658 template <
class ResultNumber = division_result_t<NumberType>>
666 return vertices_.empty() ? 0 : vertices_.size() - 1;
670 [[nodiscard]] std::size_t
numTriangles()
const {
return domainTriangleCount_; }
679 std::size_t count = 0;
680 for (TriIndex t = 0; t < firstGhost_; ++t) {
681 for (std::int8_t s = 0; s < 3; ++s) {
682 const TriIndex other = triangles_[t].nbr[s];
683 if (other != NO_TRI && other < firstGhost_ && other < t) {
686 if (edgeInDomain(Edge{t, s})) {
695 [[nodiscard]]
bool empty()
const {
return domainTriangleCount_ == 0; }
704 auto se = segmentMap().find(s);
705 return se != segmentMap().end() && edgeInDomain(se->second);
720 auto se = segmentMap().find(shared);
721 if (se == segmentMap().end()) {
724 const TriIndex given = idOf(t);
725 if (given == NO_TRI) {
728 const Edge e = se->second;
729 const TriIndex i1 = e.tri;
730 const TriIndex i2 = mirror(e).tri;
731 TriIndex other = (given == i1) ? i2 : (given == i2 ? i1 : NO_TRI);
732 if (!inDomain(other)) {
735 return triangleValue(other);
760 std::vector<TriangleType> out;
761 auto se = segmentMap().find(s);
762 if (se == segmentMap().end()) {
765 const Edge e = se->second;
766 if (inDomain(e.tri)) {
767 out.push_back(triangleValue(e.tri));
769 const TriIndex other = mirror(e).tri;
770 if (inDomain(other)) {
771 out.push_back(triangleValue(other));
803 for (TriIndex t = 0; t < firstGhost_; ++t) {
807 if (reportTriangle(fn, t)) {
824 for (
const auto& [seg, e] : segmentMap()) {
826 if (!edgeInDomain(e)) {
829 if (detail::invokeVisitor(fn, edgeSegment(e))) {
837 [[nodiscard]] std::vector<TriangleType>
triangles()
const {
838 std::vector<TriangleType> out;
841 std::sort(out.begin(), out.end());
846 [[nodiscard]] std::vector<SegmentType>
edges()
const {
847 std::vector<SegmentType> out;
850 std::sort(out.begin(), out.end());
875 for (VertexIndex v = GHOST + 1; v < static_cast<VertexIndex>(vertices_.size()); ++v) {
945 return static_cast<bool>(pointLocation_);
958 return pointLocation_ && pointLocationRevision_ == revision_;
971 std::vector<TriId> out;
985 std::vector<VertexId> out;
987 for (VertexIndex v = GHOST + 1; v < static_cast<VertexIndex>(vertices_.size()); ++v) {
988 out.push_back(vertexHandle(v));
1005 return static_cast<std::size_t
>(firstGhost_);
1041 bool answered =
false;
1042 const TriIndex seed = pointLocationSeed(p, answered);
1045 return triHandle(inDomain(seed) ? seed : NO_TRI);
1047 const TriIndex
id = locateIndex(p, seed);
1048 return triHandle(inDomain(
id) ?
id : NO_TRI);
1057 template <Po
intConcept QueryPo
int>
1059 if constexpr (std::same_as<typename QueryPoint::NumberType, NumberType> &&
1060 std::constructible_from<PointType, const QueryPoint&>) {
1063 const TriIndex
id = locateIndex(p);
1064 return triHandle(inDomain(
id) ?
id : NO_TRI);
1074 assert(
has(t) &&
"getShape(): the handle is not a triangle of the triangulation");
1075 return triangleValue(indexOf(t));
1085 assert(
has(v) &&
"getShape(): the handle is not a vertex of the triangulation");
1086 return vertices_[
static_cast<std::size_t
>(indexOf(v))];
1107 const TriIndex
id = idOf(t);
1108 return triHandle(inDomain(
id) ?
id : NO_TRI);
1121 return vertexHandle(vertexIndexAt(p));
1125 [[nodiscard]]
bool has(
TriId t)
const {
return inDomain(indexOf(t)); }
1128 [[nodiscard]]
bool has(
VertexId v)
const {
return realVertex(indexOf(v)); }
1141 assert(
has(t) &&
"vertices(): the handle is not a triangle of the triangulation");
1142 const TriIndex
id = indexOf(t);
1143 const auto& v = triangles_[
static_cast<std::size_t
>(id)].v;
1144 const int first = firstVertex(
id);
1145 return {vertexHandle(v[first]), vertexHandle(v[(first + 1) % 3]),
1146 vertexHandle(v[(first + 2) % 3])};
1164 assert(
has(t) &&
"otherTriangle(): the handle is not a triangle of the triangulation");
1165 assert(side >= 0 && side < 3 &&
"otherTriangle(): side is not one of 0, 1, 2");
1166 const TriIndex
id = indexOf(t);
1167 const TriIndex nb = triangles_[
static_cast<std::size_t
>(id)].nbr[internalSide(
id, side)];
1168 return inDomain(nb) ? std::optional<TriId>(triHandle(nb)) : std::nullopt;
1186 assert(
has(t) &&
"isConstrained(): the handle is not a triangle of the triangulation");
1187 assert(side >= 0 && side < 3 &&
"isConstrained(): side is not one of 0, 1, 2");
1188 const TriIndex
id = indexOf(t);
1189 return bit(triangles_[
static_cast<std::size_t
>(
id)].constrainedMask,
1190 internalSide(
id, side));
1206 assert(
has(t) &&
"setConstrained(): the handle is not a triangle of the triangulation");
1207 assert(side >= 0 && side < 3 &&
"setConstrained(): side is not one of 0, 1, 2");
1208 const TriIndex
id = indexOf(t);
1209 const Edge e{id,
static_cast<std::int8_t
>(internalSide(
id, side))};
1210 setBit(triangles_[
static_cast<std::size_t
>(
id)].constrainedMask, e.side, value);
1211 const Edge m = mirror(e);
1212 if (m.tri != NO_TRI) {
1213 setBit(triangles_[
static_cast<std::size_t
>(m.tri)].constrainedMask, m.side, value);
1230 const TriIndex
id = indexOf(t);
1231 if (!inDomain(
id)) {
1232 return std::nullopt;
1234 const VertexIndex first = indexOf(a);
1235 const VertexIndex second = indexOf(b);
1236 const auto& v = triangles_[
static_cast<std::size_t
>(id)].v;
1237 for (
int s = 0; s < 3; ++s) {
1238 const VertexIndex left = v[(s + 1) % 3];
1239 const VertexIndex right = v[(s + 2) % 3];
1240 if ((left == first && right == second) || (left == second && right == first)) {
1241 const TriIndex nb = triangles_[
static_cast<std::size_t
>(id)].nbr[s];
1242 return inDomain(nb) ? std::optional<TriId>(triHandle(nb)) : std::nullopt;
1245 return std::nullopt;
1250 std::vector<TriId> out;
1251 const TriIndex
id = indexOf(t);
1252 if (!realTriangle(
id)) {
1255 for (
int s = 0; s < 3; ++s) {
1256 const TriIndex nb = triangles_[
static_cast<std::size_t
>(id)].nbr[s];
1258 out.push_back(triHandle(nb));
1271 std::vector<TriId> out;
1272 const TriIndex self = indexOf(t);
1273 if (!realTriangle(self)) {
1280 std::vector<TriIndex> marked{self};
1281 triangles_[self].walkMark = 1;
1282 struct MarkClearer {
1283 const std::vector<Tri>& tris;
1284 const std::vector<TriIndex>& marked;
1285 ~MarkClearer() {
for (TriIndex m : marked) tris[m].walkMark = 0; }
1286 } markClearer{triangles_, marked};
1287 for (
const VertexIndex w : triangles_[self].v) {
1288 visitVertexFan(self, w, [&](TriIndex cur) {
1289 if (inDomain(cur) && !triangles_[cur].walkMark) {
1290 triangles_[cur].walkMark = 1;
1291 marked.push_back(cur);
1292 out.push_back(triHandle(cur));
1309 std::vector<TriId> out;
1310 const VertexIndex w = indexOf(v);
1311 if (!realVertex(w)) {
1314 const TriIndex start = fanSeedOf(w);
1315 if (start == NO_TRI) {
1318 visitVertexFan(start, w, [&](TriIndex cur) {
1319 if (inDomain(cur)) {
1320 out.push_back(triHandle(cur));
1335 template <
class L = TriangleLabel>
1336 requires(detail::has_label_v<L>)
1338 assert(
has(t) &&
"label(): the handle is not a triangle of the triangulation");
1339 return triangles_[
static_cast<std::size_t
>(indexOf(t))].triLabel;
1343 template <
class L = TriangleLabel>
1344 requires(detail::has_label_v<L>)
1346 assert(
has(t) &&
"label(): the handle is not a triangle of the triangulation");
1347 return triangles_[
static_cast<std::size_t
>(indexOf(t))].triLabel;
1516 template <
class ResultNumber = division_result_t<NumberType>>
1521 friend struct detail::ConvexCoverBuilder;
1529 [[nodiscard]]
bool trianglesFullyVisible(
1530 std::int32_t a, std::int32_t b,
const std::vector<std::uint8_t>& visible)
const {
1533 std::array<PointType, 6> points{
1534 first.a(), first.b(), first.c(), second.a(), second.b(), second.c()
1536 std::sort(points.begin(), points.end());
1537 const auto uniqueEnd = std::unique(points.begin(), points.end());
1538 const std::size_t pointCount =
1539 static_cast<std::size_t
>(std::distance(points.begin(), uniqueEnd));
1544 std::array<PointType, 12> hull;
1545 std::size_t hullSize = 0;
1546 for (std::size_t i = 0; i < pointCount; ++i) {
1547 while (hullSize >= 2 &&
1548 orientationSign(hull[hullSize - 2], hull[hullSize - 1], points[i]) <= 0) {
1551 hull[hullSize++] = points[i];
1553 const std::size_t lowerSize = hullSize;
1554 for (std::size_t i = pointCount - 1; i-- != 0;) {
1555 while (hullSize > lowerSize &&
1556 orientationSign(hull[hullSize - 2], hull[hullSize - 1], points[i]) <= 0) {
1559 hull[hullSize++] = points[i];
1561 assert(hullSize >= 2);
1564 const auto isVertexOf = [&](TriIndex triangle,
const PointType& point) {
1565 const auto& ids = triangles_[triangle].v;
1566 return vertices_[ids[0]] == point || vertices_[ids[1]] == point ||
1567 vertices_[ids[2]] == point;
1576 bool boundedByVisibleTriangles =
true;
1577 for (std::int8_t side = 0; side < 3; ++side) {
1578 const PointType u = vertices_[triangles_[b].v[(side + 1) % 3]];
1579 const PointType v = vertices_[triangles_[b].v[(side + 2) % 3]];
1580 bool onHullBoundary =
false;
1581 for (std::size_t i = 0; i < hullSize; ++i) {
1582 const Segment<PointType> hullSide(hull[i], hull[(i + 1) % hullSize]);
1583 if (hullSide.contains(u) && hullSide.contains(v)) {
1584 onHullBoundary =
true;
1588 if (onHullBoundary) {
1591 const TriIndex across = triangles_[b].nbr[side];
1592 if (!inDomain(across)) {
1595 if (!visible[
static_cast<std::size_t
>(across)]) {
1596 boundedByVisibleTriangles =
false;
1599 if (boundedByVisibleTriangles && holeWitnesses_.empty()) {
1603 for (std::size_t i = 0; i < hullSize; ++i) {
1605 const PointType v = hull[(i + 1) % hullSize];
1606 if ((isVertexOf(a, u) && isVertexOf(a, v)) ||
1607 (isVertexOf(b, u) && isVertexOf(b, v))) {
1610 if (!segmentInteriorContained(Segment<PointType>(u, v))) {
1618 if (!holeWitnesses_.empty()) {
1619 const std::vector<PointType> hullVertices(hull.begin(), hull.begin() + hullSize);
1620 const Convex<PointType>
convexHull(hullVertices,
true);
1661 [[nodiscard]] Graph<TriangleType> convexCoverVisibilityGraph()
const {
1662 Graph<TriangleType> result;
1663 std::vector<TriIndex> sources;
1664 sources.reserve(domainTriangleCount_);
1665 for (TriIndex source = 0; source < firstGhost_; ++source) {
1666 if (inDomain(source)) {
1667 sources.push_back(source);
1668 result.addVertex(triangleValue(source));
1675 std::vector<TriIndex> sourceOrder;
1676 sourceOrder.reserve(sources.size());
1677 std::vector<bool> ordered(
static_cast<std::size_t
>(firstGhost_),
false);
1678 std::vector<TriIndex> stack;
1679 for (
const TriIndex seed : sources) {
1680 if (ordered[
static_cast<std::size_t
>(seed)]) {
1683 stack.push_back(seed);
1684 while (!stack.empty()) {
1685 const TriIndex current = stack.back();
1687 if (ordered[
static_cast<std::size_t
>(current)]) {
1690 ordered[
static_cast<std::size_t
>(current)] =
true;
1691 sourceOrder.push_back(current);
1692 for (
const TriIndex neighbor : triangles_[current].nbr) {
1693 if (inDomain(neighbor) &&
1694 !ordered[
static_cast<std::size_t
>(neighbor)]) {
1695 stack.push_back(neighbor);
1701 std::vector<std::uint8_t> visited(
static_cast<std::size_t
>(firstGhost_));
1702 std::vector<std::uint8_t> visible(
static_cast<std::size_t
>(firstGhost_));
1703 std::queue<TriIndex> queue;
1704 for (
const TriIndex source : sourceOrder) {
1705 std::fill(visited.begin(), visited.end(),
false);
1706 std::fill(visible.begin(), visible.end(),
false);
1707 visited[
static_cast<std::size_t
>(source)] =
true;
1708 visible[
static_cast<std::size_t
>(source)] =
true;
1710 const TriangleType sourceTriangle = triangleValue(source);
1712 while (!queue.empty()) {
1713 const TriIndex current = queue.front();
1715 for (
const TriIndex neighbor : triangles_[current].nbr) {
1716 if (!inDomain(neighbor) ||
1717 visited[
static_cast<std::size_t
>(neighbor)]) {
1720 visited[
static_cast<std::size_t
>(neighbor)] =
true;
1721 const TriangleType neighborTriangle = triangleValue(neighbor);
1723 bool fullyVisible = result.containsEdge(sourceTriangle, neighborTriangle);
1724 if (!fullyVisible) {
1725 fullyVisible = trianglesFullyVisible(source, neighbor, visible);
1728 result.addEdge(sourceTriangle, neighborTriangle);
1729 visible[
static_cast<std::size_t
>(neighbor)] =
true;
1730 queue.push(neighbor);
1779 std::vector<Convex<PointType>> pieces;
1780 if (domainTriangleCount_ == 0) {
1790 std::vector<TriIndex> parent(
static_cast<std::size_t
>(firstGhost_));
1791 for (TriIndex t = 0; t < firstGhost_; ++t) {
1792 parent[
static_cast<std::size_t
>(t)] = t;
1794 const auto findRoot = [&parent](TriIndex
x) {
1795 while (parent[
static_cast<std::size_t
>(
x)] !=
x) {
1796 parent[
static_cast<std::size_t
>(
x)] =
1797 parent[
static_cast<std::size_t
>(parent[
static_cast<std::size_t
>(
x)])];
1798 x = parent[
static_cast<std::size_t
>(
x)];
1803 const std::size_t slots =
static_cast<std::size_t
>(firstGhost_) * 3;
1804 std::vector<std::int32_t> succ(slots, -1);
1805 std::vector<std::int32_t> pred(slots, -1);
1806 std::vector<std::int32_t> diagonals;
1807 for (TriIndex t = 0; t < firstGhost_; ++t) {
1811 for (
int s = 0; s < 3; ++s) {
1812 const std::int32_t h = 3 * t + s;
1813 succ[
static_cast<std::size_t
>(h)] = 3 * t + (s + 1) % 3;
1814 pred[
static_cast<std::size_t
>(3 * t + (s + 1) % 3)] = h;
1817 const TriIndex n = triangles_[
static_cast<std::size_t
>(t)].nbr[s];
1818 if (n > t && inDomain(n) &&
1819 !bit(triangles_[
static_cast<std::size_t
>(t)].constrainedMask, s)) {
1820 diagonals.push_back(h);
1825 const auto originOf = [
this](std::int32_t h) {
1826 return triangles_[
static_cast<std::size_t
>(h / 3)].v[
static_cast<std::size_t
>((h % 3 + 1) % 3)];
1828 const auto targetOf = [
this](std::int32_t h) {
1829 return triangles_[
static_cast<std::size_t
>(h / 3)].v[
static_cast<std::size_t
>((h % 3 + 2) % 3)];
1831 const auto neighborOf = [
this](std::int32_t h) {
1832 return triangles_[
static_cast<std::size_t
>(h / 3)].nbr[
static_cast<std::size_t
>(h % 3)];
1840 const auto sharedEdges = [&](std::int32_t start, TriIndex otherRoot) {
1842 std::int32_t h = start;
1844 const TriIndex n = neighborOf(h);
1845 if (n != NO_TRI && inDomain(n) && findRoot(n) == otherRoot) {
1848 h = succ[
static_cast<std::size_t
>(h)];
1849 }
while (h != start);
1853 for (
const std::int32_t h : diagonals) {
1854 const TriIndex t = h / 3;
1855 const TriIndex n = neighborOf(h);
1856 const TriIndex rootA = findRoot(t);
1857 const TriIndex rootB = findRoot(n);
1858 if (rootA == rootB) {
1861 const std::int32_t twin = 3 * n + findSide(n, t);
1865 const VertexIndex u = originOf(h);
1866 const VertexIndex v = targetOf(h);
1867 const VertexIndex p = originOf(pred[
static_cast<std::size_t
>(h)]);
1868 const VertexIndex q = targetOf(succ[
static_cast<std::size_t
>(h)]);
1869 const VertexIndex r = originOf(pred[
static_cast<std::size_t
>(twin)]);
1870 const VertexIndex w = targetOf(succ[
static_cast<std::size_t
>(twin)]);
1871 const auto convexAt = [
this](VertexIndex before, VertexIndex at, VertexIndex after) {
1873 vertices_[
static_cast<std::size_t
>(at)],
1874 vertices_[
static_cast<std::size_t
>(after)]) >= 0;
1878 if (!convexAt(p, u, w) || !convexAt(r, v, q)) {
1881 if (sharedEdges(h, rootB) != 1) {
1884 const std::int32_t beforeU = pred[
static_cast<std::size_t
>(h)];
1885 const std::int32_t afterU = succ[
static_cast<std::size_t
>(twin)];
1886 const std::int32_t beforeV = pred[
static_cast<std::size_t
>(twin)];
1887 const std::int32_t afterV = succ[
static_cast<std::size_t
>(h)];
1888 succ[
static_cast<std::size_t
>(beforeU)] = afterU;
1889 pred[
static_cast<std::size_t
>(afterU)] = beforeU;
1890 succ[
static_cast<std::size_t
>(beforeV)] = afterV;
1891 pred[
static_cast<std::size_t
>(afterV)] = beforeV;
1892 succ[
static_cast<std::size_t
>(h)] = -1;
1893 pred[
static_cast<std::size_t
>(h)] = -1;
1894 succ[
static_cast<std::size_t
>(twin)] = -1;
1895 pred[
static_cast<std::size_t
>(twin)] = -1;
1896 parent[
static_cast<std::size_t
>(rootA)] = rootB;
1905 std::vector<char> read(slots, 0);
1906 std::vector<PointType> ring;
1907 std::vector<PointType> kept;
1908 for (std::int32_t start = 0; start < static_cast<std::int32_t>(slots); ++start) {
1909 if (succ[
static_cast<std::size_t
>(start)] < 0 || read[
static_cast<std::size_t
>(start)]) {
1913 std::int32_t h = start;
1915 read[
static_cast<std::size_t
>(h)] = 1;
1916 ring.push_back(vertices_[
static_cast<std::size_t
>(originOf(h))]);
1917 h = succ[
static_cast<std::size_t
>(h)];
1918 }
while (h != start);
1921 for (std::size_t i = 0; i < ring.size(); ++i) {
1922 const PointType& before = ring[(i + ring.size() - 1) % ring.size()];
1923 const PointType& after = ring[(i + 1) % ring.size()];
1925 kept.push_back(ring[i]);
1928 if (kept.size() < 3) {
1931 std::rotate(kept.begin(), std::min_element(kept.begin(), kept.end()), kept.end());
1934 std::sort(pieces.begin(), pieces.end());
1967 std::vector<Convex<PointType>> result;
1968 if (domainTriangleCount_ == 0) {
1972 const std::size_t triangleSlots =
static_cast<std::size_t
>(firstGhost_);
1973 const std::size_t halfEdgeSlots = triangleSlots * 3;
1974 const auto originOf = [
this](std::int32_t h) {
1975 return triangles_[
static_cast<std::size_t
>(h / 3)]
1976 .v[
static_cast<std::size_t
>((h % 3 + 1) % 3)];
1978 const auto targetOf = [
this](std::int32_t h) {
1979 return triangles_[
static_cast<std::size_t
>(h / 3)]
1980 .v[
static_cast<std::size_t
>((h % 3 + 2) % 3)];
1982 const auto convexAt = [
this](VertexIndex before, VertexIndex at, VertexIndex after) {
1984 vertices_[
static_cast<std::size_t
>(at)],
1985 vertices_[
static_cast<std::size_t
>(after)]) >= 0;
1992 std::vector<Candidate> candidates;
1993 candidates.reserve(domainTriangleCount_);
1995 for (TriIndex seed = 0; seed < firstGhost_; ++seed) {
1996 if (!inDomain(seed)) {
2003 std::vector<std::int32_t> succ(halfEdgeSlots, -1);
2004 std::vector<std::int32_t> pred(halfEdgeSlots, -1);
2005 std::vector<char> fused(triangleSlots, 0);
2006 std::vector<TriIndex> fusedTriangles{seed};
2007 fused[
static_cast<std::size_t
>(seed)] = 1;
2009 const auto initializeRing = [&](TriIndex t) {
2010 for (
int s = 0; s < 3; ++s) {
2011 const std::int32_t h = 3 * t + s;
2012 const std::int32_t next = 3 * t + (s + 1) % 3;
2013 succ[
static_cast<std::size_t
>(h)] = next;
2014 pred[
static_cast<std::size_t
>(next)] = h;
2017 initializeRing(seed);
2019 std::deque<std::int32_t> frontier;
2020 for (
int s = 0; s < 3; ++s) {
2021 frontier.push_back(3 * seed + s);
2024 while (!frontier.empty()) {
2025 const std::int32_t h = frontier.front();
2026 frontier.pop_front();
2027 if (succ[
static_cast<std::size_t
>(h)] < 0) {
2031 const TriIndex t = h / 3;
2032 const int side = h % 3;
2033 const TriIndex neighbor = triangles_[
static_cast<std::size_t
>(t)].nbr[side];
2034 if (!inDomain(neighbor) || fused[
static_cast<std::size_t
>(neighbor)] ||
2035 bit(triangles_[
static_cast<std::size_t
>(t)].constrainedMask, side)) {
2043 int sharedEdges = 0;
2044 for (
const TriIndex adjacent :
2045 triangles_[
static_cast<std::size_t
>(neighbor)].nbr) {
2046 if (inDomain(adjacent) && fused[
static_cast<std::size_t
>(adjacent)]) {
2050 if (sharedEdges != 1) {
2054 const std::int32_t twin = 3 * neighbor + findSide(neighbor, t);
2055 initializeRing(neighbor);
2057 const VertexIndex u = originOf(h);
2058 const VertexIndex v = targetOf(h);
2059 const VertexIndex p = originOf(pred[
static_cast<std::size_t
>(h)]);
2060 const VertexIndex q = targetOf(succ[
static_cast<std::size_t
>(h)]);
2061 const VertexIndex r = originOf(pred[
static_cast<std::size_t
>(twin)]);
2062 const VertexIndex w = targetOf(succ[
static_cast<std::size_t
>(twin)]);
2063 if (!convexAt(p, u, w) || !convexAt(r, v, q)) {
2065 for (
int s = 0; s < 3; ++s) {
2066 const std::size_t rejected =
2067 static_cast<std::size_t
>(3 * neighbor + s);
2068 succ[rejected] = -1;
2069 pred[rejected] = -1;
2074 const std::int32_t beforeU = pred[
static_cast<std::size_t
>(h)];
2075 const std::int32_t afterU = succ[
static_cast<std::size_t
>(twin)];
2076 const std::int32_t beforeV = pred[
static_cast<std::size_t
>(twin)];
2077 const std::int32_t afterV = succ[
static_cast<std::size_t
>(h)];
2078 succ[
static_cast<std::size_t
>(beforeU)] = afterU;
2079 pred[
static_cast<std::size_t
>(afterU)] = beforeU;
2080 succ[
static_cast<std::size_t
>(beforeV)] = afterV;
2081 pred[
static_cast<std::size_t
>(afterV)] = beforeV;
2082 succ[
static_cast<std::size_t
>(h)] = -1;
2083 pred[
static_cast<std::size_t
>(h)] = -1;
2084 succ[
static_cast<std::size_t
>(twin)] = -1;
2085 pred[
static_cast<std::size_t
>(twin)] = -1;
2086 fused[
static_cast<std::size_t
>(neighbor)] = 1;
2087 fusedTriangles.push_back(neighbor);
2091 frontier.push_back(beforeU);
2092 frontier.push_back(afterU);
2093 frontier.push_back(beforeV);
2094 frontier.push_back(afterV);
2097 std::int32_t start = -1;
2098 for (std::int32_t h = 0; h < static_cast<std::int32_t>(halfEdgeSlots); ++h) {
2099 if (succ[
static_cast<std::size_t
>(h)] >= 0) {
2106 std::vector<PointType> ring;
2107 std::int32_t h = start;
2109 ring.push_back(vertices_[
static_cast<std::size_t
>(originOf(h))]);
2110 h = succ[
static_cast<std::size_t
>(h)];
2111 }
while (h != start);
2113 std::vector<PointType> kept;
2114 kept.reserve(ring.size());
2115 for (std::size_t i = 0; i < ring.size(); ++i) {
2116 const PointType& before = ring[(i + ring.size() - 1) % ring.size()];
2117 const PointType& after = ring[(i + 1) % ring.size()];
2119 kept.push_back(ring[i]);
2122 assert(kept.size() >= 3);
2123 std::rotate(kept.begin(), std::min_element(kept.begin(), kept.end()), kept.end());
2124 candidates.push_back(
2131 std::vector<std::vector<std::size_t>> coverers(triangleSlots);
2132 std::vector<std::size_t> gain(candidates.size());
2133 for (std::size_t c = 0; c < candidates.size(); ++c) {
2134 gain[c] = candidates[c].triangles.size();
2135 for (
const TriIndex t : candidates[c].
triangles) {
2136 coverers[
static_cast<std::size_t
>(t)].push_back(c);
2140 std::vector<char> covered(triangleSlots, 0);
2141 std::vector<char> selected(candidates.size(), 0);
2142 std::vector<std::size_t> selectionOrder;
2143 std::size_t remaining = domainTriangleCount_;
2144 while (remaining != 0) {
2145 std::size_t best = candidates.size();
2146 std::size_t bestGain = 0;
2147 for (std::size_t c = 0; c < candidates.size(); ++c) {
2148 if (!selected[c] && gain[c] > bestGain) {
2153 assert(best != candidates.size() && bestGain != 0);
2155 selectionOrder.push_back(best);
2156 for (
const TriIndex t : candidates[best].
triangles) {
2157 const std::size_t ti =
static_cast<std::size_t
>(t);
2163 for (
const std::size_t c : coverers[ti]) {
2164 assert(gain[c] != 0);
2170 std::vector<std::size_t> coverageCount(triangleSlots, 0);
2171 for (
const std::size_t c : selectionOrder) {
2172 for (
const TriIndex t : candidates[c].
triangles) {
2173 ++coverageCount[
static_cast<std::size_t
>(t)];
2176 for (
auto it = selectionOrder.rbegin(); it != selectionOrder.rend(); ++it) {
2177 const std::size_t c = *it;
2178 const bool redundant = std::ranges::all_of(
2179 candidates[c].
triangles, [&](TriIndex t) {
2180 return coverageCount[
static_cast<std::size_t
>(t)] > 1;
2186 for (
const TriIndex t : candidates[c].
triangles) {
2187 --coverageCount[
static_cast<std::size_t
>(t)];
2191 result.reserve(selectionOrder.size());
2192 for (
const std::size_t c : selectionOrder) {
2194 result.push_back(std::move(candidates[c].convex));
2197 std::sort(result.begin(), result.end());
2229 template <detail::DirectedTraversal OS,
class Fn>
2231 return visitTriangleIdsIntersecting<false>(
2232 s, [&](TriIndex t) {
return reportTriangle(f, t); });
2250 template <
bool AllTriangles, detail::DirectedTraversal OS,
class Fn>
2251 bool visitTriangleIdsIntersecting(
const OS& s, Fn f)
const {
2252 if (firstGhost_ == 0)
return false;
2253 const auto a = s[0];
2254 const auto b = s[1];
2258 const auto fa = filteredPoint(a);
2259 const auto fb = filteredPoint(b);
2268 constexpr bool unboundedBack = detail::LineOrOriented<OS>;
2269 constexpr bool unboundedFront = detail::LineOrOriented<OS> ||
RayConcept<OS>;
2278 std::vector<TriIndex> marked;
2279 struct MarkClearer {
2280 const std::vector<Tri>& tris;
2281 const std::vector<TriIndex>& marked;
2282 ~MarkClearer() {
for (TriIndex t : marked) tris[t].walkMark = 0; }
2283 } markClearer{triangles_, marked};
2287 TriIndex prev = NO_TRI;
2292 const auto emit = [&](TriIndex t) {
2293 const bool report = AllTriangles ? (t != NO_TRI && !isGhost(t)) : inDomain(t);
2294 if (!stop && report && !triangles_[t].walkMark) {
2295 triangles_[t].walkMark = 1;
2296 marked.push_back(t);
2297 if (f(t)) stop =
true;
2303 const auto rotateAround = [&](TriIndex cur, VertexIndex w, TriIndex from) -> TriIndex {
2304 const int lw = localIndex(cur, w);
2305 int s1 = -1, s2 = -1;
2306 for (
int s = 0; s < 3; ++s) {
2308 if (s1 < 0) s1 = s;
else s2 = s;
2311 return triangles_[cur].nbr[triangles_[cur].nbr[s1] == from ? s2 : s1];
2314 const auto emitFan = [&](VertexIndex w, TriIndex startTri) {
2315 TriIndex cur = startTri, from = NO_TRI;
2316 std::size_t g = 0, lim = triangles_.size() + 1;
2319 const TriIndex next = rotateAround(cur, w, from);
2322 }
while (cur != startTri && cur != NO_TRI && !stop && ++g < lim);
2331 const auto rayEnters = [&](TriIndex t,
const auto& p) ->
int {
2332 const auto& v = triangles_[t].v;
2333 const auto fp = filteredPoint(p);
2334 for (
int k = 0; k < 3; ++k) {
2335 const auto& u = vertices_[v[(k + 1) % 3]];
2336 const auto& w = vertices_[v[(k + 2) % 3]];
2337 if (detail::orientationSignOf(filteredVertex(v[(k + 1) % 3]),
2338 filteredVertex(v[(k + 2) % 3]), fp).value() == 0) {
2339 const auto forward =
2341 if (forward < 0)
return 0;
2342 if (forward == 0)
return -1;
2348 const auto forwardAround = [&](VertexIndex w, TriIndex startTri) -> TriIndex {
2349 TriIndex cur = startTri, from = NO_TRI;
2350 std::size_t g = 0, lim = triangles_.size() + 1;
2352 if (!isGhost(cur) && rayEnters(cur, vertices_[w]) == 1)
return cur;
2353 const TriIndex next = rotateAround(cur, w, from);
2356 }
while (cur != startTri && cur != NO_TRI && ++g < lim);
2365 const auto alongOrder = [&](
const auto& p,
const auto& q) {
2368 const auto vertexOrder = [&](VertexIndex u, VertexIndex w) {
2369 return alongOrder(vertices_[u], vertices_[w]);
2375 const auto advanceFromVertex = [&](VertexIndex w, TriIndex anchor) -> TriIndex {
2376 std::size_t hops = 0, lim = vertices_.size() + 1;
2377 while (!stop && ++hops < lim) {
2379 if (stop)
return NO_TRI;
2380 if constexpr (!unboundedFront) {
2381 if (alongOrder(b, vertices_[w]) >= 0)
return NO_TRI;
2383 const TriIndex interior = forwardAround(w, anchor);
2384 if (interior != NO_TRI)
return interior;
2387 VertexIndex nextV = NO_TRI;
2388 TriIndex nextAnchor = NO_TRI;
2389 TriIndex cur = anchor, from = NO_TRI;
2390 std::size_t g = 0, glim = triangles_.size() + 1;
2392 if (!isGhost(cur)) {
2393 for (VertexIndex
y : triangles_[cur].v) {
2395 detail::orientationSignOf(fa, fb, filteredVertex(
y)).value() == 0 &&
2396 vertexOrder(w,
y) > 0 &&
2397 (unboundedFront || alongOrder(b, vertices_[
y]) <= 0)) {
2404 if (nextV != NO_TRI)
break;
2405 const TriIndex next = rotateAround(cur, w, from);
2408 }
while (cur != anchor && cur != NO_TRI && ++g < glim);
2409 if (nextV == NO_TRI)
return NO_TRI;
2411 anchor = nextAnchor;
2420 [[maybe_unused]]
const auto lineEnters = [&](TriIndex g) ->
bool {
2421 const VertexIndex va = triangles_[g].v[0];
2422 const VertexIndex vb = triangles_[g].v[1];
2423 const auto& iv = triangles_[triangles_[g].nbr[2]].v;
2424 VertexIndex apex = iv[0];
2425 for (
int i = 1; i < 3; ++i) {
2426 if (iv[i] != va && iv[i] != vb) apex = iv[i];
2428 const auto insideSide =
2433 return dirCross != 0 && (dirCross > 0) == (insideSide > 0);
2456 [[maybe_unused]]
const auto lineEntry = [&](TriIndex g0) -> TriIndex {
2457 TriIndex g = g0, crossing = NO_TRI;
2458 std::size_t guard = 0, lim = triangles_.size() + 1;
2459 while (g != NO_TRI && isGhost(g) && ++guard < lim) {
2460 const VertexIndex va = triangles_[g].v[0];
2461 const VertexIndex vb = triangles_[g].v[1];
2464 if (da == 0 || db == 0 || (da > 0) != (db > 0)) { crossing = g;
break; }
2465 const auto absA = da < 0 ? -da : da;
2466 const auto absB = db < 0 ? -db : db;
2467 g = triangles_[g].nbr[absA < absB ? 1 : 0];
2469 if (crossing == NO_TRI)
return NO_TRI;
2470 TriIndex h = crossing, from = NO_TRI,
collinear = NO_TRI;
2472 while (h != NO_TRI && isGhost(h) && ++guard < lim) {
2473 const VertexIndex va = triangles_[h].v[0];
2474 const VertexIndex vb = triangles_[h].v[1];
2477 const bool straddle = da == 0 || db == 0 || (da > 0) != (db > 0);
2478 if (da == 0 && db == 0) {
2480 }
else if (straddle && lineEnters(h)) {
2483 const TriIndex next = triangles_[h].nbr[0] == from ? triangles_[h].nbr[1]
2484 : triangles_[h].nbr[0];
2487 if (h == crossing)
break;
2500 [[maybe_unused]]
const auto enterThroughGhost = [&](TriIndex g) -> TriIndex {
2501 const VertexIndex va = triangles_[g].v[0];
2502 const VertexIndex vb = triangles_[g].v[1];
2503 const bool onA = detail::orientationSignOf(fa, fb, filteredVertex(va)).value() == 0;
2504 const bool onB = detail::orientationSignOf(fa, fb, filteredVertex(vb)).value() == 0;
2506 VertexIndex w = onA ? va : vb;
2510 const VertexIndex first = vertexOrder(va, vb) >= 0 ? va : vb;
2511 const VertexIndex second = (first == va) ? vb : va;
2512 w = (unboundedBack || alongOrder(a, vertices_[first]) >= 0) ? first : second;
2515 return advanceFromVertex(w, g);
2518 const TriIndex inside = triangles_[g].nbr[2];
2519 return (inside != NO_TRI && !isGhost(inside)) ? inside : NO_TRI;
2524 [[maybe_unused]]
const auto enterAt = [&](
const auto& p, TriIndex start) -> TriIndex {
2525 const auto& v = triangles_[start].v;
2526 const auto fp = filteredPoint(p);
2527 int zeros = 0, z0 = -1, z1 = -1;
2528 for (
int k = 0; k < 3; ++k) {
2529 if (detail::orientationSignOf(filteredVertex(v[(k + 1) % 3]),
2530 filteredVertex(v[(k + 2) % 3]), fp).value() == 0) {
2532 if (z0 < 0) z0 = k;
else z1 = k;
2536 return advanceFromVertex(v[3 - z0 - z1], start);
2539 const TriIndex other = triangles_[start].nbr[z0];
2542 if (rayEnters(start, p) == 1)
return start;
2543 if (other != NO_TRI && !isGhost(other) && rayEnters(other, p) == 1)
return other;
2544 const VertexIndex e1 = v[(z0 + 1) % 3];
2545 const VertexIndex e2 = v[(z0 + 2) % 3];
2550 if (detail::orientationSignOf(filteredVertex(e1), filteredVertex(e2), fb)
2554 const VertexIndex fwd = vertexOrder(e2, e1) > 0 ? e1 : e2;
2555 if constexpr (!unboundedFront) {
2559 if (alongOrder(b, vertices_[fwd]) > 0) {
2563 return advanceFromVertex(fwd, start);
2574 [[maybe_unused]]
const auto emitTargetContacts = [&](TriIndex t) {
2575 const auto& v = triangles_[t].v;
2576 int zeros = 0, z0 = -1, z1 = -1;
2577 for (
int k = 0; k < 3; ++k) {
2578 if (detail::orientationSignOf(filteredVertex(v[(k + 1) % 3]),
2579 filteredVertex(v[(k + 2) % 3]), fb).value() == 0) {
2581 if (z0 < 0) z0 = k;
else z1 = k;
2585 emitFan(v[3 - z0 - z1], t);
2590 emit(triangles_[t].nbr[z0]);
2595 if constexpr (unboundedBack) {
2599 const TriIndex g = lineEntry(firstGhost_);
2603 t = enterThroughGhost(g);
2615 const TriIndex g = lineEntry(t);
2616 if (g == NO_TRI || !s.intersects(edgeSegment(Edge{g, 2}))) {
2619 t = enterThroughGhost(g);
2625 const std::size_t cap = triangles_.size() * 4 + 16;
2626 std::size_t guard = 0;
2627 while (!stop && t != NO_TRI) {
2628 if (++guard > cap)
break;
2631 if constexpr (!unboundedFront) {
2632 if (pointInClosure(b, t)) {
2633 emitTargetContacts(t);
2637 const auto& v = triangles_[t].v;
2644 for (
int k = 0; k < 3; ++k) {
2645 if (triangles_[t].nbr[k] == prev)
continue;
2646 const auto& u = vertices_[v[(k + 1) % 3]];
2647 const auto& w = vertices_[v[(k + 2) % 3]];
2648 const auto fu = filteredVertex(v[(k + 1) % 3]);
2649 const auto fw = filteredVertex(v[(k + 2) % 3]);
2650 const auto du = detail::orientationSignOf(fa, fb, fu).value();
2651 const auto dw = detail::orientationSignOf(fa, fb, fw).value();
2652 const bool straddleLine = (du > 0 && dw < 0) || (du < 0 && dw > 0);
2653 if (!straddleLine)
continue;
2655 if constexpr (unboundedFront) {
2658 const auto apexSide =
2659 detail::orientationSignOf(fu, fw, filteredVertex(v[k])).value();
2662 forward = dirCross != 0 && (dirCross > 0) != (apexSide > 0);
2664 const auto ea = detail::orientationSignOf(fu, fw, fa).value();
2665 const auto eb = detail::orientationSignOf(fu, fw, fb).value();
2666 forward = (ea > 0 && eb < 0) || (ea < 0 && eb > 0);
2668 if (forward) { exitK = k;
break; }
2672 t = triangles_[t].nbr[exitK];
2677 if constexpr (AllTriangles) {
2685 int onCount = 0, on0 = -1, on1 = -1;
2686 for (
int m = 0; m < 3; ++m) {
2687 if (detail::orientationSignOf(fa, fb, filteredVertex(v[m])).value() == 0) {
2689 if (on0 < 0) on0 = m;
else on1 = m;
2695 const int m = (onCount >= 2 && vertexOrder(v[on0], v[on1]) > 0) ? on1 : on0;
2696 t = advanceFromVertex(v[m], t);
2721 template <detail::TriangulationQuery OS>
2723 std::vector<TriangleType> out;
2742 template <detail::TriangulationQuery OS,
class Fn>
2746 if (!t.interiorsIntersect(s)) {
2749 if constexpr (std::is_invocable_v<Fn&, const TriangleType&>) {
2750 return detail::invokeVisitor(f, t);
2752 return detail::invokeVisitor(f,
id);
2770 template <detail::TriangulationQuery OS>
2772 std::vector<TriangleType> out;
2792 template <detail::TriangulationQuery OS,
class Fn>
2794 std::unordered_set<SegmentType> seen;
2797 for (
const auto&
edge : t.edges()) {
2798 if (!s.intersects(
edge)) {
2807 if constexpr (detail::has_label_v<SegmentLabel>) {
2808 auto se = segmentMap().find(seg);
2809 if (se == segmentMap().end()) {
2812 seg = edgeSegment(se->second);
2814 if (seen.insert(seg).second && detail::invokeVisitor(f, seg)) {
2837 template <detail::TriangulationQuery OS,
class Fn>
2840 if (s.interiorsIntersect(e)) {
2841 return detail::invokeVisitor(f, e);
2860 template <detail::TriangulationQuery OS>
2862 std::vector<SegmentType> out;
2880 template <detail::TriangulationQuery OS>
2882 std::vector<SegmentType> out;
2913 template <detail::TriangulationRegionQuery Q,
class Fn>
2915 if (firstGhost_ == 0)
return false;
2916 const std::vector<TriIndex> seeds = seedTrianglesIntersecting(shape);
2917 if (seeds.empty())
return false;
2918 return floodTriangleIdsIntersecting<false>(
2919 shape, seeds, [&](TriIndex t) {
return reportTriangle(f, t); });
2952 template <detail::ChainTraversal C,
class Fn>
2954 if (firstGhost_ == 0)
return false;
2961 std::unordered_set<TriId> seen;
2963 for (
const auto& e : c.orientedEdgesView()) {
2965 if (!seen.insert(t).second) {
2968 stop = reportTriangle(f, indexOf(t));
2997 template <Po
intConcept QueryPo
int>
2998 [[nodiscard]] std::optional<TriangleType>
locate(
const QueryPoint& p)
const {
3001 return std::nullopt;
3028 template <Po
intConcept QueryPo
int>
3029 [[nodiscard]]
bool contains(
const QueryPoint& shape)
const {
3038 template <detail::SegmentOrOriented S>
3043 return segmentInteriorContained(shape);
3050 template <detail::SegmentOrOriented S>
3051 [[nodiscard]]
bool segmentInteriorContained(
const S& shape)
const {
3061 return !visitTriangleIdsIntersecting<true>(shape, [&](TriIndex t) {
3071 for (std::int8_t side = 0; side < 3; ++side) {
3072 const Edge e{t, side};
3073 if (!edgeInDomain(e) && shape.interiorsIntersect(edgeSegment(e))) {
3084 template <detail::ChainTraversal C>
3086 if (shape.empty()) {
3089 if (shape.size() < 2) {
3092 return containsBoundary(shape.orientedEdgesView(), shape);
3096 template <detail::PolygonalRegion Q>
3101 if (!containsBoundary(shape.edges(), shape)) {
3110 if (shape.contains(witness)) {
3118 template <PolygonWithHolesConcept Q>
3120 if (shape.outer().empty()) {
3123 bool traced =
false;
3124 for (
const auto&
edge : shape.edges()) {
3133 if (!traced && !
contains(shape.outer()[0])) {
3137 if (shape.contains(witness)) {
3145 template <PolygonSetConcept Q>
3147 for (
const auto& component : shape.components()) {
3156 template <HalfplaneIntersectionConcept Q>
3158 if (shape.empty()) {
3161 if (!shape.isBounded()) {
3168 template <DiskConcept D>
3180 const std::vector<TriIndex> seeds = seedTrianglesIntersecting(shape);
3181 if (seeds.empty()) {
3184 bool reachesInside =
false;
3185 const bool escapes = floodTriangleIdsIntersecting<true>(shape, seeds, [&](TriIndex t) {
3187 if (triangle.interiorsIntersect(shape)) {
3191 reachesInside =
true;
3193 for (std::int8_t side = 0; side < 3; ++side) {
3194 if (isGhost(triangles_[t].nbr[side]) &&
3195 shape.interiorsIntersect(edgeSegment(Edge{t, side}))) {
3203 return !escapes && reachesInside;
3214 template <EmptyShapeConcept E>
3221 return std::visit([
this](
const auto& s) {
return this->
contains(s); }, shape.
variant());
3234 template <detail::TriangulationQuery Q>
3240 template <PolygonConcept Q>
3246 for (
const auto& e : shape.edges()) {
3251 return anyDomainComponent(
3252 [&](TriIndex t) {
return shape.contains(vertices_[triangles_[t].v[0]]); });
3256 template <PolygonWithHolesConcept Q>
3258 if (shape.outer().empty()) {
3261 for (
const auto&
edge : shape.edges()) {
3266 return anyDomainComponent(
3267 [&](TriIndex t) {
return shape.contains(vertices_[triangles_[t].v[0]]); });
3271 template <PolygonSetConcept Q>
3273 for (
const auto& component : shape.components()) {
3282 template <HalfplaneIntersectionConcept Q>
3284 for (TriIndex t = 0; t < static_cast<TriIndex>(triangles_.size()); ++t) {
3285 if (inDomain(t) && shape.intersects(triangleValue(t))) {
3293 template <EmptyShapeConcept E>
3300 return std::visit([
this](
const auto& s) {
return this->
intersects(s); }, shape.
variant());
3314 template <detail::TriangulationQuery Q>
3316 return contains(shape) && !meetsDomainBoundary(shape);
3320 template <PolygonConcept Q>
3326 return containsBoundary<true>(shape.edges(), shape);
3330 template <PolygonWithHolesConcept Q>
3332 if (shape.outer().empty()) {
3335 bool traced =
false;
3336 for (
const auto&
edge : shape.edges()) {
3349 if (shape.contains(witness)) {
3357 template <PolygonSetConcept Q>
3359 for (
const auto& component : shape.components()) {
3368 template <HalfplaneIntersectionConcept Q>
3370 if (shape.empty()) {
3373 if (!shape.isBounded()) {
3383 template <EmptyShapeConcept E>
3390 return std::visit([
this](
const auto& s) {
return this->
interiorContains(s); },
3404 template <Po
intConcept QueryPo
int>
3410 template <detail::TriangulationQuery Q>
3420 if (t.interiorsIntersect(shape)) {
3423 return anyEdgeOf(t, [&](
const SegmentType& e, Edge handle) {
3424 return interiorEdge(handle) && e.interiorsIntersect(shape);
3430 template <PolygonConcept Q>
3436 for (
const auto& e : shape.edges()) {
3441 return anyDomainComponent(
3442 [&](TriIndex t) {
return shape.interiorsIntersect(triangleValue(t)); });
3446 template <PolygonWithHolesConcept Q>
3448 if (shape.outer().empty()) {
3451 for (
const auto&
edge : shape.edges()) {
3456 return anyDomainComponent(
3457 [&](TriIndex t) {
return shape.interiorsIntersect(triangleValue(t)); });
3461 template <PolygonSetConcept Q>
3463 for (
const auto& component : shape.components()) {
3472 template <HalfplaneIntersectionConcept Q>
3474 for (TriIndex t = 0; t < static_cast<TriIndex>(triangles_.size()); ++t) {
3483 template <EmptyShapeConcept E>
3498 auto se = segmentMap().find(s);
3499 return se != segmentMap().end() && bit(triangles_[se->second.tri].constrainedMask, se->second.side);
3504 auto se = segmentMap().find(s);
3505 if (se == segmentMap().end()) {
3508 const Edge e = se->second;
3509 setBit(triangles_[e.tri].constrainedMask, e.side, value);
3510 const Edge m = mirror(e);
3511 if (m.tri != NO_TRI) {
3512 setBit(triangles_[m.tri].constrainedMask, m.side, value);
3530 template <
class L = TriangleLabel>
3531 requires(detail::has_label_v<L>)
3533 const TriIndex
id = idOf(t);
3534 assert(inDomain(
id) &&
"label(): triangle is not part of the triangulation");
3535 return triangles_[id].triLabel;
3539 template <
class L = TriangleLabel>
3540 requires(detail::has_label_v<L>)
3542 const TriIndex
id = idOf(t);
3543 assert(inDomain(
id) &&
"label(): triangle is not part of the triangulation");
3544 return triangles_[id].triLabel;
3559 template <
class L = SegmentLabel>
3560 requires(detail::has_label_v<L>)
3562 auto se = segmentMap().find(s);
3563 assert(se != segmentMap().end() &&
"label(): segment is not an edge of the triangulation");
3564 return se->second.segLabel;
3568 template <
class L = SegmentLabel>
3569 requires(detail::has_label_v<L>)
3571 auto se = segmentMap().find(s);
3572 assert(se != segmentMap().end() &&
"label(): segment is not an edge of the triangulation");
3573 return se->second.segLabel;
3580 auto se = segmentMap().find(s);
3581 return se != segmentMap().end() && flippableEdge(se->second);
3595 auto se = segmentMap().find(s);
3596 if (se == segmentMap().end()) {
3597 return std::nullopt;
3599 const Edge e = se->second;
3600 if (!flippableEdge(e)) {
3601 return std::nullopt;
3603 const TriIndex t = e.tri;
3604 const TriIndex t2 = mirror(e).tri;
3609 segmentMap().erase(s);
3613 return edgeSegment(Edge{t, 1});
3628 template <
class EdgeRange>
3631 std::unordered_set<TriIndex> claimed;
3632 for (
const auto& s :
edges) {
3633 const auto se = segmentMap().find(
SegmentType(s[0], s[1]));
3634 if (se == segmentMap().end() || !flippableEdge(se->second)) {
3637 const Edge e = se->second;
3638 if (!claimed.insert(e.tri).second || !claimed.insert(mirror(e).tri).second) {
3659 template <
class EdgeRange>
3661 std::optional<std::vector<SegmentType>>
flip(
const EdgeRange&
edges) {
3663 return std::nullopt;
3665 std::vector<SegmentType> diagonals;
3666 for (
const auto& s :
edges) {
3704 const bool inserted = insertVertexImpl(p).has_value();
3731 const auto inserted = insertVertexImpl(p);
3735 const auto [vp, start] = *inserted;
3738 std::vector<SegmentType> suspect;
3739 visitVertexFan(start, vp, [&](TriIndex cur) {
3740 if (!isGhost(cur)) {
3742 edgeSegment(Edge{cur,
static_cast<std::int8_t
>(localIndex(cur, vp))}));
3757 for (TriIndex t = 0; t < static_cast<TriIndex>(triangles_.size()); ++t) {
3758 const auto& T = triangles_[t];
3760 !(
orientationSign(vertices_[T.v[0]], vertices_[T.v[1]], vertices_[T.v[2]]) > 0)) {
3763 for (
int i = 0; i < 3; ++i) {
3764 const TriIndex t2 = T.nbr[i];
3769 for (
int s = 0; s < 3; ++s) {
3770 if (triangles_[t2].nbr[s] == t) {
3777 const VertexIndex a = T.v[(i + 1) % 3];
3778 const VertexIndex b = T.v[(i + 2) % 3];
3779 const VertexIndex c = triangles_[t2].v[(j + 1) % 3];
3780 const VertexIndex d = triangles_[t2].v[(j + 2) % 3];
3781 if (!((a == c && b == d) || (a == d && b == c))) {
3796 for (
const auto &t : triangulation.
triangles()) {
3805 using VertexIndex = std::int32_t;
3806 using TriIndex = std::int32_t;
3807 static constexpr TriIndex NO_TRI = -1;
3808 static constexpr VertexIndex NO_VERTEX = -1;
3814 TriIndex tri = NO_TRI;
3815 std::int8_t side = 0;
3826 std::array<VertexIndex, 3> v{NO_TRI, NO_TRI, NO_TRI};
3827 std::array<TriIndex, 3> nbr{NO_TRI, NO_TRI, NO_TRI};
3828 std::uint8_t constrainedMask = 0;
3829 std::uint8_t outOfDomain = 0;
3830 mutable std::uint8_t walkMark = 0;
3833 static_assert(
sizeof(Tri) == 28 || detail::has_label_v<TriangleLabel>,
3834 "Tri should stay 28 bytes when unlabeled");
3840 using VertexCoordinate = detail::incircle_coordinate_t<typename PointType::NumberType>;
3843 void syncVertexApproximations() {
3844 if constexpr (detail::filtersSign<VertexCoordinate>) {
3845 vertexApproximations_.clear();
3846 vertexApproximations_.reserve(vertices_.size());
3847 for (
const auto&
vertex : vertices_) {
3848 vertexApproximations_.push_back(detail::approximatePoint(
vertex));
3854 VertexIndex appendVertex(
const PointType& p) {
3855 const VertexIndex
id =
static_cast<VertexIndex
>(vertices_.size());
3856 vertices_.push_back(p);
3857 if constexpr (detail::filtersSign<VertexCoordinate>) {
3858 vertexApproximations_.push_back(detail::approximatePoint(p));
3865 [[nodiscard]]
auto filteredVertex(VertexIndex v)
const {
3866 const auto index =
static_cast<std::size_t
>(v);
3867 return detail::filtered<VertexCoordinate>(vertices_[index], vertexApproximations_, index);
3875 template <
class QueryPo
int>
3876 [[nodiscard]]
static auto filteredPoint(
const QueryPoint& p) {
3877 using QueryCoordinate =
3878 detail::incircle_coordinate_t<
typename PointType::NumberType,
3879 typename QueryPoint::NumberType>;
3880 return detail::filtered<QueryCoordinate>(p);
3883 std::vector<PointType> vertices_;
3892 std::vector<detail::ApproximatePoint> vertexApproximations_;
3893 std::vector<Tri> triangles_;
3897 mutable std::unordered_map<SegmentType, Edge> segToEdge_;
3898 mutable bool mapStale_ =
false;
3902 std::vector<TriangleType> holeWitnesses_;
3908 std::vector<TriIndex> vertexTri_;
3909 static constexpr VertexIndex GHOST = 0;
3910 TriIndex firstGhost_ = 0;
3911 std::size_t domainTriangleCount_ = 0;
3912 mutable TriIndex hint_ = NO_TRI;
3913 mutable std::mt19937 rng_;
3927 struct Kirkpatrick {
3935 static constexpr std::uint32_t FULL_TURN = 0x80000000u;
3941 std::vector<Cell> cells;
3945 std::vector<std::uint32_t> run;
3946 std::vector<std::uint32_t> roots;
3947 std::vector<std::array<VertexIndex, 3>> rootShape;
3949 std::vector<PointType> extra;
3950 std::vector<detail::ApproximatePoint> extraApprox;
3953 std::shared_ptr<const Kirkpatrick> pointLocation_;
3962 static constexpr bool exactDescent =
3963 detail::extended_integral<NumberType> || detail::arbitraryPrecision<NumberType>;
3971 static constexpr std::size_t pointLocationMaxStar = 10;
3978 static constexpr std::size_t pointLocationTopSize = 8;
3986 std::size_t revision_ = 0;
3987 std::size_t pointLocationRevision_ = 0;
3997 [[nodiscard]] TriIndex pointLocationSeed(
const PointType& p,
bool& answered)
const {
3999 if (!pointLocation_) {
4002 bool settled =
false;
4003 const TriIndex seed = kirkpatrickSeed(p, settled);
4004 answered = settled && exactDescent && pointLocationRevision_ == revision_;
4005 return realTriangle(seed) ? seed : NO_TRI;
4010 [[nodiscard]]
auto kirkpatrickVertex(
const Kirkpatrick& kp, VertexIndex v)
const {
4011 const std::size_t i =
static_cast<std::size_t
>(v);
4012 const std::size_t meshVertices = vertices_.size();
4013 return i < meshVertices
4015 : detail::filtered<VertexCoordinate>(kp.extra[i - meshVertices],
4016 kp.extraApprox, i - meshVertices);
4026 template <
class QueryPo
int>
4027 [[nodiscard]] TriIndex kirkpatrickSeed(
const QueryPoint& p,
bool& settled)
const {
4028 const Kirkpatrick& kp = *pointLocation_;
4032 const auto q = filteredPoint(p);
4035 const auto leftOfFrom = [&](
const auto& a, VertexIndex b) {
4036 return !(detail::orientationSignOf(a, kirkpatrickVertex(kp, b), q).value() < 0);
4038 const auto leftOf = [&](VertexIndex a, VertexIndex b) {
4039 return leftOfFrom(kirkpatrickVertex(kp, a), b);
4044 std::uint32_t cell = ~0u;
4045 for (std::size_t i = 0; i < kp.roots.size(); ++i) {
4046 const auto& corners = kp.rootShape[i];
4047 if (leftOf(corners[0], corners[1]) && leftOf(corners[1], corners[2]) &&
4048 leftOf(corners[2], corners[0])) {
4059 const auto& current = kp.cells[cell];
4060 const std::uint32_t count = current.runCount & ~Kirkpatrick::FULL_TURN;
4062 settled = current.runCount == 0;
4063 return static_cast<TriIndex
>(current.runBegin);
4065 const std::uint32_t*
const fan = kp.run.data() + current.runBegin;
4066 cell = fan[2 * count - 1];
4069 kirkpatrickVertex(kp,
static_cast<VertexIndex
>(current.apex));
4073 bool inside = (current.runCount & Kirkpatrick::FULL_TURN) == 0 ||
4074 leftOfFrom(apex,
static_cast<VertexIndex
>(fan[0]));
4075 for (std::uint32_t m = 1; m < count; ++m) {
4076 const bool beyond = leftOfFrom(apex,
static_cast<VertexIndex
>(fan[2 * m]));
4077 if (inside && !beyond) {
4078 cell = fan[2 * m - 1];
4096 static constexpr TriId triHandle(TriIndex t) {
4097 return TriId(
static_cast<std::uint32_t
>(t));
4099 static constexpr VertexId vertexHandle(VertexIndex v) {
4100 return VertexId(
static_cast<std::uint32_t
>(v));
4102 static constexpr TriIndex indexOf(
TriId t) {
return static_cast<TriIndex
>(t.index()); }
4103 static constexpr VertexIndex indexOf(
VertexId v) {
4104 return static_cast<VertexIndex
>(v.index());
4112 [[nodiscard]]
int firstVertex(TriIndex t)
const {
4113 const auto& v = triangles_[
static_cast<std::size_t
>(t)].v;
4115 for (
int i = 1; i < 3; ++i) {
4116 if (vertices_[
static_cast<std::size_t
>(v[i])] <
4117 vertices_[
static_cast<std::size_t
>(v[first])]) {
4128 [[nodiscard]]
int internalSide(TriIndex t,
int side)
const {
4129 return (firstVertex(t) + side + 2) % 3;
4134 [[nodiscard]]
bool realTriangle(TriIndex t)
const {
return t >= 0 && t < firstGhost_; }
4137 [[nodiscard]]
bool realVertex(VertexIndex v)
const {
4138 return v > GHOST &&
static_cast<std::size_t
>(v) < vertices_.size();
4146 bool reportTriangle(Fn& f, TriIndex t)
const {
4147 if constexpr (std::is_invocable_v<Fn&, const TriangleType&>) {
4148 return detail::invokeVisitor(f, triangleValue(t));
4150 return detail::invokeVisitor(f, triHandle(t));
4155 [[nodiscard]] std::vector<TriangleType> trianglesOf(
const std::vector<TriId>& ids)
const {
4156 std::vector<TriangleType> out;
4157 out.reserve(ids.size());
4158 for (
const TriId id : ids) {
4159 out.push_back(triangleValue(indexOf(
id)));
4167 [[nodiscard]] VertexIndex vertexIndexAt(
const PointType& p)
const {
4168 const TriIndex start = locateIndex(p);
4169 if (start == NO_TRI || isGhost(start)) {
4172 const auto& sv = triangles_[start].v;
4173 for (
int i = 0; i < 3; ++i) {
4174 if (vertices_[sv[i]] == p) {
4184 [[nodiscard]] TriIndex fanSeedOf(VertexIndex w)
const {
4185 const TriIndex hinted = incidentTriangleOf(w);
4186 if (hinted != NO_TRI) {
4189 const TriIndex start = locateIndex(vertices_[
static_cast<std::size_t
>(w)]);
4190 if (start == NO_TRI) {
4193 const auto& v = triangles_[start].v;
4194 return (v[0] == w || v[1] == w || v[2] == w) ? start : NO_TRI;
4198 static constexpr bool bit(std::uint8_t mask,
int i) {
return (mask >> i) & 1; }
4200 static constexpr void setBit(std::uint8_t& mask,
int i,
bool value) {
4201 mask =
static_cast<std::uint8_t
>(value ? (mask | (1u << i)) : (mask & ~(1u << i)));
4204 static constexpr std::uint8_t mask(
bool b0,
bool b1,
bool b2) {
4205 return static_cast<std::uint8_t
>((b0 ? 1 : 0) | (b1 ? 2 : 0) | (b2 ? 4 : 0));
4209 [[nodiscard]]
bool isGhost(TriIndex t)
const {
return t >= firstGhost_; }
4215 [[nodiscard]]
bool inDomain(TriIndex t)
const {
4216 return t != NO_TRI && t < firstGhost_ && !triangles_[t].outOfDomain;
4223 [[nodiscard]]
bool blocksVisibility(TriIndex t,
int s)
const {
4224 return bit(triangles_[t].constrainedMask, s) || !inDomain(triangles_[t].nbr[s]);
4233 struct VisibilityCone {
4247 struct VisibilitySeeds {
4248 std::vector<VisibilityCone> cones;
4249 std::vector<std::pair<VertexIndex, bool>> direct;
4250 std::vector<std::size_t> arcs;
4251 bool located =
false;
4252 bool fullTurn =
false;
4255 [[nodiscard]] VisibilitySeeds visibilitySeeds(
const PointType& query)
const;
4262 [[nodiscard]] TriIndex inDomainTriangleAt(
const PointType& query, TriIndex start)
const;
4269 template <
class OnVertex,
class OnBlocked>
4270 void expandVisibility(
const PointType& origin, VisibilityCone start,
4271 std::vector<VisibilityCone>& scratch,
4272 OnVertex onVertex, OnBlocked onBlocked)
const;
4277 [[nodiscard]] std::vector<VertexIndex> visibleIds(
const PointType& query,
4278 const VisibilitySeeds& seeds,
4279 bool grazing)
const;
4283 [[nodiscard]] std::vector<std::vector<VertexIndex>> clearVisibleAdjacency()
const;
4288 [[nodiscard]] std::vector<std::vector<VertexIndex>> visibleAdjacency()
const;
4292 [[nodiscard]] std::vector<std::vector<VertexIndex>> wallNeighbors()
const;
4299 [[nodiscard]]
bool passesThrough(VertexIndex m)
const;
4305 [[nodiscard]] VertexIndex nextVertexAlongRay(
const PointType& tail, VertexIndex current)
const;
4308 [[nodiscard]] std::int8_t findSide(TriIndex
x, TriIndex target)
const {
4309 const auto& n = triangles_[
x].nbr;
4310 return static_cast<std::int8_t
>(n[0] == target ? 0 : (n[1] == target ? 1 : 2));
4315 [[nodiscard]]
bool edgeInDomain(Edge e)
const {
4316 return inDomain(e.tri) || inDomain(mirror(e).tri);
4321 [[nodiscard]]
bool interiorEdge(Edge e)
const {
4322 return inDomain(e.tri) && inDomain(mirror(e).tri);
4327 [[nodiscard]]
bool boundaryEdge(Edge e)
const {
return edgeInDomain(e) && !interiorEdge(e); }
4332 [[nodiscard]]
bool anyEdgeOf(
const TriangleType& t, Fn f)
const {
4333 for (
const auto&
edge : t.edges()) {
4335 const auto se = segmentMap().find(seg);
4336 if (se != segmentMap().end() && f(seg, se->second)) {
4347 template <detail::TriangulationQuery Q>
4348 [[nodiscard]]
bool meetsDomainBoundary(
const Q& shape)
const {
4350 return anyEdgeOf(t, [&](
const SegmentType& e, Edge handle) {
4351 return boundaryEdge(handle) && e.intersects(shape);
4362 template <
bool Interior = false,
class EdgeRange,
class Q>
4363 [[nodiscard]]
bool containsBoundary(
const EdgeRange&
edges,
const Q& shape)
const {
4364 const auto held = [&](
const auto& piece) {
4365 if constexpr (Interior) {
4371 bool traced =
false;
4372 for (
const auto& e :
edges) {
4381 return traced || held(shape[0]);
4393 std::vector<VertexIndex> expandRing(
const std::vector<VertexIndex>& ring,
4394 const std::vector<VertexIndex>& candidates)
const {
4395 std::vector<VertexIndex> expanded;
4396 expanded.reserve(ring.size());
4397 std::vector<VertexIndex> onEdge;
4398 for (std::size_t i = 0; i < ring.size(); ++i) {
4399 const VertexIndex a = ring[i];
4400 const VertexIndex b = ring[(i + 1) % ring.size()];
4401 expanded.push_back(a);
4404 for (
const VertexIndex v : candidates) {
4405 if (v != a && v != b &&
edge.contains(vertices_[v])) {
4406 onEdge.push_back(v);
4409 if (onEdge.empty()) {
4414 std::sort(onEdge.begin(), onEdge.end(),
4415 [
this](VertexIndex p, VertexIndex q) { return vertices_[p] < vertices_[q]; });
4416 if (vertices_[b] < vertices_[a]) {
4417 std::reverse(onEdge.begin(), onEdge.end());
4419 expanded.insert(expanded.end(), onEdge.begin(), onEdge.end());
4428 [[nodiscard]] TriIndex triangleLeftOf(VertexIndex a, VertexIndex b)
const {
4429 const auto it = segmentMap().find(
SegmentType(vertices_[a], vertices_[b]));
4430 if (it == segmentMap().end()) {
4433 for (
const Edge& e : {it->second, mirror(it->second)}) {
4434 if (e.tri == NO_TRI) {
4437 const auto& v = triangles_[e.tri].v;
4438 if (v[(e.side + 1) % 3] == a && v[(e.side + 2) % 3] == b) {
4447 [[nodiscard]] Edge mirror(Edge e)
const {
4448 const TriIndex t2 = triangles_[e.tri].nbr[e.side];
4450 return Edge{NO_TRI, 0};
4452 return Edge{t2, findSide(t2, e.tri)};
4456 [[nodiscard]]
TriangleType triangleValue(TriIndex t)
const {
4457 const auto& T = triangles_[t];
4458 TriangleType tri(vertices_[T.v[0]], vertices_[T.v[1]], vertices_[T.v[2]]);
4461 if constexpr (detail::has_label_v<TriangleLabel>) {
4462 tri.label() = T.triLabel;
4470 [[nodiscard]]
SegmentType edgeSegment(Edge e)
const {
4471 const auto& T = triangles_[e.tri];
4472 SegmentType s(vertices_[T.v[(e.side + 1) % 3]], vertices_[T.v[(e.side + 2) % 3]]);
4473 if constexpr (detail::has_label_v<SegmentLabel>) {
4474 s.label() = e.segLabel;
4481 for (
const auto& p : t.vertices()) {
4482 if (p != s[0] && p != s[1]) {
4491 [[nodiscard]] TriIndex idOf(
const TriangleType& t)
const {
4492 const auto edges = t.edges();
4493 auto se = segmentMap().find(
edges[0]);
4494 if (se == segmentMap().end()) {
4498 const Edge e = se->second;
4499 if (vertices_[triangles_[e.tri].v[e.side]] == apex) {
4502 const Edge m = mirror(e);
4503 if (m.tri != NO_TRI && !isGhost(m.tri) && vertices_[triangles_[m.tri].v[m.side]] == apex) {
4511 template <
class QueryPo
int>
4512 [[nodiscard]]
bool pointInClosure(
const QueryPoint& p, TriIndex t)
const {
4513 const auto& v = triangles_[t].v;
4514 const auto fq = filteredPoint(p);
4515 const auto f0 = filteredVertex(v[0]);
4516 const auto f1 = filteredVertex(v[1]);
4517 const auto f2 = filteredVertex(v[2]);
4518 return detail::orientationSignOf(f0, f1, fq).value() >= 0 &&
4519 detail::orientationSignOf(f1, f2, fq).value() >= 0 &&
4520 detail::orientationSignOf(f2, f0, fq).value() >= 0;
4524 [[nodiscard]]
int localIndex(TriIndex t, VertexIndex w)
const {
4525 const auto& v = triangles_[t].v;
4526 return v[0] == w ? 0 : (v[1] == w ? 1 : 2);
4534 [[nodiscard]] TriIndex rotateAroundVertex(TriIndex cur, VertexIndex w, TriIndex from)
const {
4535 const int lw = localIndex(cur, w);
4536 int s1 = -1, s2 = -1;
4537 for (
int s = 0; s < 3; ++s) {
4539 if (s1 < 0) s1 = s;
else s2 = s;
4542 return triangles_[cur].nbr[triangles_[cur].nbr[s1] == from ? s2 : s1];
4551 void visitVertexFan(TriIndex start, VertexIndex w, Fn fn)
const {
4552 TriIndex cur = start, from = NO_TRI;
4553 std::size_t g = 0, lim = triangles_.size() + 1;
4556 const TriIndex next = rotateAroundVertex(cur, w, from);
4559 }
while (cur != start && cur != NO_TRI && ++g < lim);
4563 [[nodiscard]] TriIndex firstInDomainTriangle()
const {
4564 for (TriIndex t = 0; t < firstGhost_; ++t) {
4565 if (!triangles_[t].outOfDomain)
return t;
4582 bool anyDomainComponent(Fn&& f)
const {
4583 const TriIndex first = firstInDomainTriangle();
4584 if (first == NO_TRI) {
4587 if (holeWitnesses_.empty()) {
4590 std::vector<char> seen(triangles_.size(), 0);
4591 std::vector<TriIndex> stack;
4592 for (TriIndex t = 0; t < firstGhost_; ++t) {
4593 if (!inDomain(t) || seen[t]) {
4601 while (!stack.empty()) {
4602 const TriIndex cur = stack.back();
4604 for (
int s = 0; s < 3; ++s) {
4605 const TriIndex nb = triangles_[cur].nbr[s];
4606 if (nb != NO_TRI && !seen[nb] && inDomain(nb)) {
4608 stack.push_back(nb);
4621 [[nodiscard]] std::vector<TriIndex> seedTrianglesIntersecting(
const Q& shape)
const {
4622 std::vector<TriIndex> seeds;
4623 if constexpr (PointConcept<Q>) {
4626 const TriIndex t = locateIndex(shape);
4627 if (t != NO_TRI && !isGhost(t)) {
4630 }
else if constexpr (HalfplaneConcept<Q>) {
4636 for (TriIndex g = firstGhost_; g < static_cast<TriIndex>(triangles_.size()); ++g) {
4637 if (edgeSegment(Edge{g, 2}).intersects(shape)) {
4638 seeds.push_back(triangles_[g].nbr[2]);
4642 }
else if constexpr (DiskConcept<Q>) {
4648 const TriIndex t = locateIndex(shape[0]);
4649 if (t != NO_TRI && !isGhost(t)) {
4652 for (TriIndex g = firstGhost_; g < static_cast<TriIndex>(triangles_.size()); ++g) {
4653 if (edgeSegment(Edge{g, 2}).intersects(shape)) {
4654 seeds.push_back(triangles_[g].nbr[2]);
4663 for (
const auto& e : shape.edges()) {
4664 TriIndex found = NO_TRI;
4669 if (found != NO_TRI) {
4670 seeds.push_back(found);
4679 anyDomainComponent([&](TriIndex t) {
4680 if (shape.contains(vertices_[triangles_[t].v[0]])) {
4701 template <
bool AllTriangles,
class Q,
class Fn>
4702 bool floodTriangleIdsIntersecting(
const Q& shape,
const std::vector<TriIndex>& seeds, Fn f)
const {
4703 std::vector<TriIndex> marked;
4704 struct MarkClearer {
4705 const std::vector<Tri>& tris;
4706 const std::vector<TriIndex>& marked;
4707 ~MarkClearer() {
for (TriIndex t : marked) tris[t].walkMark = 0; }
4708 } markClearer{triangles_, marked};
4710 std::vector<TriIndex> stack;
4713 const auto consider = [&](TriIndex t) {
4714 if (t == NO_TRI || isGhost(t) || triangles_[t].walkMark) {
4717 triangles_[t].walkMark = 1;
4718 marked.push_back(t);
4723 for (
const TriIndex s : seeds) {
4728 while (!stop && !stack.empty()) {
4729 const TriIndex t = stack.back();
4731 if ((AllTriangles || inDomain(t)) && f(t)) {
4738 for (
const VertexIndex w : triangles_[t].v) {
4739 TriIndex cur = t, from = NO_TRI;
4740 std::size_t g = 0, lim = triangles_.size() + 1;
4743 const TriIndex next = rotateAroundVertex(cur, w, from);
4746 }
while (cur != t && cur != NO_TRI && ++g < lim);
4758 void registerSides(TriIndex t) {
4759 for (std::int8_t s = 0; s < 3; ++s) {
4760 segmentMap()[edgeSegment(Edge{t, s})] = Edge{t, s};
4762 noteVertexIncidence(t);
4767 void noteVertexIncidence(TriIndex t) {
4768 if (t < 0 || t >= firstGhost_) {
4771 if (vertexTri_.size() < vertices_.size()) {
4772 vertexTri_.resize(vertices_.size(), NO_TRI);
4774 for (
const VertexIndex w : triangles_[t].v) {
4776 vertexTri_[
static_cast<std::size_t
>(w)] = t;
4785 [[nodiscard]] TriIndex incidentTriangleOf(VertexIndex w)
const {
4786 if (
static_cast<std::size_t
>(w) >= vertexTri_.size()) {
4789 const TriIndex t = vertexTri_[
static_cast<std::size_t
>(w)];
4790 if (t == NO_TRI || t >= firstGhost_) {
4793 const auto& v = triangles_[t].v;
4794 return (v[0] == w || v[1] == w || v[2] == w) ? t : NO_TRI;
4810 void materializeSegmentMap()
const {
4815 segToEdge_.reserve(
static_cast<std::size_t
>(std::max(firstGhost_, TriIndex(0))) * 2);
4816 for (TriIndex t = 0; t < firstGhost_; ++t) {
4817 for (std::int8_t s = 0; s < 3; ++s) {
4818 segToEdge_[edgeSegment(Edge{t, s})] = Edge{t, s};
4825 [[nodiscard]] std::unordered_map<SegmentType, Edge>& segmentMap()
const {
4827 materializeSegmentMap();
4834 auto makeVertexInterner(std::unordered_map<PointType, VertexIndex>& vid) {
4835 return [
this, &vid](
const PointType& p) -> VertexIndex {
4836 auto it = vid.find(p);
4837 if (it != vid.end()) {
4840 VertexIndex
id =
static_cast<VertexIndex
>(vertices_.size());
4841 vertices_.push_back(p);
4866 static std::vector<std::array<VertexIndex, 3>>
4867 delaunayTriples(
const std::vector<PointType>& pts,
4868 const std::vector<detail::ApproximatePoint>& approximations) {
4869 const VertexIndex n =
static_cast<VertexIndex
>(pts.size());
4870 std::vector<std::array<VertexIndex, 3>> out;
4874 const VertexIndex INF = n;
4880 const auto fp = [&](VertexIndex v) {
4881 const auto index =
static_cast<std::size_t
>(v);
4882 return detail::filtered<VertexCoordinate>(pts[index], approximations, index);
4891 std::array<VertexIndex, 3> v{};
4892 std::array<int, 3> nbr{-1, -1, -1};
4895 std::vector<LTri> tri;
4896 std::vector<int> freeList;
4898 auto newTri = [&](VertexIndex a, VertexIndex b, VertexIndex d) ->
int {
4900 if (!freeList.empty()) {
4901 id = freeList.back();
4902 freeList.pop_back();
4903 tri[id] = LTri{{a, b, d}, {-1, -1, -1},
false};
4905 id =
static_cast<int>(tri.size());
4906 tri.push_back(LTri{{a, b, d}, {-1, -1, -1},
false});
4911 auto isGhost = [&](
int t) {
4912 const auto& q = tri[t].v;
4913 return q[0] == INF || q[1] == INF || q[2] == INF;
4928 auto inDisk = [&](
int t, VertexIndex p) ->
bool {
4929 const auto& q = tri[t].v;
4930 const int inf = q[0] == INF ? 0 : (q[1] == INF ? 1 : (q[2] == INF ? 2 : -1));
4932 return detail::inCircleSignOf(fp(q[0]), fp(q[1]),
4934 std::partial_ordering::greater;
4936 const VertexIndex u = q[(inf + 1) % 3];
4937 const VertexIndex w = q[(inf + 2) % 3];
4939 detail::orientationSignOf(fp(u), fp(w), fp(p)).value();
4948 return dotSign(pts[p] - pts[u], pts[w] - pts[u]) > 0 &&
4949 dotSign(pts[p] - pts[w], pts[u] - pts[w]) > 0;
4959 detail::orientationSignOf(fp(0), fp(1), fp(c)).value() == 0) {
4965 const std::array<VertexIndex, 3> seed =
4966 detail::orientationSignOf(fp(0), fp(1), fp(c)).value() > 0
4967 ? std::array<VertexIndex, 3>{0, 1, c}
4968 : std::array<VertexIndex, 3>{1, 0, c};
4969 const int seedTris[4] = {
4970 newTri(seed[0], seed[1], seed[2]),
4971 newTri(seed[1], seed[0], INF),
4972 newTri(seed[2], seed[1], INF),
4973 newTri(seed[0], seed[2], INF),
4978 for (
int t : seedTris) {
4979 for (
int s = 0; s < 3; ++s) {
4980 const VertexIndex a = tri[t].v[(s + 1) % 3];
4981 const VertexIndex b = tri[t].v[(s + 2) % 3];
4982 for (
int u : seedTris) {
4983 for (
int q = 0; q < 3; ++q) {
4984 if (tri[u].v[(q + 1) % 3] == b && tri[u].v[(q + 2) % 3] == a) {
4997 std::uint64_t rngState = 0x9e3779b97f4a7c15ULL;
4998 auto walk = [&](VertexIndex p,
int t) ->
int {
5000 const int64_t cap = int64_t(3) *
static_cast<int64_t
>(tri.size()) + 16;
5001 for (int64_t step = 0; step < cap; ++step) {
5005 rngState = rngState * 6364136223846793005ULL + 1442695040888963407ULL;
5006 const int begin =
static_cast<int>((rngState >> 33) % 3);
5008 for (
int k = 0; k < 3; ++k) {
5009 const int s = (begin + k) % 3;
5010 if (tri[t].nbr[s] == from) {
5013 const VertexIndex ea = tri[t].v[(s + 1) % 3];
5014 const VertexIndex eb = tri[t].v[(s + 2) % 3];
5015 if (detail::orientationSignOf(fp(ea), fp(eb),
5016 fp(p)).value() < 0) {
5017 next = tri[t].nbr[s];
5030 std::vector<int> cavity;
5037 std::vector<Bnd> boundary;
5043 std::vector<Spoke> spokes;
5045 int hint = seedTris[0];
5046 for (VertexIndex i = 0; i < n; ++i) {
5047 if (i == seed[0] || i == seed[1] || i == seed[2]) {
5050 const int start = walk(i, hint);
5051 if (!inDisk(start, i)) {
5061 tri[start].dead =
true;
5062 cavity.push_back(start);
5063 for (std::size_t qi = 0; qi < cavity.size(); ++qi) {
5064 const int t = cavity[qi];
5065 for (
int s = 0; s < 3; ++s) {
5066 const int nb = tri[t].nbr[s];
5070 if (inDisk(nb, i)) {
5071 tri[nb].dead =
true;
5072 cavity.push_back(nb);
5074 const int survSide =
5075 tri[nb].nbr[0] == t ? 0 : (tri[nb].nbr[1] == t ? 1 : 2);
5077 {tri[t].v[(s + 1) % 3], tri[t].v[(s + 2) % 3], nb, survSide});
5081 for (
int t : cavity) {
5082 freeList.push_back(t);
5090 for (
const Bnd& e : boundary) {
5091 const int nt = newTri(e.a, e.b, i);
5092 tri[nt].nbr[2] = e.surv;
5093 tri[e.surv].nbr[e.survSide] = nt;
5094 if (newReal < 0 && !isGhost(nt)) {
5098 for (
const auto& [
vertex, side] :
5099 {std::pair<VertexIndex, int>{e.b, 0}, std::pair<VertexIndex, int>{e.a, 1}}) {
5100 bool paired =
false;
5101 for (std::size_t k = 0; k < spokes.size(); ++k) {
5103 tri[nt].nbr[side] = spokes[k].tri;
5104 tri[spokes[k].tri].nbr[spokes[k].side] = nt;
5105 spokes[k] = spokes.back();
5112 spokes.push_back({
vertex, nt, side});
5121 for (
int t = 0; t < static_cast<int>(tri.size()); ++t) {
5125 const auto& q = tri[t].v;
5126 if (q[0] != INF && q[1] != INF && q[2] != INF) {
5127 out.push_back({q[0], q[1], q[2]});
5143 if (s1 == 0 || s2 == 0 || s3 == 0 || s4 == 0) {
5146 return ((s1 > 0) != (s2 > 0)) && ((s3 > 0) != (s4 > 0));
5150 [[nodiscard]] Edge edgeHandle(VertexIndex p, VertexIndex q)
const {
5151 auto se = segmentMap().find(
SegmentType(vertices_[p], vertices_[q]));
5152 return se == segmentMap().end() ? Edge{NO_TRI, 0} : se->second;
5155 [[nodiscard]]
bool edgeExists(VertexIndex p, VertexIndex q)
const {
5156 return segmentMap().contains(
SegmentType(vertices_[p], vertices_[q]));
5162 [[nodiscard]] std::vector<std::pair<VertexIndex, VertexIndex>>
5163 collectCrossings(VertexIndex va, VertexIndex vb)
const {
5166 std::vector<std::pair<VertexIndex, VertexIndex>> out;
5176 TriIndex t = NO_TRI;
5177 std::pair<VertexIndex, VertexIndex> entry{NO_TRI, NO_TRI};
5178 const auto enterFrom = [&](TriIndex k) {
5179 if (t != NO_TRI || k == NO_TRI || isGhost(k)) {
5182 const auto& v = triangles_[k].v;
5183 const int i = localIndex(k, va);
5184 const VertexIndex p = v[(i + 1) % 3];
5185 const VertexIndex q = v[(i + 2) % 3];
5186 if (properCross(A, B, vertices_[p], vertices_[q])) {
5187 t = triangles_[k].nbr[i];
5189 out.push_back({p, q});
5192 const TriIndex seed = incidentTriangleOf(va);
5193 if (seed != NO_TRI) {
5194 visitVertexFan(seed, va, enterFrom);
5198 for (TriIndex k = 0; k < firstGhost_ && t == NO_TRI; ++k) {
5199 const auto& v = triangles_[k].v;
5200 if (v[0] == va || v[1] == va || v[2] == va) {
5210 std::size_t guard = 0, cap = triangles_.size() + 1;
5211 while (++guard < cap) {
5212 const auto& v = triangles_[t].v;
5213 if (v[0] == vb || v[1] == vb || v[2] == vb) {
5217 for (
int k = 0; k < 3; ++k) {
5218 const VertexIndex p = v[(k + 1) % 3];
5219 const VertexIndex q = v[(k + 2) % 3];
5220 const bool sameEntry = (p == entry.first && q == entry.second) ||
5221 (p == entry.second && q == entry.first);
5225 if (properCross(A, B, vertices_[p], vertices_[q])) {
5228 out.push_back({p, q});
5235 t = triangles_[t].nbr[exitK];
5249 void insertConstraint(VertexIndex va, VertexIndex vb) {
5250 if (va == vb || edgeExists(va, vb)) {
5256 std::deque<std::pair<VertexIndex, VertexIndex>> queue;
5257 for (
const auto& pq : collectCrossings(va, vb)) {
5258 queue.push_back(pq);
5260 std::size_t guard = 0, cap = (queue.size() + 1) * (triangles_.size() + 1) * 4 + 64;
5261 while (!queue.empty() && ++guard < cap) {
5262 auto [p, q] = queue.front();
5264 const Edge e = edgeHandle(p, q);
5265 if (e.tri == NO_TRI || !properCross(A, B, vertices_[p], vertices_[q])) {
5268 if (!flippableEdge(e)) {
5269 queue.push_back({p, q});
5272 const VertexIndex r = triangles_[e.tri].v[e.side];
5273 const Edge m = mirror(e);
5274 const VertexIndex l = triangles_[m.tri].v[m.side];
5276 if (properCross(A, B, vertices_[r], vertices_[l])) {
5277 queue.push_back({r, l});
5294 void restoreConstrainedDelaunay() {
5295 std::vector<SegmentType> suspect;
5296 suspect.reserve(segmentMap().size());
5297 for (
const auto& [seg, handle] : segmentMap()) {
5299 suspect.push_back(seg);
5311 void markOutOfDomain(
const std::vector<TriIndex>& holeSeeds = {}) {
5312 std::vector<char> seen(triangles_.size(), 0);
5313 std::vector<TriIndex> stack;
5314 for (TriIndex g = firstGhost_; g < static_cast<TriIndex>(triangles_.size()); ++g) {
5318 std::size_t marked = 0;
5319 for (
const TriIndex seed : holeSeeds) {
5320 if (seed == NO_TRI || seen[seed]) {
5324 triangles_[seed].outOfDomain = 1;
5326 stack.push_back(seed);
5328 while (!stack.empty()) {
5329 const TriIndex t = stack.back();
5331 for (
int s = 0; s < 3; ++s) {
5332 if (bit(triangles_[t].constrainedMask, s)) {
5335 const TriIndex nb = triangles_[t].nbr[s];
5336 if (nb == NO_TRI || seen[nb]) {
5341 triangles_[nb].outOfDomain = 1;
5344 stack.push_back(nb);
5347 domainTriangleCount_ =
static_cast<std::size_t
>(firstGhost_) - marked;
5354 static std::vector<Polygon<PointType>> setOuters(
const PolygonSet<PointType>& set) {
5355 std::vector<Polygon<PointType>> outers;
5356 outers.reserve(set.componentCount());
5357 for (
const auto& component : set) {
5358 outers.push_back(component.outer());
5363 static std::vector<Polygon<PointType>> setHoles(
const PolygonSet<PointType>& set) {
5364 std::vector<Polygon<PointType>> holes;
5365 holes.reserve(set.holeCount());
5366 for (
const auto& component : set) {
5367 for (
const auto& hole : component.holes()) {
5368 holes.push_back(hole);
5387 template <
class Po
intRange,
class SegmentRange>
5388 void constructConstrained(
const std::vector<Polygon<PointType>>& outers,
5389 const PointRange& extraPoints,
5390 const SegmentRange& constraintSegments,
5391 const std::vector<Polygon<PointType>>& holes = {}) {
5392 std::unordered_map<PointType, VertexIndex> vid;
5393 const auto idOfPoint = makeVertexInterner(vid);
5394 for (
const auto& outer : outers) {
5395 for (std::size_t i = 0; i < outer.size(); ++i) {
5396 idOfPoint(outer[i]);
5399 for (
const auto& hole : holes) {
5400 for (std::size_t i = 0; i < hole.size(); ++i) {
5404 for (
const auto& p : extraPoints) {
5407 for (
const auto& s : constraintSegments) {
5413 syncVertexApproximations();
5414 auto triples = delaunayTriples(vertices_, vertexApproximations_);
5415 buildFromTriples(triples, std::vector<TriangleLabel>(triples.size()));
5423 for (VertexIndex i = 1; i < static_cast<VertexIndex>(vertices_.size()); ++i) {
5424 vid.emplace(vertices_[i], i);
5426 std::vector<std::vector<VertexIndex>> outerLoops;
5427 outerLoops.reserve(outers.size());
5428 for (
const auto& outer : outers) {
5429 std::vector<VertexIndex> loop;
5430 loop.reserve(outer.size());
5431 for (std::size_t i = 0; i < outer.size(); ++i) {
5432 loop.push_back(vid.at(outer[i]));
5434 outerLoops.push_back(std::move(loop));
5437 std::vector<std::vector<VertexIndex>> holeLoops;
5438 holeLoops.reserve(holes.size());
5439 for (
const auto& hole : holes) {
5440 std::vector<VertexIndex> ring;
5441 ring.reserve(hole.size());
5442 for (std::size_t i = 0; i < hole.size(); ++i) {
5443 ring.push_back(vid.at(hole[i]));
5445 holeLoops.push_back(std::move(ring));
5451 if (!holes.empty() || outers.size() > 1) {
5452 std::vector<VertexIndex> ringVertices;
5453 for (
const auto& ring : outerLoops) {
5454 ringVertices.insert(ringVertices.end(), ring.begin(), ring.end());
5456 for (
const auto& ring : holeLoops) {
5457 ringVertices.insert(ringVertices.end(), ring.begin(), ring.end());
5459 for (
auto& ring : outerLoops) {
5460 ring = expandRing(ring, ringVertices);
5462 for (
auto& ring : holeLoops) {
5463 ring = expandRing(ring, ringVertices);
5470 for (
const auto& ring : outerLoops) {
5471 for (std::size_t i = 0; i < ring.size(); ++i) {
5472 insertConstraint(ring[i], ring[(i + 1) % ring.size()]);
5475 for (
const auto& ring : holeLoops) {
5476 for (std::size_t i = 0; i < ring.size(); ++i) {
5477 insertConstraint(ring[i], ring[(i + 1) % ring.size()]);
5480 for (
const auto& s : constraintSegments) {
5481 const VertexIndex a = vid.at(
PointType(s[0]));
5482 const VertexIndex b = vid.at(
PointType(s[1]));
5484 insertConstraint(a, b);
5487 restoreConstrainedDelaunay();
5494 std::vector<TriIndex> holeSeeds;
5495 holeSeeds.reserve(holeLoops.size());
5496 for (
const auto& ring : holeLoops) {
5497 const TriIndex seed = triangleLeftOf(ring[0], ring[1 % ring.size()]);
5498 if (seed != NO_TRI && !isGhost(seed)) {
5499 holeSeeds.push_back(seed);
5500 holeWitnesses_.push_back(triangleValue(seed));
5503 markOutOfDomain(holeSeeds);
5509 if constexpr (detail::has_label_v<SegmentLabel>) {
5510 for (
const auto& s : constraintSegments) {
5512 if (it != segmentMap().end()) {
5513 it->second.segLabel = detail::copyLabel<SegmentLabel>(s);
5525 void buildFromTriples(std::vector<std::array<VertexIndex, 3>>& triples,
5526 const std::vector<TriangleLabel>& triLabels) {
5527 vertices_.insert(vertices_.begin(),
PointType{});
5528 syncVertexApproximations();
5530 for (std::size_t k = 0; k < triples.size(); ++k) {
5531 VertexIndex
x = triples[k][0] + 1,
y = triples[k][1] + 1, z = triples[k][2] + 1;
5536 "Triangulation: degenerate triangle");
5537 triangles_.push_back(Tri{{
x,
y, z}, {NO_TRI, NO_TRI, NO_TRI}, 0, 0, 0, triLabels[k]});
5539 firstGhost_ =
static_cast<TriIndex
>(triangles_.size());
5540 domainTriangleCount_ =
static_cast<std::size_t
>(firstGhost_);
5542 for (TriIndex t = 0; t < firstGhost_; ++t) {
5543 noteVertexIncidence(t);
5553 [[nodiscard]]
bool flippableEdge(Edge e)
const {
5554 const TriIndex t = e.tri;
5555 if (t == NO_TRI || bit(triangles_[t].constrainedMask, e.side)) {
5558 const TriIndex t2 = triangles_[t].nbr[e.side];
5559 if (t2 == NO_TRI || isGhost(t) || isGhost(t2)) {
5562 const Edge m = mirror(e);
5563 const VertexIndex c = triangles_[t].v[e.side];
5564 const VertexIndex a = triangles_[t].v[(e.side + 1) % 3];
5565 const VertexIndex b = triangles_[t].v[(e.side + 2) % 3];
5566 const VertexIndex d = triangles_[t2].v[m.side];
5567 const auto fc = filteredVertex(c);
5568 const auto fd = filteredVertex(d);
5569 const auto oa = detail::orientationSignOf(fc, fd, filteredVertex(a)).value();
5570 const auto ob = detail::orientationSignOf(fc, fd, filteredVertex(b)).value();
5571 return (oa > 0 && ob < 0) || (oa < 0 && ob > 0);
5579 bool flipEdge(Edge e) {
5580 if (!flippableEdge(e)) {
5583 const TriIndex t = e.tri;
5584 const int i = e.side;
5585 const Edge m = mirror(e);
5586 const TriIndex t2 = m.tri;
5587 const int j = m.side;
5589 const VertexIndex c = triangles_[t].v[i];
5590 const VertexIndex a = triangles_[t].v[(i + 1) % 3];
5591 const VertexIndex b = triangles_[t].v[(i + 2) % 3];
5592 const VertexIndex d = triangles_[t2].v[j];
5594 const TriIndex nCA = triangles_[t].nbr[(i + 2) % 3];
5595 const TriIndex nBC = triangles_[t].nbr[(i + 1) % 3];
5596 const TriIndex nDB = triangles_[t2].nbr[(j + 2) % 3];
5597 const TriIndex nAD = triangles_[t2].nbr[(j + 1) % 3];
5598 const bool cCA = bit(triangles_[t].constrainedMask, (i + 2) % 3);
5599 const bool cBC = bit(triangles_[t].constrainedMask, (i + 1) % 3);
5600 const bool cDB = bit(triangles_[t2].constrainedMask, (j + 2) % 3);
5601 const bool cAD = bit(triangles_[t2].constrainedMask, (j + 1) % 3);
5603 const int sAD = (nAD != NO_TRI) ? findSide(nAD, t2) : -1;
5604 const int sBC = (nBC != NO_TRI) ? findSide(nBC, t) : -1;
5606 triangles_[t].v = {c, a, d};
5607 triangles_[t].nbr = {nAD, t2, nCA};
5608 triangles_[t].constrainedMask = mask(cAD,
false, cCA);
5611 triangles_[t2].v = {c, d, b};
5612 triangles_[t2].nbr = {nDB, nBC, t};
5613 triangles_[t2].constrainedMask = mask(cDB, cBC,
false);
5617 triangles_[nAD].nbr[sAD] = t;
5620 triangles_[nBC].nbr[sBC] = t2;
5632 void reRegisterSides(TriIndex t) {
5633 for (std::int8_t s = 0; s < 3; ++s) {
5635 auto it = segmentMap().find(key);
5636 if (it == segmentMap().end()) {
5637 segmentMap().emplace(key, Edge{t, s});
5640 it->second.side = s;
5649 static void reserveExtra(std::vector<T>& v, std::size_t extra) {
5650 if (v.capacity() < v.size() + extra) {
5651 v.reserve(std::max(v.size() + extra, v.capacity() * 2));
5662 TriIndex makeRoom(
int k) {
5663 const TriIndex base = firstGhost_;
5664 const TriIndex oldSize =
static_cast<TriIndex
>(triangles_.size());
5665 assert(oldSize - base >= k);
5666 for (
int j = 0; j < k; ++j) {
5667 triangles_.push_back(triangles_[base + j]);
5669 for (
int j = 0; j < k; ++j) {
5670 const TriIndex moved = oldSize + j;
5671 for (
int s = 0; s < 3; ++s) {
5672 TriIndex& nb = triangles_[moved].nbr[s];
5673 assert(nb != NO_TRI);
5674 if (nb >= base && nb < base + k) {
5675 nb = nb - base + oldSize;
5677 for (
int q = 0; q < 3; ++q) {
5678 if (triangles_[nb].nbr[q] == base + j) {
5679 triangles_[nb].nbr[q] = moved;
5697 std::optional<std::pair<VertexIndex, TriIndex>> insertVertexImpl(
const PointType& p) {
5698 const TriIndex t0 = locateIndex(p);
5700 return std::nullopt;
5705 return growHull(t0, p);
5707 const auto& tv = triangles_[t0].v;
5708 for (
int k = 0; k < 3; ++k) {
5709 if (vertices_[tv[k]] == p) {
5710 return std::nullopt;
5716 const auto fp = filteredPoint(p);
5717 for (
int k = 0; k < 3; ++k) {
5718 if (detail::orientationSignOf(filteredVertex(tv[(k + 1) % 3]),
5719 filteredVertex(tv[(k + 2) % 3]), fp).value() == 0) {
5724 return splitTriangle(t0, p);
5726 return splitEdge(t0, onSide, p);
5731 std::pair<VertexIndex, TriIndex> splitTriangle(TriIndex t,
const PointType& p) {
5732 reserveExtra(vertices_, 1);
5733 reserveExtra(triangles_, 2);
5734 const VertexIndex vp = appendVertex(p);
5735 const TriIndex n1 = makeRoom(2);
5736 const TriIndex n2 = n1 + 1;
5740 const Tri old = triangles_[t];
5741 const VertexIndex a = old.v[0];
5742 const VertexIndex b = old.v[1];
5743 const VertexIndex c = old.v[2];
5747 triangles_[t] = Tri{{a, b, vp},
5748 {n1, n2, old.nbr[2]},
5749 mask(
false,
false, bit(old.constrainedMask, 2)),
5751 triangles_[n1] = Tri{{b, c, vp},
5752 {n2, t, old.nbr[0]},
5753 mask(
false,
false, bit(old.constrainedMask, 0)),
5755 triangles_[n2] = Tri{{c, a, vp},
5756 {t, n1, old.nbr[1]},
5757 mask(
false,
false, bit(old.constrainedMask, 1)),
5759 triangles_[old.nbr[0]].nbr[findSide(old.nbr[0], t)] = n1;
5760 triangles_[old.nbr[1]].nbr[findSide(old.nbr[1], t)] = n2;
5761 domainTriangleCount_ += old.outOfDomain ? 0 : 2;
5762 for (
const TriIndex
x : {t, n1, n2}) {
5775 std::pair<VertexIndex, TriIndex> splitEdge(TriIndex t,
int s,
const PointType& p) {
5776 const bool ghostSide = isGhost(triangles_[t].nbr[s]);
5777 reserveExtra(vertices_, 1);
5778 reserveExtra(triangles_, 2);
5780 const bool cUW = bit(triangles_[t].constrainedMask, s);
5781 const VertexIndex vp = appendVertex(p);
5782 const TriIndex n1 = makeRoom(ghostSide ? 1 : 2);
5788 const TriIndex across = triangles_[t].nbr[s];
5789 const Tri oldT = triangles_[t];
5790 const VertexIndex apex = oldT.v[s];
5791 const VertexIndex u = oldT.v[(s + 1) % 3];
5792 const VertexIndex w = oldT.v[(s + 2) % 3];
5793 const TriIndex nWApex = oldT.nbr[(s + 1) % 3];
5794 const TriIndex nApexU = oldT.nbr[(s + 2) % 3];
5795 const bool cWApex = bit(oldT.constrainedMask, (s + 1) % 3);
5796 const bool cApexU = bit(oldT.constrainedMask, (s + 2) % 3);
5802 const auto it = segmentMap().find(
SegmentType(vertices_[u], vertices_[w]));
5803 assert(it != segmentMap().end());
5804 halfLabel = it->second.segLabel;
5805 segmentMap().erase(it);
5811 const TriIndex n2 = n1 + 1;
5812 const TriIndex t2 = across;
5813 const int j = findSide(t2, t);
5814 const Tri oldT2 = triangles_[t2];
5815 const VertexIndex apex2 = oldT2.v[j];
5816 assert(oldT2.v[(j + 1) % 3] == w && oldT2.v[(j + 2) % 3] == u);
5817 const TriIndex nApex2W = oldT2.nbr[(j + 2) % 3];
5818 const TriIndex nUApex2 = oldT2.nbr[(j + 1) % 3];
5819 const bool cApex2W = bit(oldT2.constrainedMask, (j + 2) % 3);
5820 const bool cUApex2 = bit(oldT2.constrainedMask, (j + 1) % 3);
5822 triangles_[t] = Tri{{apex, u, vp},
5824 mask(cUW,
false, cApexU),
5826 triangles_[n1] = Tri{{apex, vp, w},
5828 mask(cUW, cWApex,
false),
5830 triangles_[t2] = Tri{{apex2, w, vp},
5832 mask(cUW,
false, cApex2W),
5834 triangles_[n2] = Tri{{apex2, vp, u},
5836 mask(cUW, cUApex2,
false),
5838 triangles_[nWApex].nbr[findSide(nWApex, t)] = n1;
5839 triangles_[nUApex2].nbr[findSide(nUApex2, t2)] = n2;
5840 domainTriangleCount_ += (oldT.outOfDomain ? 0 : 1) + (oldT2.outOfDomain ? 0 : 1);
5841 for (
const TriIndex
x : {t, n1, t2, n2}) {
5847 const TriIndex g = across;
5848 const Tri oldG = triangles_[g];
5849 assert(oldG.v[0] == u && oldG.v[1] == w && oldG.v[2] == GHOST && oldG.nbr[2] == t);
5850 const TriIndex gw = oldG.nbr[0];
5851 const TriIndex gu = oldG.nbr[1];
5853 const TriIndex g2 =
static_cast<TriIndex
>(triangles_.size());
5854 triangles_.push_back(Tri{{vp, w, GHOST},
5856 mask(
false,
false, cUW),
5858 triangles_[t] = Tri{{apex, u, vp},
5860 mask(cUW,
false, cApexU),
5862 triangles_[n1] = Tri{{apex, vp, w},
5864 mask(cUW, cWApex,
false),
5866 triangles_[g] = Tri{{u, vp, GHOST},
5868 mask(
false,
false, cUW),
5870 triangles_[nWApex].nbr[findSide(nWApex, t)] = n1;
5871 triangles_[gw].nbr[findSide(gw, g)] = g2;
5872 domainTriangleCount_ += oldT.outOfDomain ? 0 : 1;
5873 for (
const TriIndex
x : {t, n1}) {
5877 if constexpr (detail::has_label_v<SegmentLabel>) {
5878 segmentMap().at(
SegmentType(vertices_[u], vertices_[vp])).segLabel = halfLabel;
5879 segmentMap().at(
SegmentType(vertices_[vp], vertices_[w])).segLabel = halfLabel;
5883 return std::pair{vp, t};
5898 std::pair<VertexIndex, TriIndex> growHull(TriIndex g0,
const PointType& p) {
5899 const auto fp = filteredPoint(p);
5900 const auto visible = [&](TriIndex g) {
5901 const auto& gv = triangles_[g].v;
5902 return detail::orientationSignOf(filteredVertex(gv[0]), filteredVertex(gv[1]), fp)
5905 assert(isGhost(g0) && visible(g0));
5911 std::size_t guard = 0;
5912 const std::size_t ringCap = triangles_.size() + 1;
5913 TriIndex gStart = g0;
5914 while (visible(triangles_[gStart].nbr[1]) && ++guard < ringCap) {
5915 gStart = triangles_[gStart].nbr[1];
5917 std::vector<TriIndex> inner;
5918 std::vector<std::int8_t> innerSide;
5919 std::vector<VertexIndex> u;
5920 TriIndex g = gStart;
5922 while (visible(g) && ++guard < ringCap) {
5923 const TriIndex r = triangles_[g].nbr[2];
5925 innerSide.push_back(findSide(r, g));
5926 u.push_back(triangles_[g].v[0]);
5927 g = triangles_[g].nbr[0];
5929 u.push_back(triangles_[g].v[0]);
5930 const int m =
static_cast<int>(inner.size());
5933 reserveExtra(vertices_, 1);
5934 reserveExtra(triangles_, m == 1 ? 2 :
static_cast<std::size_t
>(m));
5935 const VertexIndex vp = appendVertex(p);
5936 const TriIndex nr = makeRoom(m);
5939 std::vector<TriIndex> dead(
static_cast<std::size_t
>(m));
5940 for (
int i = 0; i < m; ++i) {
5941 dead[i] = triangles_[inner[i]].nbr[innerSide[i]];
5943 const TriIndex prevG = triangles_[dead.front()].nbr[1];
5944 const TriIndex nextG = triangles_[dead.back()].nbr[0];
5949 const TriIndex ga = dead[0];
5952 gb =
static_cast<TriIndex
>(triangles_.size());
5953 triangles_.push_back(Tri{});
5960 for (
int i = 0; i < m; ++i) {
5961 const bool cBase = bit(triangles_[inner[i]].constrainedMask, innerSide[i]);
5962 triangles_[nr + i] = Tri{{u[i + 1], u[i], vp},
5963 {i > 0 ? nr + i - 1 : ga,
5964 i < m - 1 ? nr + i + 1 : gb, inner[i]},
5965 mask(
false,
false, cBase),
5967 triangles_[inner[i]].nbr[innerSide[i]] = nr + i;
5969 triangles_[ga] = Tri{{u.front(), vp, GHOST}, {gb, prevG, nr}, 0, 0, 0,
TriangleLabel{}};
5971 Tri{{vp, u.back(), GHOST}, {nextG, ga, nr + m - 1}, 0, 0, 0,
TriangleLabel{}};
5972 triangles_[prevG].nbr[0] = ga;
5973 triangles_[nextG].nbr[1] = gb;
5980 std::vector<TriIndex> holes(dead.begin() + 2, dead.end());
5981 std::sort(holes.begin(), holes.end());
5982 const auto isHole = [&](TriIndex t) {
5983 return std::binary_search(holes.begin(), holes.end(), t);
5985 TriIndex last =
static_cast<TriIndex
>(triangles_.size()) - 1;
5986 for (std::size_t h = 0; h < holes.size() && holes[h] < last;) {
5991 const TriIndex hole = holes[h];
5992 triangles_[hole] = triangles_[last];
5993 for (
int s = 0; s < 3; ++s) {
5994 const TriIndex nb = triangles_[hole].nbr[s];
5995 for (
int q = 0; q < 3; ++q) {
5996 if (triangles_[nb].nbr[q] == last) {
5997 triangles_[nb].nbr[q] = hole;
6004 triangles_.resize(triangles_.size() -
static_cast<std::size_t
>(m - 2));
6007 for (
int i = 0; i < m; ++i) {
6008 reRegisterSides(nr + i);
6010 domainTriangleCount_ +=
static_cast<std::size_t
>(m);
6021 void legalize(std::vector<SegmentType>& suspect) {
6022 std::size_t guard = 0;
6023 const std::size_t cap = triangles_.size() * triangles_.size() + 64;
6024 while (!suspect.empty() && ++guard < cap) {
6027 const auto se = segmentMap().find(s);
6028 if (se == segmentMap().end() || !flippableEdge(se->second)) {
6031 const Edge e = se->second;
6032 const Edge m = mirror(e);
6033 const auto& tv = triangles_[e.tri].v;
6034 const VertexIndex d = triangles_[m.tri].v[m.side];
6035 if (detail::inCircleSignOf(filteredVertex(tv[0]), filteredVertex(tv[1]),
6036 filteredVertex(tv[2]), filteredVertex(d)) !=
6037 std::partial_ordering::greater) {
6040 const TriIndex t = e.tri;
6041 const TriIndex t2 = m.tri;
6045 for (
const TriIndex
x : {t, t2}) {
6046 for (std::int8_t q = 0; q < 3; ++q) {
6047 suspect.push_back(edgeSegment(Edge{
x, q}));
6051 assert(suspect.empty() &&
"Triangulation: legalization did not terminate");
6059 [[nodiscard]]
bool checkEdgeMap()
const {
6060 for (TriIndex t = 0; t < firstGhost_; ++t) {
6061 for (std::int8_t s = 0; s < 3; ++s) {
6062 if (!segmentMap().
contains(edgeSegment(Edge{t, s}))) {
6067 for (
const auto& [seg, e] : segmentMap()) {
6068 if (e.tri < 0 || e.tri >= firstGhost_) {
6071 const auto& tv = triangles_[e.tri].v;
6073 vertices_[tv[(e.side + 2) % 3]]) != seg) {
6085 template <
class QueryPo
int>
6086 [[nodiscard]] TriIndex locateIndex(
const QueryPoint& p, TriIndex start = NO_TRI)
const {
6087 if (triangles_.empty()) {
6090 TriIndex t = realTriangle(start) ? start
6091 : ((hint_ != NO_TRI && !isGhost(hint_)) ? hint_ : 0);
6092 TriIndex from = NO_TRI;
6093 const std::size_t cap = triangles_.size() * 3 + 16;
6096 const auto fq = filteredPoint(p);
6097 for (std::size_t step = 0; step < cap; ++step) {
6102 const auto& T = triangles_[t];
6103 const int begin =
static_cast<int>(rng_() % 3);
6104 TriIndex next = NO_TRI;
6105 for (
int k = 0; k < 3; ++k) {
6106 const int s = (begin + k) % 3;
6107 if (T.nbr[s] == from) {
6110 const VertexIndex ea = T.v[(s + 1) % 3];
6111 const VertexIndex eb = T.v[(s + 2) % 3];
6112 if (detail::orientationSignOf(filteredVertex(ea), filteredVertex(eb), fq)
6118 if (next == NO_TRI) {
6131 void buildAdjacency() {
6133 const auto key = [](VertexIndex u, VertexIndex w) {
6134 return u < w ? std::pair<VertexIndex, VertexIndex>{u, w} : std::pair<VertexIndex, VertexIndex>{w, u};
6143 std::pair<VertexIndex, VertexIndex>
edge;
6147 std::vector<Side> sides;
6148 sides.reserve(
static_cast<std::size_t
>(std::max(firstGhost_, TriIndex(0))) * 3);
6149 for (TriIndex t = 0; t < firstGhost_; ++t) {
6150 for (
int i = 0; i < 3; ++i) {
6151 sides.push_back(Side{key(triangles_[t].v[(i + 1) % 3],
6152 triangles_[t].v[(i + 2) % 3]),
6156 std::sort(sides.begin(), sides.end(),
6157 [](
const Side& a,
const Side& b) { return a.edge < b.edge; });
6160 std::vector<Side>
edges;
6161 for (std::size_t i = 0; i < sides.size();) {
6162 if (i + 1 < sides.size() && sides[i].edge == sides[i + 1].edge) {
6163 const Side& one = sides[i];
6164 const Side& other = sides[i + 1];
6165 triangles_[one.tri].nbr[one.side] = other.tri;
6166 triangles_[other.tri].nbr[other.side] = one.tri;
6169 edges.push_back(sides[i]);
6174 std::map<VertexIndex, std::pair<TriIndex, int>> ghostEdges;
6175 for (
const Side& unmatched :
edges) {
6176 const TriIndex t = unmatched.tri;
6177 const int i = unmatched.side;
6178 const VertexIndex a = triangles_[t].v[(i + 1) % 3];
6179 const VertexIndex b = triangles_[t].v[(i + 2) % 3];
6180 const TriIndex g =
static_cast<TriIndex
>(triangles_.size());
6181 triangles_.push_back(Tri{{a, b, GHOST}, {NO_TRI, NO_TRI, NO_TRI}, 0});
6182 triangles_[g].nbr[2] = t;
6183 triangles_[t].nbr[i] = g;
6184 for (
auto [realVertex, side] :
6185 {std::pair<VertexIndex, int>{b, 0}, std::pair<VertexIndex, int>{a, 1}}) {
6186 auto it = ghostEdges.find(realVertex);
6187 if (it == ghostEdges.end()) {
6188 ghostEdges.emplace(realVertex, std::pair<TriIndex, int>{g, side});
6190 auto [g2, s2] = it->second;
6191 triangles_[g].nbr[side] = g2;
6192 triangles_[g2].nbr[s2] = g;
6193 ghostEdges.erase(it);
6197 assert(ghostEdges.empty() &&
"Triangulation: open boundary (input is not a triangulation)");
6213 pointLocation_.reset();
6214 const std::size_t meshCells =
static_cast<std::size_t
>(firstGhost_);
6215 if (meshCells == 0 || triangles_.size() <= meshCells) {
6218 auto kp = std::make_shared<Kirkpatrick>();
6223 std::vector<std::array<VertexIndex, 3>> shape;
6226 kp->cells.resize(meshCells);
6227 shape.resize(meshCells);
6228 for (std::size_t t = 0; t < meshCells; ++t) {
6229 shape[t] = triangles_[t].v;
6230 kp->cells[t].runBegin =
static_cast<std::uint32_t
>(t);
6236 std::vector<VertexIndex> ring;
6237 std::vector<TriIndex> ringTri;
6239 const TriIndex first = firstGhost_;
6242 const Tri& ghost = triangles_[
static_cast<std::size_t
>(g)];
6243 ring.push_back(ghost.v[0]);
6244 ringTri.push_back(ghost.nbr[2]);
6246 }
while (g != first && ring.size() <= triangles_.size());
6247 if (g != first || ring.size() < 3) {
6251 const std::uint32_t hullSize =
static_cast<std::uint32_t
>(ring.size());
6254 NumberType xlo = vertices_[
static_cast<std::size_t
>(ring[0])].
x();
6256 NumberType ylo = vertices_[
static_cast<std::size_t
>(ring[0])].
y();
6258 for (
const VertexIndex v : ring) {
6259 const PointType& p = vertices_[
static_cast<std::size_t
>(v)];
6260 if (p.x() < xlo) xlo = p.x();
6261 if (xhi < p.x()) xhi = p.x();
6262 if (p.y() < ylo) ylo = p.y();
6263 if (yhi < p.y()) yhi = p.y();
6265 if constexpr (std::numeric_limits<NumberType>::is_specialized &&
6266 std::numeric_limits<NumberType>::is_integer &&
6267 std::numeric_limits<NumberType>::is_bounded) {
6270 if (xlo == std::numeric_limits<NumberType>::lowest() ||
6271 ylo == std::numeric_limits<NumberType>::lowest() ||
6272 xhi == std::numeric_limits<NumberType>::max() ||
6273 yhi == std::numeric_limits<NumberType>::max()) {
6282 if (!(bxlo < xlo) || !(bylo < ylo) || !(xhi < bxhi) || !(yhi < byhi)) {
6287 if constexpr (detail::filtersSign<VertexCoordinate>) {
6288 kp->extraApprox.reserve(kp->extra.size());
6289 for (
const PointType& corner : kp->extra) {
6290 kp->extraApprox.push_back(detail::approximatePoint(corner));
6293 const auto cornerVertex = [&](std::size_t j) {
6294 return static_cast<VertexIndex
>(vertices_.size() + j);
6298 const auto orient = [&](VertexIndex a, VertexIndex b, VertexIndex c) {
6299 return detail::orientationSignOf(kirkpatrickVertex(*kp, a), kirkpatrickVertex(*kp, b),
6300 kirkpatrickVertex(*kp, c))
6303 const auto holds = [&](VertexIndex a, VertexIndex b, VertexIndex c, VertexIndex p) {
6304 return !(orient(a, b, p) < 0) && !(orient(b, c, p) < 0) && !(orient(c, a, p) < 0);
6306 const auto addLeaf = [&](VertexIndex a, VertexIndex b, VertexIndex c, TriIndex seed) {
6308 cell.runBegin =
static_cast<std::uint32_t
>(seed);
6309 cell.runCount = Kirkpatrick::FULL_TURN;
6310 kp->cells.push_back(cell);
6311 shape.push_back({a, b, c});
6323 std::vector<std::array<std::uint32_t, 3>> ears;
6324 std::vector<std::uint32_t> prevAt;
6325 std::vector<std::uint32_t> nextAt;
6326 std::vector<std::int8_t> turnAt;
6327 const auto earClip = [&](
const std::vector<VertexIndex>& poly,
bool mountain) {
6329 const std::uint32_t m =
static_cast<std::uint32_t
>(poly.size());
6336 for (std::uint32_t i = 0; i < m; ++i) {
6337 prevAt[i] = (i + m - 1) % m;
6338 nextAt[i] = (i + 1) % m;
6340 const auto turn = [&](std::uint32_t i) -> std::int8_t {
6341 const auto side = orient(poly[prevAt[i]], poly[i], poly[nextAt[i]]);
6342 return side > 0 ? std::int8_t{1} : (side < 0 ? std::int8_t{-1} : std::int8_t{0});
6344 std::uint32_t blockingCount = 0;
6345 for (std::uint32_t i = 0; i < m; ++i) {
6346 turnAt[i] = turn(i);
6347 blockingCount += turnAt[i] <= 0 ? 1u : 0u;
6349 std::uint32_t remaining = m;
6350 std::uint32_t cursor = mountain ? 2 : 0;
6351 std::uint32_t skipped = 0;
6352 while (remaining > 3) {
6353 if (skipped > remaining) {
6356 const std::uint32_t i = cursor;
6357 cursor = nextAt[cursor];
6358 if (turnAt[i] <= 0 || (mountain && i < 2)) {
6362 const std::uint32_t p = prevAt[i];
6363 const std::uint32_t n = nextAt[i];
6364 if (!mountain && blockingCount != 0) {
6366 for (std::uint32_t k = nextAt[n]; k != p; k = nextAt[k]) {
6367 if (turnAt[k] <= 0 && holds(poly[p], poly[i], poly[n], poly[k])) {
6377 ears.push_back({p, i, n});
6381 for (
const std::uint32_t z : {p, n}) {
6382 const std::int8_t was = turnAt[z];
6383 turnAt[z] = turn(z);
6384 blockingCount += (turnAt[z] <= 0 ? 1u : 0u) - (was <= 0 ? 1u : 0u);
6389 const std::uint32_t a = cursor;
6390 const std::uint32_t b = nextAt[a];
6391 ears.push_back({a, b, nextAt[b]});
6401 std::array<std::uint32_t, 4> anchor{0, 0, 0, 0};
6403 using Wide = detail::promoted_number_t<NumberType>;
6404 const auto wx = [&](std::uint32_t i) {
6405 return detail::asNumber<Wide>(vertices_[
static_cast<std::size_t
>(ring[i])].
x());
6407 const auto wy = [&](std::uint32_t i) {
6408 return detail::asNumber<Wide>(vertices_[
static_cast<std::size_t
>(ring[i])].
y());
6410 Wide leastSum = wx(0) + wy(0);
6411 Wide mostSum = leastSum;
6412 for (std::uint32_t i = 1; i < hullSize; ++i) {
6413 const Wide sum = wx(i) + wy(i);
6414 if (sum < leastSum) {
6418 if (mostSum < sum) {
6423 if (wx(anchor[1]) + wy(i) < wx(i) + wy(anchor[1])) {
6426 if (wx(i) + wy(anchor[3]) < wx(anchor[3]) + wy(i)) {
6432 std::vector<VertexIndex> poly;
6433 std::vector<TriIndex> edgeSeed;
6434 for (std::size_t j = 0; j < 4; ++j) {
6437 poly.push_back(cornerVertex(j));
6438 edgeSeed.push_back(NO_TRI);
6439 poly.push_back(cornerVertex((j + 1) % 4));
6440 edgeSeed.push_back(NO_TRI);
6444 for (std::uint32_t k = anchor[(j + 1) % 4];; k = (k + hullSize - 1) % hullSize) {
6445 poly.push_back(ring[k]);
6446 const bool last = k == anchor[j];
6447 edgeSeed.push_back(last ? NO_TRI : ringTri[(k + hullSize - 1) % hullSize]);
6452 if (!earClip(poly,
true)) {
6455 for (
const auto& ear : ears) {
6459 TriIndex seed = edgeSeed[ear[0]];
6460 if (seed == NO_TRI) seed = edgeSeed[ear[1]];
6461 if (seed == NO_TRI) seed = edgeSeed[ear[2]];
6462 addLeaf(poly[ear[0]], poly[ear[1]], poly[ear[2]], seed);
6463 edgeSeed[ear[0]] = seed;
6469 std::vector<std::uint32_t> active(kp->cells.size());
6470 for (std::uint32_t i = 0; i < static_cast<std::uint32_t>(active.size()); ++i) {
6474 std::vector<std::int32_t> slotOf(vertices_.size() + kp->extra.size(), -1);
6475 std::vector<VertexIndex> used;
6476 std::vector<std::uint32_t> starBegin;
6477 std::vector<std::uint32_t> starFill;
6478 std::vector<std::uint32_t> starCells;
6479 std::vector<VertexIndex> starFrom;
6480 std::vector<VertexIndex> starTo;
6481 std::vector<std::uint8_t> blocked;
6482 std::vector<std::uint32_t> order;
6483 std::vector<std::uint8_t> replaced;
6484 std::vector<std::uint32_t> fan;
6485 std::vector<VertexIndex> link;
6486 std::vector<std::uint32_t> fresh;
6487 std::vector<std::uint32_t> kept;
6488 std::size_t degreeLimit = pointLocationMaxStar;
6490 while (active.size() > pointLocationTopSize) {
6493 for (
const std::uint32_t c : active) {
6494 for (
const VertexIndex v : shape[c]) {
6495 if (slotOf[
static_cast<std::size_t
>(v)] < 0) {
6496 slotOf[
static_cast<std::size_t
>(v)] =
static_cast<std::int32_t
>(used.size());
6501 const std::uint32_t vertexCount =
static_cast<std::uint32_t
>(used.size());
6502 starBegin.assign(vertexCount + 1, 0);
6503 for (
const std::uint32_t c : active) {
6504 for (
const VertexIndex v : shape[c]) {
6505 ++starBegin[
static_cast<std::size_t
>(slotOf[
static_cast<std::size_t
>(v)]) + 1];
6508 for (std::uint32_t s = 0; s < vertexCount; ++s) {
6509 starBegin[s + 1] += starBegin[s];
6511 starFill = starBegin;
6512 starCells.resize(starBegin[vertexCount]);
6513 for (
const std::uint32_t c : active) {
6514 for (
const VertexIndex v : shape[c]) {
6515 starCells[starFill[
static_cast<std::size_t
>(
6516 slotOf[
static_cast<std::size_t
>(v)])]++] = c;
6529 std::vector<std::uint32_t> tally(degreeLimit + 2, 0);
6530 for (std::uint32_t s = 0; s < vertexCount; ++s) {
6531 const std::size_t degree = starBegin[s + 1] - starBegin[s];
6532 if (degree >= 3 && degree <= degreeLimit) {
6533 ++tally[degree + 1];
6536 for (std::size_t d = 1; d < tally.size(); ++d) {
6537 tally[d] += tally[d - 1];
6539 order.resize(tally.back());
6540 for (std::uint32_t s = 0; s < vertexCount; ++s) {
6541 const std::size_t degree = starBegin[s + 1] - starBegin[s];
6542 if (degree >= 3 && degree <= degreeLimit) {
6543 order[tally[degree]++] = s;
6548 blocked.assign(vertexCount, 0);
6549 replaced.assign(kp->cells.size(), 0);
6551 for (
const std::uint32_t slot : order) {
6552 if (blocked[slot]) {
6555 const VertexIndex v = used[slot];
6556 const std::uint32_t begin = starBegin[slot];
6557 const std::uint32_t degree = starBegin[slot + 1] - begin;
6563 starFrom.resize(degree);
6564 starTo.resize(degree);
6566 for (std::uint32_t k = 0; k < degree && closes; ++k) {
6567 const auto& corners = shape[starCells[begin + k]];
6568 std::uint32_t position = 0;
6569 while (position < 3 && corners[position] != v) {
6572 closes = position < 3;
6574 starFrom[k] = corners[(position + 1) % 3];
6575 starTo[k] = corners[(position + 2) % 3];
6580 for (std::uint32_t step = 0, at = 0; closes && step < degree; ++step) {
6581 fan.push_back(starCells[begin + at]);
6582 link.push_back(starFrom[at]);
6583 const VertexIndex after = starTo[at];
6584 if (step + 1 == degree) {
6585 closes = after == link[0];
6588 std::uint32_t following = degree;
6589 for (std::uint32_t k = 0; k < degree; ++k) {
6590 if (starFrom[k] == after) {
6595 closes = following < degree;
6596 at = closes ? following : 0;
6598 if (!closes || link.size() != degree || !earClip(link,
false)) {
6603 for (
const auto& ear : ears) {
6608 std::uint32_t from = 0;
6609 std::uint32_t count = degree;
6611 bool decided =
true;
6612 for (
int g = 0; g < 3 && decided; ++g) {
6614 orient(v, link[ear[
static_cast<std::size_t
>(g)]],
6615 link[ear[
static_cast<std::size_t
>((g + 1) % 3)]]);
6618 }
else if (side < 0) {
6619 decided = outward < 0;
6623 if (decided && outward >= 0) {
6624 from = ear[
static_cast<std::size_t
>((outward + 1) % 3)];
6625 const std::uint32_t stop = ear[
static_cast<std::size_t
>(outward)];
6626 count = (stop + degree - from) % degree;
6632 cell.apex =
static_cast<std::uint32_t
>(v);
6633 cell.runBegin =
static_cast<std::uint32_t
>(kp->run.size());
6634 cell.runCount = count | (count == degree ? Kirkpatrick::FULL_TURN : 0u);
6635 for (std::uint32_t k = 0; k < count; ++k) {
6636 kp->run.push_back(
static_cast<std::uint32_t
>(link[(from + k) % degree]));
6637 kp->run.push_back(fan[(from + k) % degree]);
6639 kp->run.push_back(
static_cast<std::uint32_t
>(link[(from + count) % degree]));
6640 fresh.push_back(
static_cast<std::uint32_t
>(kp->cells.size()));
6641 kp->cells.push_back(cell);
6642 shape.push_back({link[ear[0]], link[ear[1]], link[ear[2]]});
6646 for (
const VertexIndex u : link) {
6647 blocked[
static_cast<std::size_t
>(slotOf[
static_cast<std::size_t
>(u)])] = 1;
6649 for (
const std::uint32_t cell : fan) {
6654 for (
const VertexIndex v : used) {
6655 slotOf[
static_cast<std::size_t
>(v)] = -1;
6658 if (fresh.empty()) {
6661 if (degreeLimit >= 64) {
6668 kept.reserve(active.size());
6669 for (
const std::uint32_t c : active) {
6674 kept.insert(kept.end(), fresh.begin(), fresh.end());
6676 degreeLimit = pointLocationMaxStar;
6689 kp->roots.reserve(active.size());
6690 kp->rootShape.reserve(active.size());
6691 for (
const std::uint32_t c : active) {
6692 kp->rootShape.push_back(shape[c]);
6695 shape.shrink_to_fit();
6697 const std::uint32_t cellCount =
static_cast<std::uint32_t
>(kp->cells.size());
6698 std::vector<std::uint32_t> relabel(cellCount, ~0u);
6699 std::vector<Cell> laid;
6700 laid.reserve(cellCount);
6701 std::vector<std::uint32_t> laidRun;
6702 laidRun.reserve(kp->run.size());
6703 std::vector<std::uint32_t> stack(active.rbegin(), active.rend());
6704 while (!stack.empty()) {
6705 const std::uint32_t c = stack.back();
6707 if (relabel[c] != ~0u) {
6710 relabel[c] =
static_cast<std::uint32_t
>(laid.size());
6711 laid.push_back(kp->cells[c]);
6712 const Cell& cell = kp->cells[c];
6713 for (std::uint32_t k = cell.runCount & ~Kirkpatrick::FULL_TURN; k-- > 0;) {
6714 stack.push_back(kp->run[cell.runBegin + 2 * k + 1]);
6717 for (Cell& cell : laid) {
6718 const std::uint32_t count = cell.runCount & ~Kirkpatrick::FULL_TURN;
6722 const std::uint32_t from = cell.runBegin;
6723 cell.runBegin =
static_cast<std::uint32_t
>(laidRun.size());
6724 for (std::uint32_t k = 0; k < count; ++k) {
6725 laidRun.push_back(kp->run[from + 2 * k]);
6726 laidRun.push_back(relabel[kp->run[from + 2 * k + 1]]);
6728 laidRun.push_back(kp->run[from + 2 * count]);
6730 for (
const std::uint32_t c : active) {
6731 kp->roots.push_back(relabel[c]);
6733 kp->cells.swap(laid);
6734 kp->run.swap(laidRun);
6736 pointLocation_ = std::move(kp);
6737 pointLocationRevision_ = revision_;