#include "clipper2/clipper.h"
#include "clipper2/clipper.triangulation.h"
namespace Clipper2Lib
{
enum class EdgeKind { loose, ascend, descend }; enum class IntersectKind { none, collinear, intersect };
enum class EdgeContainsResult { neither, left, right };
class Vertex2;
class Edge;
class Triangle;
typedef std::vector<Vertex2*> VertexList;
typedef std::vector<Edge*> EdgeList;
class Vertex2 {
public:
Point64 pt;
EdgeList edges;
bool innerLM = false;
Vertex2(const Point64& p64) : pt(p64) { edges.reserve(2); };
};
class Edge {
public:
Vertex2* vL = nullptr;
Vertex2* vR = nullptr;
Vertex2* vB = nullptr;
Vertex2* vT = nullptr;
EdgeKind kind = EdgeKind::loose;
Triangle* triA = nullptr;
Triangle* triB = nullptr;
bool isActive = false;
Edge* nextE = nullptr;
Edge* prevE = nullptr;
};
class Triangle {
public:
Edge* edges[3];
Triangle(Edge* e1, Edge* e2, Edge* e3)
{
edges[0] = e1;
edges[1] = e2;
edges[2] = e3;
}
};
class Delaunay {
private:
VertexList allVertices;
EdgeList allEdges;
std::vector<Triangle*> allTriangles;
std::stack<Edge*> pendingDelaunayStack;
std::stack<Edge*> horzEdgeStack;
std::stack<Vertex2*> locMinStack;
bool useDelaunay = true;
Vertex2* lowermostVertex = nullptr;
Edge* firstActive = nullptr;
void AddPath(const Path64& path);
bool AddPaths(const Paths64& paths);
void CleanUp();
bool FixupEdgeIntersects();
void MergeDupOrCollinearVertices();
void SplitEdge(Edge* longE, Edge* shortE);
bool RemoveIntersection(Edge* e1, Edge* e2);
Edge* CreateEdge(Vertex2* v1, Vertex2* v2, EdgeKind k);
Triangle* CreateTriangle(Edge* e1, Edge* e2, Edge* e3);
Edge* CreateInnerLocMinLooseEdge(Vertex2* vAbove);
Edge* HorizontalBetween(Vertex2* v1, Vertex2* v2);
void DoTriangulateLeft(Edge* edge, Vertex2* pivot, int64_t minY);
void DoTriangulateRight(Edge* edge, Vertex2* pivot, int64_t minY);
void AddEdgeToActives(Edge* edge);
void RemoveEdgeFromActives(Edge* edge);
void ForceLegal(Edge* edge);
public:
explicit Delaunay(bool delaunay = true) : useDelaunay(delaunay) {};
~Delaunay() { CleanUp(); };
Paths64 Execute(const Paths64& paths, TriangulateResult& triResult);
};
static bool VertexListSort(const Vertex2* a, const Vertex2* b)
{
return (a->pt.y == b->pt.y) ? (a->pt.x < b->pt.x) : (a->pt.y > b->pt.y);
}
static bool EdgeListSort(const Edge* a, const Edge* b)
{
return (a->vL->pt.x < b->vL->pt.x);
}
static bool IsLooseEdge(const Edge& e)
{
return e.kind == EdgeKind::loose;
}
static bool IsLeftEdge(const Edge& e)
{
return e.kind == EdgeKind::ascend;
}
static bool IsRightEdge(const Edge& e)
{
return e.kind == EdgeKind::descend;
}
static bool IsHorizontal(const Edge& e)
{
return e.vB->pt.y == e.vT->pt.y;
}
static bool LeftTurning(const Point64& p1, const Point64& p2, const Point64& p3)
{
return CrossProductSign(p1, p2, p3) < 0;
}
static bool RightTurning(const Point64& p1, const Point64& p2, const Point64& p3)
{
return CrossProductSign(p1, p2, p3) > 0;
}
static bool EdgeCompleted(Edge* edge)
{
if (!edge->triA) return false;
if (edge->triB) return true;
return edge->kind != EdgeKind::loose;
}
static EdgeContainsResult EdgeContains(const Edge* edge, const Vertex2* v)
{
if (edge->vL == v) return EdgeContainsResult::left;
else if (edge->vR == v) return EdgeContainsResult::right;
else return EdgeContainsResult::neither;
}
static double GetAngle(const Point64& a, const Point64& b, const Point64& c)
{
double abx = static_cast<double>(b.x - a.x);
double aby = static_cast<double>(b.y - a.y);
double bcx = static_cast<double>(b.x - c.x);
double bcy = static_cast<double>(b.y - c.y);
double dp = (abx * bcx + aby * bcy);
double cp = (abx * bcy - aby * bcx);
return std::atan2(cp, dp); }
static double GetLocMinAngle(Vertex2* v)
{
int asc, des;
if (v->edges[0]->kind == EdgeKind::ascend)
{
asc = 0;
des = 1;
}
else
{
des = 0;
asc = 1;
}
return GetAngle(v->edges[des]->vT->pt, v->pt, v->edges[asc]->vT->pt);
}
static void RemoveEdgeFromVertex(Vertex2* vert, Edge* edge)
{
auto it = std::find(vert->edges.begin(), vert->edges.end(), edge);
if (it == vert->edges.end()) DoError(undefined_error_i);
vert->edges.erase(it);
}
static bool FindLocMinIdx(const Path64& path, size_t len, size_t& idx)
{
if (len < 3) return false;
size_t i0 = idx, n = (idx + 1) % len;
while (path[n].y <= path[idx].y)
{
idx = n; n = (n + 1) % len;
if (idx == i0) return false; }
while (path[n].y >= path[idx].y)
{
idx = n; n = (n + 1) % len;
}
return true;
}
static size_t Prev(size_t& idx, size_t len)
{
if (idx == 0) return len - 1; else return idx - 1;
}
static size_t Next(size_t& idx, size_t len)
{
return (idx + 1) % len;
}
static Edge* FindLinkingEdge(const Vertex2* vert1, const Vertex2* vert2, bool preferAscending)
{
Edge* res = nullptr;
for (auto e : vert1->edges)
{
if (e->vL == vert2 || e->vR == vert2)
{
if (e->kind == EdgeKind::loose ||
((e->kind == EdgeKind::ascend) == preferAscending)) return e;
res = e;
}
}
return res;
}
static Path64 PathFromTriangle(Triangle tri)
{
Path64 res;
res.reserve(3);
res.push_back(tri.edges[0]->vL->pt);
res.push_back(tri.edges[0]->vR->pt);
const Edge& e = *tri.edges[1];
if (e.vL->pt == res[0] || e.vL->pt == res[1])
res.push_back(e.vR->pt);
else
res.push_back(e.vL->pt);
return res;
}
static double InCircleTest(const Point64& ptA, const Point64& ptB,
const Point64& ptC, const Point64& ptD)
{
double m00 = static_cast<double>(ptA.x - ptD.x);
double m01 = static_cast<double>(ptA.y - ptD.y);
double m02 = (Sqr(m00) + Sqr(m01));
double m10 = static_cast<double>(ptB.x - ptD.x);
double m11 = static_cast<double>(ptB.y - ptD.y);
double m12 = (Sqr(m10) + Sqr(m11));
double m20 = static_cast<double>(ptC.x - ptD.x);
double m21 = static_cast<double>(ptC.y - ptD.y);
double m22 = (Sqr(m20) + Sqr(m21));
return m00 * (m11 * m22 - m21 * m12) -
m10 * (m01 * m22 - m21 * m02) +
m20 * (m01 * m12 - m11 * m02);
}
static double ShortestDistFromSegment(const Point64& pt, const Point64& segPt1, const Point64& segPt2)
{
double dx = static_cast<double>(segPt2.x - segPt1.x);
double dy = static_cast<double>(segPt2.y - segPt1.y);
double ax = static_cast<double>(pt.x - segPt1.x);
double ay = static_cast<double>(pt.y - segPt1.y);
double qNum = ax * dx + ay * dy;
if (qNum < 0) return DistanceSqr(pt, segPt1);
else if (qNum > (Sqr(dx) + Sqr(dy))) return DistanceSqr(pt, segPt2);
else
return Sqr(ax * dy - dx * ay) / (dx * dx + dy * dy);
}
static IntersectKind SegsIntersect(const Point64 s1a, const Point64 s1b,
const Point64 s2a, const Point64 s2b)
{
if (s1a == s2a || s1b == s2a || s1b == s2b) return IntersectKind::none;
double dy1 = static_cast<double>(s1b.y - s1a.y);
double dx1 = static_cast<double>(s1b.x - s1a.x);
double dy2 = static_cast<double>(s2b.y - s2a.y);
double dx2 = static_cast<double>(s2b.x - s2a.x);
double cp = dy1 * dx2 - dy2 * dx1;
if (cp == 0) return IntersectKind::collinear;
double t = (static_cast<double>(s1a.x - s2a.x) * dy2 -
static_cast<double>(s1a.y - s2a.y) * dx2);
if (t >= 0)
{
if (cp < 0 || t >= cp) return IntersectKind::none;
}
else
{
if (cp > 0 || t <= cp) return IntersectKind::none;
}
t = ((s1a.x - s2a.x) * dy1 - (s1a.y - s2a.y) * dx1);
if (t >= 0)
{
if (cp > 0 && t < cp) return IntersectKind::intersect;
}
else
{
if (cp < 0 && t > cp) return IntersectKind::intersect;
}
return IntersectKind::none;
}
void Delaunay::CleanUp()
{
for (auto v : allVertices) delete v;
allVertices.resize(0);
for (auto e : allEdges) delete e;
allEdges.resize(0);
for (auto t : allTriangles) delete t;
allTriangles.resize(0);
firstActive = nullptr;
lowermostVertex = nullptr;
}
void Delaunay::ForceLegal(Edge* edge)
{
if (!edge->triA || !edge->triB) return;
Vertex2* vertA = nullptr;
Vertex2* vertB = nullptr;
Edge* edgesA[3] = { nullptr, nullptr, nullptr };
Edge* edgesB[3] = { nullptr, nullptr, nullptr };
for (int i = 0; i < 3; ++i)
{
if (edge->triA->edges[i] == edge) continue;
switch (EdgeContains(edge->triA->edges[i], edge->vL))
{
case EdgeContainsResult::left:
edgesA[1] = edge->triA->edges[i];
vertA = edge->triA->edges[i]->vR;
break;
case EdgeContainsResult::right:
edgesA[1] = edge->triA->edges[i];
vertA = edge->triA->edges[i]->vL;
break;
default:
edgesB[1] = edge->triA->edges[i];
}
}
for (int i = 0; i < 3; ++i)
{
if (edge->triB->edges[i] == edge) continue;
switch (EdgeContains(edge->triB->edges[i], edge->vL))
{
case EdgeContainsResult::left:
edgesA[2] = edge->triB->edges[i];
vertB = edge->triB->edges[i]->vR;
break;
case EdgeContainsResult::right:
edgesA[2] = edge->triB->edges[i];
vertB = edge->triB->edges[i]->vL;
break;
default:
edgesB[2] = edge->triB->edges[i];
}
}
if (CrossProductSign(vertA->pt, edge->vL->pt, edge->vR->pt) == 0) return;
double ictResult = InCircleTest(vertA->pt, edge->vL->pt, edge->vR->pt, vertB->pt);
if (ictResult == 0 || (RightTurning(vertA->pt, edge->vL->pt, edge->vR->pt) == (ictResult < 0))) return;
edge->vL = vertA;
edge->vR = vertB;
edge->triA->edges[0] = edge;
for (int i = 1; i < 3; ++i)
{
edge->triA->edges[i] = edgesA[i];
if (!edgesA[i]) DoError(undefined_error_i);
if (IsLooseEdge(*edgesA[i]))
pendingDelaunayStack.push(edgesA[i]);
if (edgesA[i]->triA == edge->triA || edgesA[i]->triB == edge->triA) continue;
if (edgesA[i]->triA == edge->triB)
edgesA[i]->triA = edge->triA;
else if (edgesA[i]->triB == edge->triB)
edgesA[i]->triB = edge->triA;
else DoError(undefined_error_i);
}
edge->triB->edges[0] = edge;
for (int i = 1; i < 3; ++i)
{
edge->triB->edges[i] = edgesB[i];
if (!edgesB[i]) DoError(undefined_error_i);
if (IsLooseEdge(*edgesB[i]))
pendingDelaunayStack.push(edgesB[i]);
if (edgesB[i]->triA == edge->triB || edgesB[i]->triB == edge->triB) continue;
if (edgesB[i]->triA == edge->triA)
edgesB[i]->triA = edge->triB;
else if (edgesB[i]->triB == edge->triA)
edgesB[i]->triB = edge->triB;
else DoError(undefined_error_i);
}
}
Edge* Delaunay::CreateEdge(Vertex2* v1, Vertex2* v2, EdgeKind k)
{
Edge* res = allEdges.emplace_back(new Edge());
if (v1->pt.y == v2->pt.y)
{
res->vB = v1; res->vT = v2;
}
else if (v1->pt.y < v2->pt.y)
{
res->vB = v2; res->vT = v1;
}
else
{
res->vB = v1; res->vT = v2;
}
if (v1->pt.x <= v2->pt.x)
{
res->vL = v1; res->vR = v2;
}
else
{
res->vL = v2; res->vR = v1;
}
res->kind = k;
v1->edges.push_back(res);
v2->edges.push_back(res);
if (k == EdgeKind::loose)
{
pendingDelaunayStack.push(res);
AddEdgeToActives(res);
}
return res;
}
Triangle* Delaunay::CreateTriangle(Edge* e1, Edge* e2, Edge* e3)
{
Triangle* res = allTriangles.emplace_back(new Triangle(e1, e2, e3));
for (int i = 0; i < 3; ++i)
if (res->edges[i]->triA)
{
res->edges[i]->triB = res;
RemoveEdgeFromActives(res->edges[i]);
}
else
{
res->edges[i]->triA = res;
if (!IsLooseEdge(*res->edges[i]))
RemoveEdgeFromActives(res->edges[i]);
}
return res;
}
bool Delaunay::RemoveIntersection(Edge* e1, Edge* e2)
{
Vertex2* v = e1->vL;
Edge* tmpE = e2;
double d = ShortestDistFromSegment(e1->vL->pt, e2->vL->pt, e2->vR->pt);
double d2 = ShortestDistFromSegment(e1->vR->pt, e2->vL->pt, e2->vR->pt);
if (d2 < d) { d = d2; v = e1->vR; }
d2 = ShortestDistFromSegment(e2->vL->pt, e1->vL->pt, e1->vR->pt);
if (d2 < d) { d = d2; tmpE = e1; v = e2->vL; }
d2 = ShortestDistFromSegment(e2->vR->pt, e1->vL->pt, e1->vR->pt);
if (d2 < d) { d = d2; tmpE = e1; v = e2->vR; }
if (d > 1.000)
return false;
Vertex2* v2 = tmpE->vT;
RemoveEdgeFromVertex(v2, tmpE);
if (tmpE->vL == v2) tmpE->vL = v;
else tmpE->vR = v;
tmpE->vT = v;
v->edges.push_back(tmpE);
v->innerLM = false; if (tmpE->vB->innerLM && GetLocMinAngle(tmpE->vB) <= 0)
tmpE->vB->innerLM = false; CreateEdge(v, v2, tmpE->kind);
return true;
}
bool Delaunay::FixupEdgeIntersects()
{
for (size_t i1 = 0; i1 < allEdges.size(); ++i1)
{
Edge* e1 = allEdges[i1];
for (size_t i2 = i1 + 1; i2 < allEdges.size(); ++i2)
{
Edge* e2 = allEdges[i2];
if (e2->vL->pt.x >= e1->vR->pt.x)
break;
if (e2->vT->pt.y < e1->vB->pt.y && e2->vB->pt.y > e1->vT->pt.y &&
(SegsIntersect(e2->vL->pt, e2->vR->pt,
e1->vL->pt, e1->vR->pt) == IntersectKind::intersect))
{
if (!RemoveIntersection(e2, e1))
return false; }
}
}
return true;
}
void Delaunay::SplitEdge(Edge* longE, Edge* shortE)
{
auto oldT = longE->vT, newT = shortE->vT;
RemoveEdgeFromVertex(oldT, longE);
longE->vT = newT;
if (longE->vL == oldT) longE->vL = newT;
else longE->vR = newT;
newT->edges.push_back(longE);
CreateEdge(newT, oldT, longE->kind);
}
void Delaunay::MergeDupOrCollinearVertices()
{
auto vIter1 = allVertices.begin();
for (auto vIter2 = allVertices.begin() + 1; vIter2 != allVertices.end(); ++vIter2)
{
if ((*vIter1)->pt != (*vIter2)->pt)
{
vIter1 = vIter2;
continue;
}
Vertex2* v1 = *vIter1, * v2 = *vIter2;
if (!v1->innerLM || !v2->innerLM)
v1->innerLM = false;
for (auto e : v2->edges)
{
if (e->vB == v2) e->vB = v1;
else e->vT = v1;
if (e->vL == v2) e->vL = v1;
else e->vR = v1;
}
std::copy(v2->edges.begin(), v2->edges.end(), back_inserter(v1->edges));
v2->edges.resize(0);
for (auto itE = v1->edges.begin(); itE != v1->edges.end(); ++itE)
{
if (IsHorizontal(*(*itE)) || (*itE)->vB != v1) continue;
for (auto itE2 = itE + 1; itE2 != v1->edges.end(); ++itE2)
{
auto e1 = *itE, e2 = *itE2;
if (e2->vB != v1 || e1->vT->pt.y == e2->vT->pt.y ||
(CrossProductSign(e1->vT->pt, v1->pt, e2->vT->pt) != 0)) continue;
if (e1->vT->pt.y < e2->vT->pt.y) SplitEdge(e1, e2);
else SplitEdge(e2, e1);
break; }
}
}
}
Edge* Delaunay::CreateInnerLocMinLooseEdge(Vertex2* vAbove)
{
if (!firstActive) return nullptr;
int64_t xAbove = vAbove->pt.x;
int64_t yAbove = vAbove->pt.y;
Edge* e = firstActive, *eBelow = nullptr;
double bestD = -1.0;
while (e)
{
if (e->vL->pt.x <= xAbove && e->vR->pt.x >= xAbove &&
e->vB->pt.y >= yAbove && e->vB != vAbove && e->vT != vAbove &&
!LeftTurning(e->vL->pt, vAbove->pt, e->vR->pt))
{
double d = ShortestDistFromSegment(vAbove->pt, e->vL->pt, e->vR->pt);
if (!eBelow || d < bestD) {
eBelow = e;
bestD = d;
}
}
e = e->nextE;
}
if (!eBelow) return nullptr;
Vertex2* vBest = (eBelow->vT->pt.y <= yAbove) ? eBelow->vB : eBelow->vT;
int64_t xBest = vBest->pt.x;
int64_t yBest = vBest->pt.y;
e = firstActive;
if (xBest < xAbove)
{
while (e)
{
if (e->vR->pt.x > xBest && e->vL->pt.x < xAbove &&
e->vB->pt.y > yAbove && e->vT->pt.y < yBest &&
(SegsIntersect(e->vB->pt, e->vT->pt,
vBest->pt, vAbove->pt) == IntersectKind::intersect))
{
vBest = (e->vT->pt.y > yAbove) ? e->vT : e->vB;
xBest = vBest->pt.x;
yBest = vBest->pt.y;
}
e = e->nextE;
}
}
else
{
while (e)
{
if (e->vR->pt.x < xBest && e->vL->pt.x > xAbove &&
e->vB->pt.y > yAbove && e->vT->pt.y < yBest &&
(SegsIntersect(e->vB->pt, e->vT->pt,
vBest->pt, vAbove->pt) == IntersectKind::intersect))
{
vBest = e->vT->pt.y > yAbove ? e->vT : e->vB;
xBest = vBest->pt.x;
yBest = vBest->pt.y;
}
e = e->nextE;
}
}
return CreateEdge(vBest, vAbove, EdgeKind::loose);
}
Edge* Delaunay::HorizontalBetween(Vertex2* v1, Vertex2* v2)
{
int64_t y = v1->pt.y, l, r;
if (v1->pt.x > v2->pt.x)
{
l = v2->pt.x;
r = v1->pt.x;
}
else
{
l = v1->pt.x;
r = v2->pt.x;
}
Edge* res = firstActive;
while (res)
{
if (res->vL->pt.y == y && res->vR->pt.y == y &&
res->vL->pt.x >= l && res->vR->pt.x <= r &&
(res->vL->pt.x != l || res->vL->pt.x != r)) break;
res = res->nextE;
}
return res;
}
void Delaunay::DoTriangulateLeft(Edge* edge, Vertex2* pivot, int64_t minY)
{
Vertex2* vAlt = nullptr;
Edge* eAlt = nullptr;
Vertex2* v = (edge->vB == pivot) ? edge->vT : edge->vB;
for (auto e : pivot->edges)
{
if (e == edge || !e->isActive) continue;
Vertex2* vX = e->vT == pivot ? e->vB : e->vT;
if (vX == v) continue;
int cps = CrossProductSign(v->pt, pivot->pt, vX->pt);
if (cps == 0) {
if ((v->pt.x > pivot->pt.x) == (pivot->pt.x > vX->pt.x)) continue;
}
else if (cps > 0 || (vAlt && !LeftTurning(vX->pt, pivot->pt, vAlt->pt)))
continue;
vAlt = vX;
eAlt = e;
}
if (!vAlt || vAlt->pt.y < minY) return;
if (vAlt->pt.y < pivot->pt.y)
{
if (IsLeftEdge(*eAlt)) return;
}
else if (vAlt->pt.y > pivot->pt.y)
{
if (IsRightEdge(*eAlt)) return;
}
Edge* eX = FindLinkingEdge(vAlt, v, (vAlt->pt.y < v->pt.y));
if (!eX)
{
if (vAlt->pt.y == v->pt.y && v->pt.y == minY &&
HorizontalBetween(vAlt, v)) return;
eX = CreateEdge(vAlt, v, EdgeKind::loose);
}
CreateTriangle(edge, eAlt, eX);
if (!EdgeCompleted(eX))
DoTriangulateLeft(eX, vAlt, minY);
}
void Delaunay::DoTriangulateRight(Edge* edge, Vertex2* pivot, int64_t minY)
{
Vertex2* vAlt = nullptr;
Edge* eAlt = nullptr;
Vertex2* v = (edge->vB == pivot) ? edge->vT : edge->vB;
for (auto e : pivot->edges)
{
if (e == edge || !e->isActive) continue;
Vertex2* vX = e->vT == pivot ? e->vB : e->vT;
if (vX == v) continue;
int cps = CrossProductSign(v->pt, pivot->pt, vX->pt);
if (cps == 0) {
if ((v->pt.x > pivot->pt.x) == (pivot->pt.x > vX->pt.x)) continue;
}
else if (cps < 0 || (vAlt && !RightTurning(vX->pt, pivot->pt, vAlt->pt)))
continue;
vAlt = vX;
eAlt = e;
}
if (!vAlt || vAlt->pt.y < minY) return;
if (vAlt->pt.y < pivot->pt.y)
{
if (IsRightEdge(*eAlt)) return;
}
else if (vAlt->pt.y > pivot->pt.y)
{
if (IsLeftEdge(*eAlt)) return;
}
Edge* eX = FindLinkingEdge(vAlt, v, (vAlt->pt.y > v->pt.y));
if (!eX)
{
if (vAlt->pt.y == v->pt.y && v->pt.y == minY &&
HorizontalBetween(vAlt, v)) return;
eX = CreateEdge(vAlt, v, EdgeKind::loose);
}
CreateTriangle(edge, eX, eAlt);
if (!EdgeCompleted(eX))
DoTriangulateRight(eX, vAlt, minY);
}
void Delaunay::AddEdgeToActives(Edge* edge)
{
if (edge->isActive) return;
edge->prevE = nullptr;
edge->nextE = firstActive;
edge->isActive = true;
if (firstActive)
firstActive->prevE = edge;
firstActive = edge;
}
void Delaunay::RemoveEdgeFromActives(Edge* edge)
{
RemoveEdgeFromVertex(edge->vB, edge);
RemoveEdgeFromVertex(edge->vT, edge);
Edge* prev = edge->prevE;
Edge* next = edge->nextE;
if (next) next->prevE = prev;
if (prev) prev->nextE = next;
edge->isActive = false;
if (firstActive == edge) firstActive = next;
}
Paths64 Delaunay::Execute(const Paths64& paths, TriangulateResult& triResult)
{
if (!AddPaths(paths))
{
triResult = TriangulateResult::no_polygons;
return Paths64(); }
if (lowermostVertex->innerLM)
{
Vertex2* lm;
while (!locMinStack.empty())
{
lm = locMinStack.top();
lm->innerLM = !lm->innerLM;
locMinStack.pop();
}
for (Edge* e : allEdges)
if (e->kind == EdgeKind::ascend)
e->kind = EdgeKind::descend;
else
e->kind = EdgeKind::ascend;
}
else
{
while (!locMinStack.empty())
locMinStack.pop();
}
std::sort(allEdges.begin(), allEdges.end(), EdgeListSort);
if (!FixupEdgeIntersects())
{
CleanUp();
triResult = TriangulateResult::paths_intersect;
return Paths64(); }
std::sort(allVertices.begin(), allVertices.end(), VertexListSort);
MergeDupOrCollinearVertices();
int64_t currY = allVertices[0]->pt.y;
for (auto vIt = allVertices.begin(); vIt != allVertices.end(); ++vIt)
{
Vertex2* v = *vIt;
if (v->edges.empty()) continue;
if (v->pt.y != currY)
{
while (!locMinStack.empty())
{
Vertex2* lm = locMinStack.top();
locMinStack.pop();
Edge* e = CreateInnerLocMinLooseEdge(lm);
if (!e)
{
CleanUp();
triResult = TriangulateResult::fail;
return Paths64(); }
if (IsHorizontal(*e))
{
if (e->vL == e->vB)
DoTriangulateLeft(e, e->vB, currY); else
DoTriangulateRight(e, e->vB, currY);
}
else
{
DoTriangulateLeft(e, e->vB, currY);
if (!EdgeCompleted(e))
DoTriangulateRight(e, e->vB, currY);
}
AddEdgeToActives(lm->edges[0]);
AddEdgeToActives(lm->edges[1]);
}
while (!horzEdgeStack.empty())
{
Edge* e = horzEdgeStack.top();
horzEdgeStack.pop();
if (EdgeCompleted(e)) continue;
if (e->vB == e->vL) {
if (IsLeftEdge(*e))
DoTriangulateLeft(e, e->vB, currY);
}
else
if (IsRightEdge(*e))
DoTriangulateRight(e, e->vB, currY);
}
currY = v->pt.y;
}
for (int i = static_cast<int>(v->edges.size()) - 1; i >= 0; --i)
{
if (i >= static_cast<int>(v->edges.size())) continue;
Edge* e = v->edges[i];
if (EdgeCompleted(e) || IsLooseEdge(*e)) continue;
if (v == e->vB)
{
if (IsHorizontal(*e))
horzEdgeStack.push(e);
if (!v->innerLM)
AddEdgeToActives(e);
}
else
{
if (IsHorizontal(*e))
horzEdgeStack.push(e);
else if (IsLeftEdge(*e))
DoTriangulateLeft(e, e->vB, v->pt.y);
else
DoTriangulateRight(e, e->vB, v->pt.y);
}
}
if (v->innerLM) locMinStack.push(v);
}
while (!horzEdgeStack.empty())
{
Edge* e = horzEdgeStack.top();
horzEdgeStack.pop();
if (!EdgeCompleted(e) && e->vB == e->vL)
DoTriangulateLeft(e, e->vB, currY);
}
if (useDelaunay)
{
while (!pendingDelaunayStack.empty())
{
Edge* e = pendingDelaunayStack.top();
pendingDelaunayStack.pop();
ForceLegal(e);
}
}
Paths64 res;
res.reserve(allTriangles.size());
for (auto tri : allTriangles)
{
Path64 p = PathFromTriangle(*tri);
int cps = CrossProductSign(p[0], p[1], p[2]);
if (cps == 0) continue; if (cps < 0) std::reverse(p.begin(), p.end());
res.push_back(p);
}
CleanUp();
triResult = TriangulateResult::success;
return res;
}
static double DistSqr(const Point64& pt1, const Point64& pt2)
{
return Sqr(pt1.x - pt2.x) + Sqr(pt1.y - pt2.y);
}
void Delaunay::AddPath(const Path64& path)
{
size_t len = path.size(), i0 = 0, iPrev, iNext;
if (!FindLocMinIdx(path, len, i0)) return;
iPrev = Prev(i0, len);
while (path[iPrev] == path[i0]) iPrev = Prev(iPrev, len);
iNext = Next(i0, len);
size_t i = i0;
while (CrossProductSign(path[iPrev], path[i], path[iNext]) == 0)
{
FindLocMinIdx(path, len, i);
if (i == i0) return; iPrev = Prev(i, len);
while (path[iPrev] == path[i]) iPrev = Prev(iPrev, len);
iNext = Next(i, len);
}
size_t vert_cnt = allVertices.size();
Vertex2* v0 = allVertices.emplace_back(new Vertex2(path[i]));
if (LeftTurning(path[iPrev], path[i], path[iNext]))
v0->innerLM = true;
Vertex2* vPrev = v0;
i = iNext;
for (;;)
{
locMinStack.push(vPrev);
if (!lowermostVertex ||
vPrev->pt.y > lowermostVertex->pt.y ||
(vPrev->pt.y == lowermostVertex->pt.y &&
vPrev->pt.x < lowermostVertex->pt.x))
lowermostVertex = vPrev;
iNext = Next(i, len);
if (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
{
i = iNext;
continue;
}
while (path[i].y <= vPrev->pt.y)
{
Vertex2* v = allVertices.emplace_back(new Vertex2(path[i]));
CreateEdge(vPrev, v, EdgeKind::ascend);
vPrev = v;
i = iNext;
iNext = Next(i, len);
while (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
{
i = iNext;
iNext = Next(i, len);
}
}
Vertex2* vPrevPrev = vPrev;
while (i != i0 && path[i].y >= vPrev->pt.y)
{
Vertex2* v = allVertices.emplace_back(new Vertex2(path[i]));
CreateEdge(v, vPrev, EdgeKind::descend);
vPrevPrev = vPrev;
vPrev = v;
i = iNext;
iNext = Next(i, len);
while (CrossProductSign(vPrev->pt, path[i], path[iNext]) == 0)
{
i = iNext;
iNext = Next(i, len);
}
}
if (i == i0) break; if (LeftTurning(vPrevPrev->pt, vPrev->pt, path[i]))
vPrev->innerLM = true;
}
CreateEdge(v0, vPrev, EdgeKind::descend);
len = allVertices.size() - vert_cnt;
i = vert_cnt;
if (len < 3 || (len == 3 && ((DistSqr(allVertices[i]->pt, allVertices[i + 1]->pt) <= 1) ||
(DistSqr(allVertices[i + 1]->pt, allVertices[i + 2]->pt) <= 1) ||
(DistSqr(allVertices[i + 2]->pt, allVertices[i]->pt) <= 1))))
{
for (size_t j = vert_cnt; j < allVertices.size(); ++j)
allVertices[j]->edges.clear(); }
}
bool Delaunay::AddPaths(const Paths64& paths)
{
const auto total_vertex_count =
std::accumulate(paths.begin(), paths.end(), size_t(0),
[](const auto& a, const Path64& path)
{return a + path.size(); });
if (total_vertex_count == 0) return false;
allVertices.reserve(allVertices.capacity() + total_vertex_count);
allEdges.reserve(allEdges.capacity() + total_vertex_count);
for (const Path64& path : paths)
AddPath(path);
return (allVertices.size() > 2);
}
TriangulateResult Triangulate(const Paths64& pp, Paths64& solution, bool useDelaunay)
{
TriangulateResult result;
Delaunay d(useDelaunay);
solution = d.Execute(pp, result);
return result;
}
TriangulateResult Triangulate(const PathsD& pp, int decPlaces, PathsD& solution, bool useDelaunay)
{
int ec = 0;
double scale;
TriangulateResult result;
if (decPlaces <= 0) scale = 1;
else if (decPlaces > 8) scale = std::pow(10, 8);
else scale = std::pow(10, decPlaces);
Paths64 pp64 = ScalePaths<int64_t, double>(pp, scale, ec);
Delaunay d(useDelaunay);
Paths64 sol64 = d.Execute(pp64, result);
solution = ScalePaths<double, int64_t>(sol64, 1 / scale, ec);
return result;
}
}