16#include "predicates.h"
40inline std::uint64_t
mortonKey(std::uint32_t
x, std::uint32_t
y)
42 auto spread = [](std::uint64_t v) {
44 v = (v | (v << 16)) & 0x0000ffff0000ffffull;
45 v = (v | (v << 8)) & 0x00ff00ff00ff00ffull;
46 v = (v | (v << 4)) & 0x0f0f0f0f0f0f0f0full;
47 v = (v | (v << 2)) & 0x3333333333333333ull;
48 v = (v | (v << 1)) & 0x5555555555555555ull;
51 return spread(
x) | (spread(
y) << 1);
67 return (std::uint64_t(std::uint32_t(
a)) << 32) | std::uint64_t(std::uint32_t(
b));
77 return int(std::uint32_t(
k >> 32));
86 return int(std::uint32_t(
k & 0xffffffffull));
99template <
class Po
int>
double orientPts(
const Point &
a,
const Point &
b,
const Point &
c)
101 const double pa[2] = {
a.x(),
a.y()}, pb[2] = {
b.x(),
b.y()}, pc[2] = {
c.x(),
c.y()};
102 return orient2d(pa, pb, pc);
112template <
class Po
int>
inline double dist2(
const Point &
a,
const Point &
b)
114 const double dx =
a.x() -
b.x(),
dy =
a.y() -
b.y();
122 auto close = [](
double x,
double y) {
123 return (
x == 0 ||
y == 0) ? std::abs(
x -
y) <= 1e-12
124 : std::abs(
x -
y) * 1e12 <= std::min(std::abs(
x), std::abs(
y));
126 return close(
a.x(),
b.x()) && close(
a.y(),
b.y());
132using namespace cdt_detail;
136template <
class Storage>
146 const double pa[2] = {m_pts[
a].x(), m_pts[
a].y()};
147 const double pb[2] = {m_pts[
b].x(), m_pts[
b].y()};
148 const double pc[2] = {m_pts[
c].x(), m_pts[
c].y()};
149 return orient2d(pa, pb, pc);
152template <
class Storage>
153double BasicConstrainedDelaunay<Storage>::orient(
int a,
int b,
const Point &
p)
const
155 const double pa[2] = {m_pts[
a].x(), m_pts[
a].y()};
156 const double pb[2] = {m_pts[
b].x(), m_pts[
b].y()};
157 const double pc[2] = {
p.x(),
p.y()};
158 return orient2d(pa, pb, pc);
161template <
class Storage>
162bool BasicConstrainedDelaunay<Storage>::inCircle(
int a,
int b,
int c,
int d)
const
164 const double pa[2] = {m_pts[
a].x(), m_pts[
a].y()};
165 const double pb[2] = {m_pts[
b].x(), m_pts[
b].y()};
166 const double pc[2] = {m_pts[
c].x(), m_pts[
c].y()};
167 const double pd[2] = {m_pts[d].x(), m_pts[d].y()};
168 return incircle(pa, pb, pc, pd) > 0.0;
173template <
class Storage>
int BasicConstrainedDelaunay<Storage>::edgeIndex(
int t,
int v)
const
176 for (
int i = 0;
i < 3; ++
i)
182template <
class Storage>
183void BasicConstrainedDelaunay<Storage>::trianglesAround(
int v, Vector<int> *out)
const
185 const int start = m_vertexTri[v];
188 for (
int dir = 0; dir < 2; ++dir) {
190 for (
int guard = 0; guard < 100000; ++guard) {
191 const int i = edgeIndex(
t, v);
194 if (!(dir == 1 &&
t ==
start))
196 const int next = m_tris[
t].adj[dir == 0 ? (
i + 1) % 3 : (
i + 2) % 3];
206template <
class Storage>
void BasicConstrainedDelaunay<Storage>::setAdj(
int t,
int e,
int n)
209 m_tris[
t].adj[e] =
n;
214template <
class Storage>
215int BasicConstrainedDelaunay<Storage>::triangleWithEdge(
int a,
int b,
int *edgeIndexOut)
const
217 const int start = m_vertexTri[
a];
221 for (
int guard = 0; guard < 100000; ++guard) {
223 const int i = edgeIndex(
t,
a);
226 if (T.v[(
i + 1) % 3] ==
b) {
228 *edgeIndexOut = (
i + 2) % 3;
233 const int next = T.adj[(
i + 1) % 3];
234 if (next < 0 || next ==
start)
240 for (
int guard = 0; guard < 100000; ++guard) {
242 const int i = edgeIndex(
t,
a);
245 if (T.v[(
i + 1) % 3] ==
b) {
247 *edgeIndexOut = (
i + 2) % 3;
250 const int next = T.adj[(
i + 2) % 3];
251 if (next < 0 || next ==
start)
261 int t = triangleWithEdge(
a,
b, &e);
263 t = triangleWithEdge(
b,
a, &e);
264 return t >= 0 && m_tris[
t].constrained[e];
267template <
class Storage>
275 int cur =
a, prev = -1;
276 for (
int guard = 0; guard < 1000000 && cur !=
b; ++guard) {
278 trianglesAround(cur, &fan);
279 bool reachesB =
false;
280 int byOrigin = -1, byLine = -1;
281 double bestD2 = std::numeric_limits<double>::infinity();
282 const Point &P = m_pts[cur], &B = m_pts[
b];
285 const int i = edgeIndex(
t, cur);
286 for (
int k = 1;
k <= 2; ++
k) {
287 const int v = T.
v[(
i +
k) % 3];
289 (
i + (
k == 1 ? 2 : 1)) % 3;
296 const std::uint64_t ek =
edgeKey64(cur, v);
297 const auto it = m_segOrigin.constFind(ek);
298 if (it != m_segOrigin.constEnd() &&
299 (it.value() == origin || m_segOriginExtra.contains(ek, origin))) {
303 if (orient(
a,
b, v) != 0.0)
305 const Point &V = m_pts[v];
307 (V.x() - P.x()) * (B.x() - P.x()) + (V.y() - P.y()) * (B.y() - P.y());
310 const double d2 =
dist2(V, P);
317 const int next = reachesB ?
b : (byOrigin >= 0 ? byOrigin : byLine);
329 for (
int dir = 0; dir < 2; ++dir) {
331 const int t = dir == 0 ? triangleWithEdge(
a,
b, &e) : triangleWithEdge(
b,
a, &e);
334 m_tris[
t].constrained[e] =
true;
335 const int n = m_tris[
t].adj[e];
337 for (
int i = 0;
i < 3; ++
i)
338 if (m_tris[
n].
adj[
i] ==
t)
339 m_tris[
n].constrained[
i] =
true;
343template <
class Storage>
344int BasicConstrainedDelaunay<Storage>::findTriangle(
const Point &
p,
int start)
const
346 if (m_tris.isEmpty())
349 const int cap = m_tris.size() * 2 + 16;
353 for (
int i = 0;
i < 3; ++
i) {
354 if (orient(T.v[(
i + 1) % 3], T.v[(
i + 2) % 3],
p) < 0.0) {
366 for (
int i = 0;
i < m_tris.size(); ++
i) {
368 if (orient(T.v[1], T.v[2],
p) >= 0.0 && orient(T.v[2], T.v[0],
p) >= 0.0 &&
369 orient(T.v[0], T.v[1],
p) >= 0.0)
377 const int t = findTriangle(
p, m_lastLocate);
378 return (
t >= 0 && m_tris[
t].alive) ?
t : -1;
383 return m_liveTriangles;
392 Triangle &T = m_tris[
t];
393 const int n = T.adj[e];
394 Triangle &N = m_tris[
n];
395 const int a = T.v[e],
b = T.v[(e + 1) % 3],
c = T.v[(e + 2) % 3];
397 for (
int i = 0;
i < 3; ++
i)
402 const int d = N.v[
k];
403 const int Tca = T.adj[(e + 1) % 3], Tab = T.adj[(e + 2) % 3];
404 const int Nbd = N.adj[(
k + 1) % 3], Ndc = N.adj[(
k + 2) % 3];
405 const bool cCA = T.constrained[(e + 1) % 3], cAB = T.constrained[(e + 2) % 3];
406 const bool cBD = N.constrained[(
k + 1) % 3], cDC = N.constrained[(
k + 2) % 3];
414 T.constrained[0] = cBD;
415 T.constrained[1] =
false;
416 T.constrained[2] = cAB;
423 N.constrained[0] = cDC;
424 N.constrained[1] = cCA;
425 N.constrained[2] =
false;
427 for (
int i = 0;
i < 3; ++
i)
428 if (m_tris[Nbd].
adj[
i] ==
n)
429 m_tris[Nbd].adj[
i] =
t;
431 for (
int i = 0;
i < 3; ++
i)
432 if (m_tris[Tca].
adj[
i] ==
t)
433 m_tris[Tca].adj[
i] =
n;
438 if (m_triangleChanged) {
439 m_triangleChanged(
t);
440 m_triangleChanged(
n);
444template <
class Storage>
void BasicConstrainedDelaunay<Storage>::legalize(
int t0,
int e0)
446 Vector<std::pair<int, int>> stack;
447 stack.append(std::make_pair(t0, e0));
448 while (!stack.isEmpty()) {
449 const std::pair<int, int> te = stack.takeLast();
450 const int t = te.first, e = te.second;
452 const int n = T.adj[e];
453 if (
n < 0 || T.constrained[e])
457 for (
int i = 0;
i < 3; ++
i)
462 const int d = N.v[
k];
463 if (!inCircle(T.v[0], T.v[1], T.v[2], d))
467 stack.append(std::make_pair(
t, 0));
468 stack.append(std::make_pair(
t, 2));
469 stack.append(std::make_pair(
n, 0));
470 stack.append(std::make_pair(
n, 1));
474template <
class Storage>
void BasicConstrainedDelaunay<Storage>::splitTriangle(
int t,
int v)
477 const int a = old.v[0],
b = old.v[1],
c = old.v[2];
478 const int t1 =
t, t2 = m_tris.size(), t3 = m_tris.size() + 1;
489 T1.adj[0] = old.adj[2];
492 T2.adj[0] = old.adj[0];
495 T3.adj[0] = old.adj[1];
498 T1.constrained[0] = old.constrained[2];
499 T1.constrained[1] = T1.constrained[2] =
false;
500 T2.constrained[0] = old.constrained[0];
501 T2.constrained[1] = T2.constrained[2] =
false;
502 T3.constrained[0] = old.constrained[1];
503 T3.constrained[1] = T3.constrained[2] =
false;
504 T1.alive = T2.alive = T3.alive = old.alive;
508 m_liveTriangles += old.alive ? 2 : 0;
510 for (
int i = 0;
i < 3; ++
i)
511 if (m_tris[old.adj[2]].adj[
i] ==
t)
512 m_tris[old.adj[2]].adj[
i] = t1;
514 for (
int i = 0;
i < 3; ++
i)
515 if (m_tris[old.adj[0]].adj[
i] ==
t)
516 m_tris[old.adj[0]].adj[
i] = t2;
518 for (
int i = 0;
i < 3; ++
i)
519 if (m_tris[old.adj[1]].adj[
i] ==
t)
520 m_tris[old.adj[1]].adj[
i] = t3;
528 if (m_triangleChanged) {
529 m_triangleChanged(t1);
530 m_triangleChanged(t2);
531 m_triangleChanged(t3);
535template <
class Storage>
void BasicConstrainedDelaunay<Storage>::splitEdge(
int t,
int e,
int v)
538 const int a = old.v[e],
b = old.v[(e + 1) % 3],
c = old.v[(e + 2) % 3];
539 const int n = old.adj[e];
540 const bool f = old.constrained[e];
541 const int t1 =
t, t2 = m_tris.size();
549 T1.alive = T2.alive = old.alive;
550 int n1 = -1, n2 = -1;
553 n2 = m_tris.size() + 1;
556 const int Tab = old.adj[(e + 2) % 3], Tca = old.adj[(e + 1) % 3];
563 T1.constrained[0] = old.constrained[(e + 2) % 3];
564 T1.constrained[1] = f;
565 T1.constrained[2] =
false;
566 T2.constrained[0] = old.constrained[(e + 1) % 3];
567 T2.constrained[1] =
false;
568 T2.constrained[2] = f;
571 m_liveTriangles += old.alive ? 1 : 0;
573 for (
int i = 0;
i < 3; ++
i)
574 if (m_tris[Tab].
adj[
i] ==
t)
575 m_tris[Tab].adj[
i] = t1;
577 for (
int i = 0;
i < 3; ++
i)
578 if (m_tris[Tca].
adj[
i] ==
t)
579 m_tris[Tca].adj[
i] = t2;
587 for (
int i = 0;
i < 3; ++
i)
588 if (oldN.adj[
i] ==
t) {
592 const int d = oldN.v[
k];
600 N1.alive = N2.alive = oldN.alive;
601 N1.adj[0] = oldN.adj[(
k + 1) % 3];
604 N2.adj[0] = oldN.adj[(
k + 2) % 3];
607 N1.constrained[0] = oldN.constrained[(
k + 1) % 3];
608 N1.constrained[1] =
false;
609 N1.constrained[2] = f;
610 N2.constrained[0] = oldN.constrained[(
k + 2) % 3];
611 N2.constrained[1] = f;
612 N2.constrained[2] =
false;
615 m_liveTriangles += oldN.alive ? 1 : 0;
617 for (
int i = 0;
i < 3; ++
i)
618 if (m_tris[N1.adj[0]].adj[
i] ==
n)
619 m_tris[N1.adj[0]].adj[
i] = n1;
621 for (
int i = 0;
i < 3; ++
i)
622 if (m_tris[N2.adj[0]].adj[
i] ==
n)
623 m_tris[N2.adj[0]].adj[
i] = n2;
632 if (m_triangleChanged) {
633 m_triangleChanged(t1);
634 m_triangleChanged(t2);
636 m_triangleChanged(n1);
637 m_triangleChanged(n2);
644template <
class Storage>
647 const std::function<
void(std::uint64_t)> &
progress)
656 m_terrainElevations.clear();
658 m_segOriginExtra.clear();
665 m_errorMsg = Storage::message(
"cdt: cancelled");
675 seen.reserve(points.size());
677 vertexOfPoint->resize(points.size());
678 double minX = std::numeric_limits<double>::infinity(), minY = minX, maxX = -minX, maxY = -minX;
679 for (
int i = 0;
i < points.size(); ++
i) {
683 if (!std::isfinite(
p.x()) || !std::isfinite(
p.y())) {
684 m_errorMsg = Storage::invalidPointMessage(
i);
688 double x =
p.x(),
y =
p.y();
693 std::memcpy(&kx, &
x, 8);
694 std::memcpy(&ky, &
y, 8);
695 const std::pair<std::int64_t, std::int64_t> key(kx, ky);
696 auto it = seen.constFind(key);
698 if (it == seen.constEnd()) {
701 seen.insert(key,
id);
702 minX = std::min(minX,
p.x());
703 maxX = std::max(maxX,
p.x());
704 minY = std::min(minY,
p.y());
705 maxY = std::max(maxY,
p.y());
709 (*vertexOfPoint)[
i] =
id;
711 if (m_pts.size() < 3) {
712 m_errorMsg = Storage::message(
"cdt: fewer than 3 distinct points");
718 const double ext = std::max({bbox.width(), bbox.height(), 1e-9});
719 const double cx = bbox.center().x(),
cy = bbox.center().y();
720 const double R = ext * 1e5;
721 m_superBase = m_pts.size();
722 m_pts.append(
Point(
cx - 2.0 * R,
cy - R));
723 m_pts.append(
Point(
cx + 2.0 * R,
cy - R));
725 m_vertexTri.fill(-1, m_pts.size());
727 sup.
v[0] = m_superBase;
728 sup.
v[1] = m_superBase + 1;
729 sup.
v[2] = m_superBase + 2;
730 sup.
adj[0] = sup.
adj[1] = sup.
adj[2] = -1;
735 m_vertexTri[m_superBase] = m_vertexTri[m_superBase + 1] = m_vertexTri[m_superBase + 2] = 0;
739 const double sx = bbox.width() > 0 ? 1048575.0 / bbox.width() : 0.0;
740 const double sy = bbox.height() > 0 ? 1048575.0 / bbox.height() : 0.0;
741 for (
int i = 0;
i < m_superBase; ++
i) {
742 const std::uint32_t qx = std::uint32_t((m_pts[
i].
x() - bbox.left()) * sx);
743 const std::uint32_t qy = std::uint32_t((m_pts[
i].
y() - bbox.top()) * sy);
751 std::uint64_t comparisons = 0;
753 std::sort(
order.begin(),
order.end(), [&](
const auto &
a,
const auto &
b) {
754 if ((++comparisons & 16383) == 0 && stopped())
758 }
catch (
const CancelBuild &) {
763 m_tris.reserve(2 * m_superBase + 8);
764 std::uint64_t completed = 0;
765 for (
const std::pair<std::uint64_t, int> &o :
order) {
766 if ((completed++ & 4095) == 0) {
772 const int v = o.second;
773 const int t = findTriangle(m_pts[v], m_lastLocate);
775 m_errorMsg = Storage::message(
"cdt: point location failed");
780 for (
int i = 0;
i < 3; ++
i)
781 if (orient(T.v[(
i + 1) % 3], T.v[(
i + 2) % 3], v) == 0.0) {
786 splitEdge(
t, onEdge, v);
789 m_lastLocate = m_vertexTri[v];
796 const int t = findTriangle(
p, m_lastLocate);
797 if (
t < 0 || !m_tris[
t].alive)
799 const int v = m_pts.size();
801 if (!m_origins.isEmpty())
803 m_vertexTri.append(-1);
806 for (
int i = 0;
i < 3; ++
i)
807 if (orient(T.
v[(
i + 1) % 3], T.
v[(
i + 2) % 3], v) == 0.0) {
813 for (
int i = 0;
i < 3; ++
i)
816 m_vertexTri.removeLast();
819 splitEdge(
t, onEdge, v);
822 m_lastLocate = m_vertexTri[v];
831template <
class Storage>
836 if (
a < 0 || b < 0 || a >= m_pts.size() ||
b >= m_pts.size() || isSuperVertex(
a) ||
842 auto record = [&](
int x,
int y) {
844 const auto it = m_segOrigin.constFind(
k);
845 if (it == m_segOrigin.constEnd())
846 m_segOrigin.insert(
k, origin);
847 else if (it.value() != origin && !m_segOriginExtra.contains(
k, origin))
848 m_segOriginExtra.insert(
k, origin);
849 if (!m_segPiece.contains(
k))
850 m_segPiece.insert(
k,
k);
852 if (triangleWithEdge(
a,
b, &e) >= 0 || triangleWithEdge(
b,
a, &e) >= 0) {
853 markConstrained(
a,
b);
860 auto onSegment = [&](
int p) {
861 if (
p ==
a ||
p ==
b || orient(
a,
b,
p) != 0.0)
863 const Point &A = m_pts[
a], &B = m_pts[
b], &P = m_pts[
p];
864 return (P.x() - A.x()) * (B.x() - A.x()) + (P.y() - A.y()) * (B.y() - A.y()) > 0.0 &&
865 (P.x() - B.x()) * (A.x() - B.x()) + (P.y() - B.y()) * (A.y() - B.y()) > 0.0;
867 int start = -1, startEdge = -1;
868 for (
int dir = 0; dir < 2 &&
start < 0; ++dir) {
869 const int t0 = m_vertexTri[
a];
871 for (
int guard = 0; guard < 1000000 && t >= 0; ++guard) {
872 const int i = edgeIndex(
t,
a);
875 const int p = m_tris[
t].v[(
i + 1) % 3], q = m_tris[
t].v[(
i + 2) % 3];
877 return insertConstraintImpl(
a,
p, origin) && insertConstraintImpl(
p,
b, origin);
879 return insertConstraintImpl(
a, q, origin) && insertConstraintImpl(q,
b, origin);
880 if (orient(
a,
b,
p) < 0.0 && orient(
a,
b, q) > 0.0) {
885 const int next = m_tris[
t].adj[dir == 0 ? (
i + 1) % 3 : (
i + 2) % 3];
886 if (next < 0 || next == t0)
892 m_errorMsg = Storage::message(
"cdt: constraint start not found");
897 Vector<std::pair<int, int>> crossed;
899 int t =
start, e = startEdge;
900 for (
int guard = 0; guard < 10000000; ++guard) {
901 const int p = m_tris[
t].v[(e + 1) % 3], q = m_tris[
t].v[(e + 2) % 3];
902 if (m_tris[
t].constrained[e]) {
903 m_errorMsg = Storage::message(
"cdt: constraint crosses another constraint");
906 crossed.append(std::make_pair(
p, q));
907 const int n = m_tris[
t].adj[e];
909 m_errorMsg = Storage::message(
"cdt: walked off the triangulation");
913 for (
int i = 0;
i < 3; ++
i)
914 if (m_tris[
n].
adj[
i] ==
t) {
918 const int r = m_tris[
n].v[
k];
921 const double orr = orient(
a,
b, r);
923 return insertConstraintImpl(
a, r, origin) && insertConstraintImpl(r,
b, origin);
929 e = (orr > 0.0) ? (
k + 1) % 3 : (
k + 2) % 3;
935 Vector<std::pair<int, int>> newEdges;
937 while (!crossed.isEmpty()) {
938 const std::pair<int, int> pq = crossed.takeFirst();
940 int t = triangleWithEdge(pq.first, pq.second, &te);
942 t = triangleWithEdge(pq.second, pq.first, &te);
944 m_errorMsg = Storage::message(
"cdt: lost a crossed edge");
947 const int n = m_tris[
t].adj[te];
948 const int apex = m_tris[
t].v[te];
950 for (
int i = 0;
i < 3; ++
i)
951 if (m_tris[
n].
adj[
i] ==
t) {
955 const int d = m_tris[
n].v[
k];
957 const int p = m_tris[
t].v[(te + 1) % 3], q = m_tris[
t].v[(te + 2) % 3];
958 const double s1 = orient(apex, d,
p), s2 = orient(apex, d, q);
959 if (!((s1 > 0.0 && s2 < 0.0) || (s1 < 0.0 && s2 > 0.0))) {
961 if (++spins > 100 * (crossed.size() + 1)) {
962 m_errorMsg = Storage::message(
"cdt: constraint recovery stalled");
970 const double o1 = orient(
a,
b, apex), o2 = orient(
a,
b, d);
971 const bool isTarget = (apex ==
a && d ==
b) || (apex ==
b && d ==
a);
972 if (!isTarget && ((o1 > 0.0 && o2 < 0.0) || (o1 < 0.0 && o2 > 0.0)))
973 crossed.append(std::make_pair(apex, d));
975 newEdges.append(std::make_pair(apex, d));
977 markConstrained(
a,
b);
980 for (
const std::pair<int, int> &ed : newEdges) {
982 int t = triangleWithEdge(ed.first, ed.second, &e2);
984 t = triangleWithEdge(ed.second, ed.first, &e2);
996 for (
int i = 0;
i < m_tris.size(); ++
i) {
1000 if (isSuperVertex(T.
v[0]) || isSuperVertex(T.
v[1]) || isSuperVertex(T.
v[2])) {
1006 while (!stack.isEmpty()) {
1007 const int t = stack.takeLast();
1008 for (
int i = 0;
i < 3; ++
i) {
1009 const int n = m_tris[
t].adj[
i];
1010 if (
n < 0 || !m_tris[
n].alive || m_tris[
t].constrained[
i])
1012 m_tris[
n].alive =
false;
1022 if (T.alive && (isSuperVertex(T.v[0]) || isSuperVertex(T.v[1]) || isSuperVertex(T.v[2]))) {
1030 const int t0 = locate(
p);
1034 m_tris[t0].alive =
false;
1037 while (!stack.isEmpty()) {
1038 const int t = stack.takeLast();
1039 for (
int i = 0;
i < 3; ++
i) {
1040 const int n = m_tris[
t].adj[
i];
1041 if (
n < 0 || !m_tris[
n].alive || m_tris[
t].constrained[
i])
1043 m_tris[
n].alive =
false;
1055 if (!isConstrained(
a,
b))
1058 const auto chain = constrainedChain(
a,
b);
1059 for (
int k = 0;
k + 1 <
chain.size(); ++
k) {
1061 trianglesAround(
chain[
k], &around);
1062 for (
int t : around) {
1063 const auto &tri = m_tris[
t];
1068 for (
int v : tri.v) {
1074 if (hasB && apex >= 0 && orient(
a,
b, apex) > 0)
1079 while (!stack.isEmpty()) {
1080 const int t = stack.takeLast();
1081 if (!m_tris[
t].alive)
1083 m_tris[
t].alive =
false;
1086 for (
int e = 0; e < 3; ++e) {
1087 const int n = m_tris[
t].adj[e];
1088 if (
n >= 0 && m_tris[
n].alive && !m_tris[
t].constrained[e])
1102 for (
int i = 0;
i + 1 <
chain.size(); ++
i)
1108 return !m_fixedSub.isEmpty() && m_fixedSub.contains(
edgeKey64(
a,
b));
1113template <
class Storage>
1114bool BasicConstrainedDelaunay<Storage>::subsegmentEncroached(
int a,
int b)
const
1116 const Point &A = m_pts[
a], &B = m_pts[
b];
1117 for (
int dir = 0; dir < 2; ++dir) {
1119 const int t = dir == 0 ? triangleWithEdge(
a,
b, &e) : triangleWithEdge(
b,
a, &e);
1120 if (
t < 0 || !m_tris[
t].alive)
1122 const int c = m_tris[
t].v[e];
1123 if (isSuperVertex(
c))
1125 const Point &C = m_pts[
c];
1126 if ((A.x() - C.x()) * (B.x() - C.x()) + (A.y() - C.y()) * (B.y() - C.y()) < 0.0)
1137template <
class Storage>
int BasicConstrainedDelaunay<Storage>::splitSubsegment(
int a,
int b)
1140 int t = triangleWithEdge(
a,
b, &e);
1142 t = triangleWithEdge(
b,
a, &e);
1146 const std::uint64_t origin = m_segOrigin.value(key, key);
1147 const std::uint64_t piece = m_segPiece.value(key, key);
1148 const Vector<std::uint64_t> extra = m_segOriginExtra.values(key);
1150 auto acuteAt = [&](
int x,
int other) {
1151 if (
x >= m_superBase)
1154 = m_pts[
x], &O = m_pts[other];
1156 trianglesAround(
x, &fan);
1157 for (
int ft : fan) {
1159 const int i = edgeIndex(ft,
x);
1162 for (
int k = 1;
k <= 2; ++
k) {
1163 const int w = T.v[(
i +
k) % 3];
1164 const int ei = (
i + (
k == 1 ? 2 : 1)) % 3;
1165 if (!T.constrained[ei] ||
w == other)
1167 const Point &W = m_pts[
w];
1168 if ((W.x() - X.x()) * (O.x() - X.x()) + (W.y() - X.y()) * (O.y() - X.y()) > 0.0)
1174 const Point A = m_pts[
a], B = m_pts[
b];
1175 const double len = std::sqrt(
dist2(A, B));
1176 if (!(
len > 0.0) || !std::isfinite(
len))
1178 const bool acA = acuteAt(
a,
b), acB = acuteAt(
b,
a);
1182 while (
len > 3.0 * pw)
1184 while (
len < 1.5 * pw)
1188 split = 1.0 - split;
1190 const Point p = A + (B - A) * split;
1191 if (
p == A ||
p == B)
1193 const int v = m_pts.size();
1195 if (!m_origins.isEmpty())
1196 m_origins.append(0);
1197 m_vertexTri.append(-1);
1198 if (!m_origins.isEmpty())
1199 m_origins[v] = ((m_origins[
a] | m_origins[
b]) & 127u) | SegmentSplit;
1201 m_segOrigin.remove(key);
1202 m_segPiece.remove(key);
1203 m_segOriginExtra.remove(key);
1205 m_segOrigin.insert(half, origin);
1206 m_segPiece.insert(half, piece);
1207 for (std::uint64_t o : extra)
1208 m_segOriginExtra.insert(half, o);
1210 m_vertexSeg.insert(v, piece);
1220template <
class Storage>
1221bool BasicConstrainedDelaunay<Storage>::exemptShortestEdge(
int u,
int v)
const
1223 const auto iu = m_vertexSeg.constFind(u), iv = m_vertexSeg.constFind(v);
1224 if (iu == m_vertexSeg.constEnd() || iv == m_vertexSeg.constEnd() || iu.value() == iv.value())
1226 const int a1 =
keyLo(iu.value()), b1 =
keyHi(iu.value());
1227 const int a2 =
keyLo(iv.value()), b2 =
keyHi(iv.value());
1229 if (a1 == a2 || a1 == b2)
1231 else if (b1 == a2 || b1 == b2)
1235 const double d1 =
dist2(m_pts[u], m_pts[j]), d2 =
dist2(m_pts[v], m_pts[j]);
1236 return d1 < 1.001 * d2 && d1 > 0.999 * d2;
1242 double best = std::numeric_limits<double>::infinity();
1244 for (
int k = 0;
k < 3; ++
k) {
1245 const double l =
dist2(m_pts[T.
v[(
k + 1) % 3]], m_pts[T.
v[(
k + 2) % 3]]);
1251 return exemptShortestEdge(T.
v[(
s + 1) % 3], T.
v[(
s + 2) % 3]);
1256 if (m_fixedSub.isEmpty())
1258 for (
int k = 0;
k < 3; ++
k) {
1259 const int x = m_tris[
t].v[
k];
1261 trianglesAround(
x, &fan);
1262 for (
int ft : fan) {
1264 const int i = edgeIndex(ft,
x);
1267 for (
int kk = 1; kk <= 2; ++kk) {
1268 const int ei = (
i + (kk == 1 ? 2 : 1)) % 3;
1279template <
class Storage>
1282 if (v < m_superBase + 3 || !isVertexActive(v) || m_vertexSeg.contains(v))
1285 trianglesAround(v, &r.star);
1286 if (r.star.size() < 3 || r.star.size() > 128)
1288 for (
int t : r.star) {
1289 const auto &T = m_tris[
t];
1290 const int i = edgeIndex(
t, v);
1291 if (!T.alive ||
i < 0 || T.constrained[(
i + 1) % 3] || T.constrained[(
i + 2) % 3] ||
1292 T.adj[(
i + 1) % 3] < 0 || T.adj[(
i + 2) % 3] < 0)
1294 for (
int k = 0;
k < 3; ++
k)
1295 if (isSuperVertex(T.v[
k]))
1297 r.ring.append(T.v[(
i + 1) % 3]);
1299 const int n = r.ring.size();
1300 for (
int j = 0; j <
n; ++j) {
1301 const auto &T = m_tris[r.star[j]];
1302 if (T.v[(edgeIndex(r.star[j], v) + 2) % 3] != r.ring[(j + 1) %
n])
1306 return Triangle{{
a,
b,
c}, {-1, -1, -1}, {
false,
false,
false},
true};
1308 Vector<int> polygon = r.ring;
1309 while (polygon.size() > 3) {
1310 bool clipped =
false;
1311 for (
int j = 0; j < polygon.size(); ++j) {
1312 const int a = polygon[(j + polygon.size() - 1) % polygon.size()],
b = polygon[j],
1313 c = polygon[(j + 1) % polygon.size()];
1314 if (!(orient(
a,
b,
c) > 0))
1316 bool occupied =
false;
1317 for (
int q : polygon)
1318 if (q !=
a && q !=
b && q !=
c && orient(
a,
b, q) >= 0 && orient(
b,
c, q) >= 0 &&
1319 orient(
c,
a, q) >= 0) {
1326 polygon.removeAt(j);
1333 if (!(orient(polygon[0], polygon[1], polygon[2]) > 0))
1335 r.replacement.append(
triangle(polygon[0], polygon[1], polygon[2]));
1338 bool settled =
false;
1339 for (
int guard = 0; guard < 4 *
n *
n; ++guard) {
1340 bool flipped =
false;
1341 for (
int i = 0;
i < r.replacement.size() && !flipped; ++
i)
1342 for (
int j =
i + 1; j < r.replacement.size() && !flipped; ++j)
1343 for (
int e = 0; e < 3 && !flipped; ++e)
1344 for (
int f = 0; f < 3 && !flipped; ++f) {
1345 auto &T = r.replacement[
i];
1346 auto &N = r.replacement[j];
1347 const int a = T.v[e],
b = T.v[(e + 1) % 3],
c = T.v[(e + 2) % 3],
1349 if (
b != N.v[(f + 2) % 3] ||
c != N.v[(f + 1) % 3])
1351 if (orient(
a,
b, d) <= 0 || orient(
a, d,
c) <= 0 ||
1352 !inCircle(T.v[0], T.v[1], T.v[2], d))
1365 for (
int i = 0;
i < r.replacement.size(); ++
i)
1366 for (
int e = 0; e < 3; ++e) {
1367 auto &T = r.replacement[
i];
1368 const int a = T.v[(e + 1) % 3],
b = T.v[(e + 2) % 3];
1370 for (
int j = 0; j <
n; ++j)
1371 if (
a == r.ring[j] &&
b == r.ring[(j + 1) %
n]) {
1372 const auto &old = m_tris[r.star[j]];
1373 const int k = edgeIndex(r.star[j], v), neighbour = old.adj[
k];
1374 T.adj[e] = neighbour;
1375 T.constrained[e] = old.constrained[
k];
1379 if (neighbour >= 0 && !T.constrained[e]) {
1380 const auto &N = m_tris[neighbour];
1383 for (
int f = 0; f < 3; ++f)
1384 if (N.adj[f] == r.star[j] && inCircle(T.v[0], T.v[1], T.v[2], N.v[f]))
1391 for (
int j = 0; j < r.replacement.size() && !found; ++j)
1393 for (
int f = 0; f < 3; ++f)
1394 if (r.replacement[j].v[(f + 1) % 3] ==
b &&
1395 r.replacement[j].v[(f + 2) % 3] ==
a) {
1396 T.adj[e] = r.star[j];
1403 *out = std::move(r);
1407template <
class Storage>
1408void BasicConstrainedDelaunay<Storage>::commitRemoval(
int v,
const Removal &r)
1411 for (
int j = 0; j < r.ring.size(); ++j) {
1412 const int old = r.star[j],
k = edgeIndex(old, v), neighbour = m_tris[old].adj[
k];
1415 int replacement = -1;
1416 for (
int i = 0;
i < r.replacement.size(); ++
i)
1417 for (
int e = 0; e < 3; ++e)
1418 if (r.replacement[
i].v[(e + 1) % 3] == r.ring[j] &&
1419 r.replacement[
i].v[(e + 2) % 3] == r.ring[(j + 1) % r.ring.size()])
1420 replacement = r.star[
i];
1421 for (
int e = 0; e < 3; ++e)
1422 if (m_tris[neighbour].v[(e + 1) % 3] == r.ring[(j + 1) % r.ring.size()] &&
1423 m_tris[neighbour].v[(e + 2) % 3] == r.ring[j])
1424 m_tris[neighbour].adj[e] = replacement;
1426 for (
int t : r.star) {
1427 m_liveTriangles -= m_tris[
t].alive ? 1 : 0;
1428 m_tris[
t].alive =
false;
1429 for (
int &
a : m_tris[
t].
adj)
1432 for (
int i = 0;
i < r.replacement.size(); ++
i) {
1433 const int t = r.star[
i];
1434 m_tris[
t] = r.replacement[
i];
1435 m_liveTriangles += m_tris[
t].alive ? 1 : 0;
1436 for (
int q : m_tris[
t].v)
1438 if (m_triangleChanged)
1439 m_triangleChanged(
t);
1441 m_vertexTri[v] = -1;
1442 m_lastLocate = r.star[0];
1448 if (!prepareRemoval(v, &r))
1450 commitRemoval(v, r);
1454template <
class Storage>
1458 const bool resolutionOrder = opt.coarseningOrder == CoarseningOrder::ResolutionAware;
1459 if ((opt.coarseningOrder != CoarseningOrder::LegacyFlatness && !resolutionOrder) ||
1461 (!opt.coarseningTargetSize || !(opt.coarseningReferenceSize > 0) ||
1462 !std::isfinite(opt.coarseningReferenceSize) || !(opt.normalVerticalToXY > 0) ||
1463 !std::isfinite(opt.normalVerticalToXY))) ||
1464 (opt.terrainToleranceForFootprint &&
1465 (!(opt.terrainTolerance > 0) || !opt.terrainError || !opt.terrainElevationAt))) {
1471 for (
int k = 1;
k < 3; ++
k) {
1472 const auto p = m_pts[
cell.v[
k]];
1473 x0 = std::min(
x0,
p.x());
1474 x1 = std::max(
x1,
p.x());
1475 y0 = std::min(
y0,
p.y());
1476 y1 = std::max(
y1,
p.y());
1481 const double value = opt.terrainToleranceForFootprint
1482 ? opt.terrainToleranceForFootprint(triangleBox(
cell))
1483 : opt.terrainTolerance;
1486 if (opt.terrainToleranceForFootprint)
1488 return std::numeric_limits<double>::quiet_NaN();
1490 const double theta = std::clamp(opt.minAngleDeg, 0.0, 60.0) * M_PI / 180.0;
1491 const double sinMin = std::sin(theta);
1495 theta > 0.0 ? 0.475 * std::sqrt((1.0 + std::cos(theta)) / (1.0 - std::cos(theta))) : 0.0;
1497 const double minEdge2 = opt.minEdge > 0.0 ? opt.minEdge * opt.minEdge : 0.0;
1498 auto tooShortToSplit = [&](
int a,
int b) {
1499 return minEdge2 > 0.0 &&
dist2(m_pts[
a], m_pts[
b]) < 4.0 * minEdge2;
1501 const bool terrain = opt.terrainError && opt.terrainElevationAt && opt.terrainTolerance > 0;
1504 const double terrainSpacing = std::max(opt.terrainMinSpacing, opt.minEdge);
1506 auto terrainKey = [&](
const Point &
p) {
1507 return std::make_pair(std::int64_t(std::floor(
p.x() / terrainSpacing)),
1508 std::int64_t(std::floor(
p.y() / terrainSpacing)));
1510 auto indexTerrainVertex = [&](
int v) {
1511 if (terrain && terrainSpacing > 0 && !isSuperVertex(v))
1512 terrainGrid[terrainKey(m_pts[v])].append(v);
1514 auto rebuildTerrainGrid = [&] {
1515 terrainGrid.clear();
1516 for (
int v = 0; v < m_pts.size(); ++v)
1517 indexTerrainVertex(v);
1519 auto terrainClear = [&](
const Point &
p) {
1520 if (!(terrainSpacing > 0))
1522 const auto key = terrainKey(
p);
1523 for (
int dy = -1;
dy <= 1; ++
dy)
1524 for (
int dx = -1;
dx <= 1; ++
dx) {
1526 terrainGrid.constFind(std::make_pair(key.first +
dx, key.second +
dy));
1527 if (it != terrainGrid.cend())
1529 if (
dist2(
p, m_pts[v]) < terrainSpacing * terrainSpacing * (1.0 - 1e-12))
1535 rebuildTerrainGrid();
1536 m_terrainElevations.clear();
1537 auto &heights = m_terrainElevations;
1538 auto terrainError = [&](
int t,
Point *
p) {
1539 const auto &T = m_tris[
t];
1540 while (heights.size() < m_pts.size())
1541 heights.append(std::numeric_limits<double>::infinity());
1544 for (
int k = 0;
k < 3; ++
k) {
1545 const int v = T.v[
k];
1547 if (std::isinf(heights[v])) {
1548 const double h = opt.terrainElevationAt(xy[
k].
x(), xy[
k].y());
1549 heights[v] = std::isfinite(
h) ?
h : std::numeric_limits<double>::quiet_NaN();
1553 return opt.terrainError(xy, z,
p);
1559 int v[3] = {-1, -1, -1};
1563 auto measure = [&](
int t,
Point *
p) {
1564 const double e = terrainError(
t,
p);
1565 if (std::isfinite(e)) {
1566 if (measured.size() <=
t)
1567 measured.resize(std::max<std::ptrdiff_t>(
t + 1, measured.size() * 3 / 2));
1568 Measured &
m = measured[
t];
1569 for (
int k = 0;
k < 3; ++
k)
1570 m.v[
k] = m_tris[
t].v[
k];
1576 bool repairing =
false;
1577 int repairBegin = 0;
1578 auto footprintCap = [&](
const Triangle &
t) {
1579 if (!opt.maximumAreaForTriangle)
1581 const Point xy[3] = {m_pts[
t.v[0]], m_pts[
t.v[1]], m_pts[
t.v[2]]};
1582 return opt.maximumAreaForTriangle(xy);
1586 auto badPoint = [&](
int t,
Point *out) {
1589 if (!T.
alive || isSuperVertex(T.
v[0]) || isSuperVertex(T.
v[1]) || isSuperVertex(T.
v[2]))
1591 const Point &A = m_pts[T.
v[0]], &B = m_pts[T.
v[1]], &C = m_pts[T.
v[2]];
1592 const double cross = (B.x() - A.x()) * (C.y() - A.y()) - (B.y() - A.y()) * (C.x() - A.x());
1596 const int s = (l[0] <= l[1] && l[0] <= l[2]) ? 0 : (l[1] <= l[2] ? 1 : 2);
1599 const Point g = (A + B + C) / 3.0;
1600 const double h = opt.hAt(g.x(), g.y());
1606 if (!bad && opt.maximumAreaForTriangle) {
1607 const double cap = footprintCap(T);
1608 if (cap > 0 && .5 * cross > cap * (1 + 1e-10)) {
1613 if (!bad && repairing && opt.maximumAdjacentRatio >= 1) {
1614 for (
int neighbour : T.
adj) {
1615 if (neighbour < 0 || !m_tris[neighbour].alive)
1617 const auto &other = m_tris[neighbour];
1618 if (isSuperVertex(other.v[0]) || isSuperVertex(other.v[1]) ||
1619 isSuperVertex(other.v[2]))
1621 const double otherArea =
1622 std::abs(
orientPts(m_pts[other.v[0]], m_pts[other.v[1]], m_pts[other.v[2]])) *
1624 if (std::sqrt(.5 * cross / otherArea) > opt.maximumAdjacentRatio * (1 + 1e-10)) {
1630 if (!bad && opt.fixedNeighbourSize)
1631 for (
int k = 0;
k < 3; ++
k) {
1632 const double neighbour =
1633 opt.fixedNeighbourSize(T.
v[(
k + 1) % 3], T.
v[(
k + 2) % 3]);
1634 if (neighbour > 0 && std::sqrt(.5 * cross /
kEquilateralArea) / neighbour >
1635 opt.maximumAdjacentRatio * (1 + 1e-10)) {
1642 if (!bad && sinMin > 0.0 && l[
s] >= minEdge2) {
1645 std::sqrt(l[(
s + 1) % 3] * l[(
s + 2) % 3]);
1646 if (sinA < sinMin && !exemptShortestEdge(T.
v[(
s + 1) % 3], T.
v[(
s + 2) % 3])) {
1651 if (!bad && terrain && (!opt.terrainWorstFirst || repairing)) {
1652 const double e = measure(
t, out);
1653 if (std::isfinite(e) && e > toleranceFor(T)) {
1654 const double clearance = std::max(opt.terrainMinSpacing, opt.minEdge);
1658 clearance * clearance)
1667 const Point &P = m_pts[T.
v[(
s + 1) % 3]], &Q = m_pts[T.
v[(
s + 2) % 3]], &R = m_pts[T.
v[
s]];
1668 const double bx = Q.x() - P.x(), by = Q.y() - P.y(),
cx = R.x() - P.x(),
cy = R.y() - P.y();
1669 const double d = 2.0 * (bx *
cy - by *
cx);
1672 const double b2 = bx * bx + by * by, c2 =
cx *
cx +
cy *
cy;
1673 double ux = (
cy * b2 - by * c2) / d, uy = (bx * c2 -
cx * b2) / d;
1675 const double mx = 0.5 * bx, my = 0.5 * by;
1676 const double ox = mx - offK * by, oy = my + offK * bx;
1677 if ((ox - mx) * (ox - mx) + (oy - my) * (oy - my) <
1678 (ux - mx) * (ux - mx) + (uy - my) * (uy - my)) {
1683 *out =
Point(P.x() + ux, P.y() + uy);
1684 return std::isfinite(out->x()) && std::isfinite(out->y());
1690 auto walkTo = [&](
int t,
const Point &
p,
int *blockT,
int *blockE) {
1691 *blockT = *blockE = -1;
1693 const Point g = (m_pts[T0.
v[0]] + m_pts[T0.
v[1]] + m_pts[T0.
v[2]]) / 3.0;
1694 const int cap = 4 * m_tris.size() + 16;
1698 for (
int i = 0;
i < 3; ++
i)
1699 o[
i] = orient(T.
v[(
i + 1) % 3], T.
v[(
i + 2) % 3],
p);
1700 if (o[0] >= 0.0 && o[1] >= 0.0 && o[2] >= 0.0)
1703 for (
int i = 0;
i < 3 && exit < 0; ++
i) {
1706 const double s1 =
orientPts(g,
p, m_pts[T.
v[(
i + 1) % 3]]);
1707 const double s2 =
orientPts(g,
p, m_pts[T.
v[(
i + 2) % 3]]);
1708 if ((s1 <= 0.0 && s2 >= 0.0) || (s1 >= 0.0 && s2 <= 0.0))
1712 for (
int i = 0;
i < 3 && exit < 0; ++
i)
1720 const int n = T.
adj[exit];
1721 if (
n < 0 || !m_tris[
n].alive)
1732 const double pd[2] = {
p.x(),
p.y()};
1734 while (!stack.isEmpty()) {
1735 const int t = stack.takeLast();
1737 for (
int k = 0;
k < 3; ++
k) {
1738 const int a = T.
v[(
k + 1) % 3],
b = T.
v[(
k + 2) % 3];
1740 const Point &A = m_pts[
a], &B = m_pts[
b];
1741 if ((A.x() -
p.x()) * (B.x() -
p.x()) + (A.y() -
p.y()) * (B.y() -
p.y()) < 0.0)
1745 const int n = T.
adj[
k];
1746 if (
n < 0 || !m_tris[
n].alive || seen.contains(
n))
1749 if (isSuperVertex(N.
v[0]) || isSuperVertex(N.
v[1]) || isSuperVertex(N.
v[2]))
1751 const double pa[2] = {m_pts[N.
v[0]].x(), m_pts[N.
v[0]].y()};
1752 const double pb[2] = {m_pts[N.
v[1]].x(), m_pts[N.
v[1]].y()};
1753 const double pc[2] = {m_pts[N.
v[2]].x(), m_pts[N.
v[2]].y()};
1754 if (!(incircle(pa, pb, pc, pd) > 0.0))
1765 std::array<int, 32> heads, tails;
1769 auto enqueue = [&](
int t) {
1770 while (next.size() < m_tris.size())
1772 const auto &T = m_tris[
t];
1773 if (!T.alive || next[
t] != -2)
1776 if (opt.prioritizeQuality && sinMin > 0) {
1777 const auto &
a = m_pts[T.v[0]], &
b = m_pts[T.v[1]], &
c = m_pts[T.v[2]];
1778 const double cross =
1779 std::abs((
b.x() -
a.x()) * (
c.y() -
a.y()) - (
b.y() -
a.y()) * (
c.x() -
a.x()));
1781 const double denom = std::sqrt(std::max({l0 * l1, l1 * l2, l2 * l0}));
1783 bucket = std::clamp(
int(31 * cross / (denom * sinMin)), 0, 31);
1786 if (tails[bucket] >= 0)
1787 next[tails[bucket]] =
t;
1792 auto dequeue = [&]() {
1793 for (
int k = 0;
k < 32; ++
k)
1794 if (heads[
k] >= 0) {
1795 const int t = heads[
k];
1804 m_triangleChanged = enqueue;
1808 auto publishProgress = [&] {
1809 if (opt.onProgress) {
1811 state.
stage = progressStage;
1812 state.
workingVertices = std::max<std::ptrdiff_t>(0, m_pts.size() - 3);
1816 opt.onProgress(state);
1820 const auto clearObserver = detail::scopeExit([&] { m_triangleChanged = {}; });
1821 auto queueEncroachedOf = [&](
int t) {
1823 for (
int k = 0;
k < 3; ++
k) {
1826 const int a = T.
v[(
k + 1) % 3],
b = T.
v[(
k + 2) % 3];
1827 if (isSuperVertex(
a) || isSuperVertex(
b) || isFixedSub(
a,
b))
1829 if (subsegmentEncroached(
a,
b))
1833 auto afterInsert = [&](
int v) {
1834 indexTerrainVertex(v);
1837 trianglesAround(v, &star);
1838 for (
int t : std::as_const(star))
1839 if (m_tris[
t].alive) {
1841 queueEncroachedOf(
t);
1843 if (rep.
inserted >= opt.maxInsertions || liveTriangleCount() >= opt.maxTriangles) {
1847 if (repairing && rep.
inserted - repairBegin >= opt.maximumSizingRepairInsertions)
1851 if (opt.cancelled && opt.cancelled()) {
1857 auto drainSegments = [&]() {
1859 const std::uint64_t
k = segQueue.takeLast();
1861 if (isFixedSub(
a,
b) || tooShortToSplit(
a,
b) || !isConstrained(
a,
b) ||
1862 !subsegmentEncroached(
a,
b))
1864 const int v = splitSubsegment(
a,
b);
1872 for (
int i = 0;
i < m_tris.size(); ++
i)
1873 if (m_tris[
i].alive) {
1875 queueEncroachedOf(
i);
1877 if (liveTriangleCount() >= opt.maxTriangles) {
1886 auto tryInsert = [&](
int t,
Point p) {
1887 if (
reason == 3 && !terrainClear(
p))
1890 int blockT = -1, blockE = -1;
1891 int c = walkTo(
t,
p, &blockT, &blockE);
1896 edgeKey64(m_tris[blockT].v[(blockE + 1) % 3], m_tris[blockT].v[(blockE + 2) % 3]));
1898 cavityEncroached(
c,
p, &enc);
1899 if (!enc.isEmpty()) {
1903 for (std::uint64_t
k : std::as_const(enc)) {
1905 if (stop || isFixedSub(
a,
b) || tooShortToSplit(
a,
b) || !isConstrained(
a,
b))
1907 if (
reason == 3 && !terrainClear((m_pts[
a] + m_pts[
b]) * 0.5))
1909 const int v = splitSubsegment(
a,
b);
1921 if (!repairing ||
reason != 1 || !opt.maximumAreaForTriangle)
1923 const auto &
cell = m_tris[
t];
1924 const auto a = m_pts[
cell.v[0]],
b = m_pts[
cell.v[1]], cxy = m_pts[
cell.v[2]];
1925 const double cap = footprintCap(
cell);
1926 if (!(cap > 0) ||
orientPts(
a,
b, cxy) * .5 <= cap * (1 + 1e-10))
1928 const Point center =
a + ((
b -
a) + (cxy -
a)) / 3.;
1929 if (!std::isfinite(center.x()) || !std::isfinite(center.y()) ||
1930 std::min({
dist2(center,
a),
dist2(center,
b),
dist2(center, cxy)}) < minEdge2 ||
1935 cavityEncroached(
t, center, &enc);
1936 for (std::uint64_t key : enc)
1948 const int before = m_pts.size();
1949 const int v = insertPoint(
p);
1950 if (v < 0 || v < before)
1952 addVertexOrigin(v,
reason == 1 ? Size :
reason == 2 ? Angle : Terrain);
1964 auto drainQueue = [&]() {
1966 const int t = dequeue();
1969 if ((++examined & 4095) == 0) {
1971 if (opt.cancelled && opt.cancelled()) {
1977 if (!badPoint(
t, &
p))
1986 if (opt.smoothingPasses > 0 && (!terrain || opt.terrainWorstFirst) && !stop && !rep.
cancelled) {
1987 progressStage = GenerationProgress::Stage::Smoothing;
1989 auto minAngleOf = [&](
int t) {
1990 const auto &T = m_tris[
t];
1991 const Point &
a = m_pts[T.v[0]], &
b = m_pts[T.v[1]], &
c = m_pts[T.v[2]];
1993 const double cross =
1994 std::abs((
b.x() -
a.x()) * (
c.y() -
a.y()) - (
b.y() -
a.y()) * (
c.x() -
a.x()));
1996 const int s = (l[0] <= l[1] && l[0] <= l[2]) ? 0 : (l[1] <= l[2] ? 1 : 2);
1997 return cross / std::sqrt(l[(
s + 1) % 3] * l[(
s + 2) % 3]);
1999 auto positive = [&](
int t) {
2000 const auto &T = m_tris[
t];
2001 const Point &
a = m_pts[T.v[0]], &
b = m_pts[T.v[1]], &
c = m_pts[T.v[2]];
2002 return (
b.x() -
a.x()) * (
c.y() -
a.y()) - (
b.y() -
a.y()) * (
c.x() -
a.x()) > 0.0;
2004 const int firstFree = m_superBase + 3;
2006 for (
int pass = 0; pass < opt.smoothingPasses; ++pass) {
2007 for (
int v = firstFree; v < m_pts.size(); ++v) {
2009 trianglesAround(v, &star);
2010 if (star.size() < 3)
2013 double wsum = 0,
cx = 0,
cy = 0, before = 1e300;
2014 for (
int t : std::as_const(star)) {
2015 const auto &T = m_tris[
t];
2016 const int i = edgeIndex(
t, v);
2017 if (!T.alive ||
i < 0 || isSuperVertex(T.v[0]) || isSuperVertex(T.v[1]) ||
2018 isSuperVertex(T.v[2]) || T.constrained[(
i + 1) % 3] ||
2019 T.constrained[(
i + 2) % 3] || T.adj[(
i + 1) % 3] < 0 ||
2020 T.adj[(
i + 2) % 3] < 0) {
2024 const Point &
a = m_pts[T.v[0]], &
b = m_pts[T.v[1]], &
c = m_pts[T.v[2]];
2025 const double area = 0.5 * std::abs((
b.x() -
a.x()) * (
c.y() -
a.y()) -
2026 (
b.y() -
a.y()) * (
c.x() -
a.x()));
2027 cx +=
area * (
a.x() +
b.x() +
c.x()) / 3.0;
2028 cy +=
area * (
a.y() +
b.y() +
c.y()) / 3.0;
2030 before = std::min(before, minAngleOf(
t));
2032 if (fixed || !(
wsum > 0.0))
2034 const Point old = m_pts[v];
2036 double after = 1e300;
2038 for (
int t : std::as_const(star)) {
2043 after = std::min(after, minAngleOf(
t));
2045 if (!valid || !(after > before * (1.0 + 1e-9))) {
2049 for (
int t : std::as_const(star))
2050 if (m_tris[
t].alive) {
2051 const int i = edgeIndex(
t, v);
2063 if (terrain && opt.terrainWorstFirst && !stop && !rep.
cancelled) {
2064 progressStage = GenerationProgress::Stage::Terrain;
2067 rebuildTerrainGrid();
2074 auto worse = [](
const Candidate &
a,
const Candidate &
b) {
return a.e <
b.e; };
2075 std::priority_queue<Candidate, std::vector<Candidate>,
decltype(worse)> heap(worse);
2076 auto consider = [&](
int t) {
2078 if (!T.
alive || isSuperVertex(T.
v[0]) || isSuperVertex(T.
v[1]) || isSuperVertex(T.
v[2]))
2080 const Point &A = m_pts[T.
v[0]], &B = m_pts[T.
v[1]], &C = m_pts[T.
v[2]];
2081 if (!((B.x() - A.x()) * (C.y() - A.y()) - (B.y() - A.y()) * (C.x() - A.x()) > 0.0))
2084 const double e = measure(
t, &
p);
2085 const double localTolerance = toleranceFor(T);
2086 if (!std::isfinite(e) || !std::isfinite(localTolerance) || e <= localTolerance)
2088 const double clearance = std::max(opt.terrainMinSpacing, opt.minEdge);
2091 heap.push({opt.terrainToleranceForFootprint ? e / localTolerance : e,
2093 {T.
v[0], T.
v[1], T.
v[2]},
2096 for (
int t = 0;
t < m_tris.size(); ++
t)
2099 m_triangleChanged = [&](
int t) {
2103 while (!heap.empty() && !stop) {
2104 const Candidate
c = heap.top();
2107 if (!T.
alive || T.
v[0] !=
c.v[0] || T.
v[1] !=
c.v[1] || T.
v[2] !=
c.v[2])
2109 if ((++examined & 4095) == 0 && opt.cancelled && opt.cancelled()) {
2115 if (!tryInsert(
c.t,
c.p))
2118 for (
int t : std::as_const(
changed))
2121 m_triangleChanged = enqueue;
2126 m_triangleChanged = {};
2127 if (opt.coarsenPasses > 0 && !rep.
cancelled) {
2128 progressStage = GenerationProgress::Stage::Coarsening;
2134 const double unknown = std::numeric_limits<double>::quiet_NaN();
2138 const auto freeSlots = std::max<std::ptrdiff_t>(0, m_pts.size() - m_superBase - 3);
2139 CoarseningQueue resolutionHeap(resolutionOrder && freeSlots ? m_pts.size() : 0,
2140 resolutionOrder ? freeSlots : 0);
2143 double x0 = std::numeric_limits<double>::infinity(),
y0 =
x0,
x1 = -
x0,
y1 = -
x0;
2145 for (
const auto &T : m_tris)
2148 if (!isSuperVertex(v)) {
2150 x0 = std::min(
x0, m_pts[v].
x());
2151 y0 = std::min(
y0, m_pts[v].
y());
2152 x1 = std::max(
x1, m_pts[v].
x());
2153 y1 = std::max(
y1, m_pts[v].
y());
2155 auto key = [&](
int v,
unsigned scale) {
2158 ? unsigned(std::clamp((m_pts[v].
x() -
x0) / (
x1 -
x0), 0.0, 1.0) *
scale)
2162 ? unsigned(std::clamp((m_pts[v].
y() -
y0) / (
y1 -
y0), 0.0, 1.0) *
scale)
2170 while (heights.size() < m_pts.size())
2171 heights.append(std::numeric_limits<double>::infinity());
2173 for (
int v = 0; v < m_pts.size(); ++v)
2174 if (
used[v] && std::isinf(heights[v]))
2175 pending.append({key(v, 65535), v});
2176 std::sort(pending.begin(), pending.end());
2177 for (
int i = 0;
i < pending.size(); ++
i) {
2178 if ((
i & 4095) == 0 && opt.cancelled && opt.cancelled()) {
2182 const int v = pending[
i].second;
2183 heights[v] = opt.terrainElevationAt(m_pts[v].
x(), m_pts[v].
y());
2189 unsigned version, tile;
2191 auto less = [](
const Candidate &
a,
const Candidate &
b) {
2192 if (
a.tile !=
b.tile)
2193 return a.tile >
b.tile;
2194 return a.score !=
b.score ?
a.score <
b.score :
a.v >
b.v;
2196 std::priority_queue<Candidate, std::vector<Candidate>,
decltype(less)> heap(less);
2197 auto height = [&](
int v) {
2198 while (heights.size() < m_pts.size())
2199 heights.append(std::numeric_limits<double>::infinity());
2200 if (std::isinf(heights[v]))
2201 heights[v] = opt.terrainElevationAt(m_pts[v].x(), m_pts[v].y());
2204 auto score = [&](
int v,
const Vector<int> &star) {
2207 std::array<double, 3>
sum{0, 0, 0};
2209 for (
int t : star) {
2210 const auto &T = m_tris[
t];
2211 const Point b = m_pts[T.v[1]] - m_pts[T.v[0]],
2212 c = m_pts[T.v[2]] - m_pts[T.v[0]];
2213 const double scale = resolutionOrder ? opt.normalVerticalToXY : 1.;
2216 if (!std::isfinite(z) || !std::isfinite(bz) || !std::isfinite(cz))
2218 std::array<double, 3> normal{
b.y() * cz - bz *
c.y(), bz *
c.x() -
b.x() * cz,
2219 b.x() *
c.y() -
b.y() *
c.x()};
2220 const double length = std::hypot(normal[0], normal[1], normal[2]);
2223 for (
int k = 0;
k < 3; ++
k) {
2224 sum[
k] += normal[
k];
2227 normals.append(normal);
2233 for (
const auto &
n : normals)
2238 const int limit = std::clamp(opt.coarsenPasses, 1, 10);
2239 auto queue = [&](
int v) {
2240 if (v < m_superBase + 3 || !isVertexActive(v) || m_vertexSeg.contains(v) ||
2241 opt.protectedVertices.contains(v) ||
2242 (opt.protectedLocation && opt.protectedLocation(m_pts[v])) ||
2243 attempts[v] >=
limit)
2246 if (terrain || resolutionOrder)
2247 trianglesAround(v, &star);
2248 const double flatness = score(v, star);
2249 if (!resolutionOrder) {
2250 heap.push({float(flatness), v, ++versions[v], unsigned(key(v, 31))});
2256 double totalArea = 0;
2258 for (
int t : star) {
2259 const auto &
cell = m_tris[
t];
2260 const auto bound = triangleBox(
cell);
2267 double target = opt.coarseningTargetSize(
box);
2273 target = opt.coarseningReferenceSize;
2274 const double actual =
2275 star.isEmpty() ? 0 : std::sqrt(totalArea / (star.size() *
kEquilateralArea));
2277 const double importance =
2279 : std::numeric_limits<double>::infinity();
2280 const auto compact = [](
double value) {
2281 return float(std::min(
value,
double(std::numeric_limits<float>::max())));
2283 resolutionHeap.update(
2284 {compact(importance), compact(
density), v, unsigned(key(v, 31))});
2290 if ((v & 4095) == 0 && opt.cancelled && opt.cancelled()) {
2297 const auto &
a = m_pts[T.
v[0]], &
b = m_pts[T.
v[1]], &
c = m_pts[T.
v[2]];
2298 const double z =
height(T.
v[0]);
2301 ((
p.x() -
a.x()) * (
c.y() -
a.y()) - (
p.y() -
a.y()) * (
c.x() -
a.x())) / cross;
2303 ((
b.x() -
a.x()) * (
p.y() -
a.y()) - (
b.y() -
a.y()) * (
p.x() -
a.x())) / cross;
2306 while (!(resolutionOrder ? resolutionHeap.empty() : heap.empty()) && !rep.
cancelled &&
2309 if (resolutionOrder) {
2310 const auto next = resolutionHeap.pop();
2317 if ((!resolutionOrder &&
c.version != versions[v]) || !isVertexActive(v))
2321 if (opt.cancelled && opt.cancelled()) {
2329 if (!prepareRemoval(v, &r))
2331 double shortest = std::numeric_limits<double>::infinity();
2332 for (
int t : r.star)
2333 for (
int k = 0;
k < 3; ++
k)
2334 shortest = std::min(shortest,
dist2(m_pts[m_tris[
t].v[
k]],
2335 m_pts[m_tris[
t].v[(
k + 1) % 3]]));
2336 bool accepted =
true;
2337 for (
const auto &T : r.replacement) {
2338 const Point &
a = m_pts[T.
v[0]], &
b = m_pts[T.
v[1]], &
c = m_pts[T.
v[2]];
2341 const int k = l[0] <= l[1] && l[0] <= l[2] ? 0 : l[1] <= l[2] ? 1 : 2;
2342 if (l[
k] < shortest * (1 - 1e-12)) {
2347 if (l[
k] >= minEdge2 &&
2348 cross / std::sqrt(l[(
k + 1) % 3] * l[(
k + 2) % 3]) < sinMin &&
2349 !exemptShortestEdge(T.
v[(
k + 1) % 3], T.
v[(
k + 2) % 3])) {
2355 const double h = opt.hAt ? opt.hAt(g.x(), g.y()) : 0;
2361 if (resolutionOrder) {
2362 const double target = opt.coarseningTargetSize(triangleBox(T));
2368 if (
target > 0 &&
static_cast<long double>(cross) * .5 >
2376 const double cap = footprintCap(T);
2377 if (cap > 0 && cross * .5 > cap * (1 + 1e-10)) {
2389 for (
const auto &T : r.replacement)
2390 if (orient(T.
v[0], T.
v[1], m_pts[v]) >= 0 &&
2391 orient(T.
v[1], T.
v[2], m_pts[v]) >= 0 &&
2392 orient(T.
v[2], T.
v[0], m_pts[v]) >= 0) {
2393 const double error = std::abs(plane(T, m_pts[v]) -
height(v));
2394 if (!std::isfinite(
error) || !std::isfinite(toleranceFor(T)) ||
2395 error > toleranceFor(T)) {
2401 for (
const auto &T : r.replacement) {
2408 for (
int t : r.star) {
2409 if (!std::isfinite(
bounds[
t])) {
2413 double difference = 0;
2414 for (
int q : m_tris[
t].v) {
2415 const double d = std::abs(plane(T, m_pts[q]) -
height(q));
2416 if (!std::isfinite(d)) {
2417 difference = unknown;
2420 difference = std::max(difference, d);
2422 if (!std::isfinite(difference)) {
2426 bound = std::max(bound,
bounds[
t] + difference);
2428 if (std::isfinite(bound) && bound <= toleranceFor(T) * (1 - 1e-10))
2433 for (
int k = 0;
k < 3; ++
k) {
2434 xy[
k] = m_pts[T.
v[
k]];
2437 bound = opt.terrainErrorBound(xy, z);
2440 if (!std::isfinite(bound) || bound < 0 || !std::isfinite(toleranceFor(T)) ||
2441 bound > toleranceFor(T))
2443 newBounds.append(bound);
2450 commitRemoval(v, r);
2452 for (
int i = 0;
i < r.star.size(); ++
i) {
2453 const int t = r.star[
i];
2454 if (
t < measured.size())
2455 measured[
t] = Measured{};
2457 bounds[
t] =
i < newBounds.size() ? newBounds[
i] : unknown;
2459 for (
int q : r.ring)
2468 if (opt.sizingRepairPasses > 0 && !stop && !rep.
cancelled) {
2471 if (opt.maximumSizingRepairInsertions <= 0)
2473 progressStage = GenerationProgress::Stage::Refining;
2474 m_triangleChanged = [&](
int t) {
2476 for (
int n : m_tris[
t].adj)
2480 for (
int pass = 0; pass < std::clamp(opt.sizingRepairPasses, 0, 3) && !stop &&
2485 next.fill(-2, m_tris.size());
2488 for (
int t = 0;
t < m_tris.size(); ++
t) {
2489 if ((
t & 4095) == 0 && opt.cancelled && opt.cancelled()) {
2493 if (m_tris[
t].alive)
2503 m_triangleChanged = {};
2508 if (terrain && opt.terrainFinalCheck && !rep.
cancelled) {
2509 progressStage = GenerationProgress::Stage::Verifying;
2511 for (
int t = 0;
t < m_tris.size(); ++
t) {
2512 const auto &T = m_tris[
t];
2513 if (!T.alive || isSuperVertex(T.v[0]) || isSuperVertex(T.v[1]) || isSuperVertex(T.v[2]))
2515 if ((
t & 4095) == 0 && opt.cancelled && opt.cancelled()) {
2520 if (
t < measured.size() && measured[
t].v[0] == T.v[0] && measured[
t].v[1] == T.v[1] &&
2521 measured[
t].v[2] == T.v[2])
2525 e = terrainError(
t, &
p);
2527 if (!std::isfinite(e) || !std::isfinite(toleranceFor(T)))
2531 if (e > toleranceFor(T))
double step
Definition aquifereditordialog.cpp:62
typename Storage::PointInput PointInput
Definition cdt.hpp:53
typename Storage::template Vector< T > Vector
Definition cdt.hpp:54
int liveTriangleCount() const
Count slots whose alive flag is true, including super faces if still alive.
Definition cdt_impl.hpp:381
static double orientExact(const Point &a, const Point &b, const Point &c)
Return adaptive-exact orientation sign after thread-safe one-time predicate initialization.
Definition cdt_impl.hpp:137
bool build(const PointInput &points, Vector< int > *vertexOfPoint=nullptr, const std::function< bool()> &cancelled={}, const std::function< void(std::uint64_t)> &progress={})
Reset and incrementally triangulate distinct finite points inside a constructed super triangle.
Definition cdt_impl.hpp:645
int locate(const Point &p) const
Locate a finite point and require its found triangle to be alive.
Definition cdt_impl.hpp:375
typename Storage::template Hash< K, V > Hash
Definition cdt.hpp:55
bool isConstrained(int a, int b) const
Check whether a direct edge between two stored vertices is marked constrained.
Definition cdt_impl.hpp:258
Vector< int > constrainedChain(int a, int b) const
Follow direct, origin-associated or collinear constrained edges from one endpoint to another.
Definition cdt_impl.hpp:268
typename Storage::Box Box
Definition cdt.hpp:51
typename Storage::Point Point
Definition cdt.hpp:50
Definition coarseningqueue.hpp:13
Definition storage.hpp:196
std::int64_t nsecsElapsed() const
Definition storage.hpp:201
void start()
Definition storage.hpp:200
bool stopped
Definition constraints.cpp:63
size_t i
Definition contourjob.cpp:30
DiagramType t
Definition diagramspec.cpp:23
std::function< void(bool)> changed
Guarded layer refresh callback.
Definition extensionattributes.cpp:36
QString target
Definition initialbedqualitydialog.cpp:51
double s
Scene px per model length unit.
Definition inletdrawingview.cpp:95
std::string reason
Definition job.cpp:20
int density
Plants across the cell.
Definition lidlayerdiagram.cpp:101
ArrowPlacement p
Definition linesymbollayer.cpp:29
MaskMode m
Definition maskspec.cpp:21
hid_t id
Definition mesh2dh5reader.cpp:54
int h
Definition mesh2dresultsexport.cpp:477
int w
Definition mesh2dresultsexport.cpp:477
CellAdjacency adj
Definition mesh2dresultsexport.cpp:693
std::vector< double > cx
Definition mesh2dresultsexport.cpp:131
std::vector< double > cy
area centroid per cell
Definition mesh2dresultsexport.cpp:131
std::vector< int > start
size = pixels + 1
Definition mesh2dresultsexport.cpp:762
std::vector< double > len
Definition mesh2dresultsexport.cpp:571
std::vector< float > dx
Definition mesh2dresultsexport.cpp:765
std::size_t n
Definition mesh2dresultsexport.cpp:538
int k
Definition mesh2dresultsexport.cpp:694
std::vector< int > cell
Definition mesh2dresultsexport.cpp:763
std::vector< float > dy
Green–Gauss only, parallel to cell
Definition mesh2dresultsexport.cpp:765
double sum
Σ weight · value.
Definition meshassignmentsampling.cpp:285
double wsum
Σ weight.
Definition meshassignmentsampling.cpp:286
void triangle(QImage &image, std::array< Vertex, 3 > v)
Composite one Gouraud-shaded triangle using a shared-edge ownership rule.
Definition smoothfillraster.h:40
std::uint64_t mortonKey(std::uint32_t x, std::uint32_t y)
Interleave two unsigned 32-bit coordinates into a Morton ordering key.
Definition cdt_impl.hpp:40
constexpr double kEquilateralArea
Definition cdt_impl.hpp:129
std::once_flag gPredicatesInit
Definition cdt_impl.hpp:31
double dist2(const Point &a, const Point &b)
Compute ordinary squared Cartesian distance without overflow/finite checking.
Definition cdt_impl.hpp:112
int keyHi(std::uint64_t k)
Recover the larger endpoint word from an edgeKey64 key.
Definition cdt_impl.hpp:84
std::uint64_t edgeKey64(int a, int b)
Pack a normalized integer vertex pair into a 64-bit undirected edge identity.
Definition cdt_impl.hpp:63
bool refinementCoincident(const Point &a, const Point &b)
Definition cdt_impl.hpp:120
int keyLo(std::uint64_t k)
Recover the smaller endpoint word from an edgeKey64 key.
Definition cdt_impl.hpp:75
double orientPts(const Point &a, const Point &b, const Point &c)
Evaluate adaptive orientation on three borrowed Cartesian points after predicate initialization.
Definition cdt_impl.hpp:99
Definition qtcdtstorage.h:9
double resolutionRemovalImportance(double flatness, double actualSize, double targetSize)
Definition terrainpolicy.hpp:51
Box box
Definition constraintaudit.cpp:161
int chain
Definition constraintshared.cpp:85
double length
Definition patch.cpp:102
Vector< int > order
Definition patch.cpp:166
double area
Definition raingageassignment.cpp:302
double value
Definition rasterfluxintegration.cpp:27
double height
Full depth of the section (0 → thin stub).
Definition sectionmodelbuilders.cpp:717
std::uint64_t limit
Definition smsio.cpp:24
Options for refineQuality().
Definition cdt.hpp:231
bool coarsenSkippedTerrain
Definition cdt.hpp:314
int segmentSplits
Definition cdt.hpp:303
bool capped
Stopped at maxInsertions.
Definition cdt.hpp:309
std::uint64_t coarsenQueuePeak
Definition cdt.hpp:316
int inserted
Vertices added, segment splits included.
Definition cdt.hpp:302
int terrainInsertions
Definition cdt.hpp:306
int terrainUnresolved
Definition cdt.hpp:307
int terrainUnknown
Definition cdt.hpp:307
int sizingRepairInserted
Definition cdt.hpp:300
int coarsenAttempts
Definition cdt.hpp:312
int coarsenExactQueries
Definition cdt.hpp:313
int coarsenBoundAccepts
Definition cdt.hpp:313
int sizeInsertions
Definition cdt.hpp:306
int coarsenRejectedSize
Definition cdt.hpp:311
int coarsenRejectedEdge
Definition cdt.hpp:312
bool sizingRepairLimited
Definition cdt.hpp:301
int coarsenRejectedTerrain
Definition cdt.hpp:312
double maxTerrainError
Definition cdt.hpp:308
int coarsenRejectedAngle
Definition cdt.hpp:311
int coarsenRemoved
Definition cdt.hpp:311
bool cancelled
Definition cdt.hpp:310
int qualityInsertions
Definition cdt.hpp:306
int coarsenHeightRejects
Definition cdt.hpp:313
bool invalidPolicy
Definition cdt.hpp:317
std::uint64_t coarsenPriorityQueries
Definition cdt.hpp:316
int sizingRepairPasses
Definition cdt.hpp:300
int blockedByFixed
Bad triangles left because their point would encroach a fixed edge.
Definition cdt.hpp:304
double coarsenSeconds
Definition cdt.hpp:315
int adj[3]
Neighbour across the edge opposite v[i] (v[i+1], v[i+2]); -1 = none.
Definition cdt.hpp:60
bool alive
Definition cdt.hpp:62
int v[3]
Counter-clockwise.
Definition cdt.hpp:59
bool constrained[3]
Edge opposite v[i] is a constraint.
Definition cdt.hpp:61
Definition progress.hpp:7
Stage stage
Definition progress.hpp:9
std::int64_t triangles
Definition progress.hpp:12
std::int64_t inserted
Definition progress.hpp:14
std::int64_t removed
Definition progress.hpp:14
Stage
Definition progress.hpp:8
std::int64_t workingVertices
Definition progress.hpp:11
int y
Definition terrainerrorfield.cpp:60
double a
Definition terrainerrorfield.cpp:42
double b
Definition terrainerrorfield.cpp:42
double error
Definition terrainerrorfield.cpp:42
double c
Definition terrainerrorfield.cpp:42
int x
Definition terrainerrorfield.cpp:60
double y0
Definition terrainerrorfield.cpp:41
double y1
Definition terrainerrorfield.cpp:41
double x1
Definition terrainerrorfield.cpp:41
double x0
Definition terrainerrorfield.cpp:41
double scale
Definition timeseriesassignment.cpp:21
Sample first
Definition timeseriesrunlayer.cpp:51
bool used
Definition timeseriesrunlayer.cpp:50
QRectF bounds
Definition traceanalysislayer.cpp:165
const ExportProgress * progress
Definition vectorexport.cpp:211
bool cancelled
Definition vectorexport.cpp:214