Compare commits

...
Author SHA1 Message Date
Emmett Lalish 25cc2e496f 11 polygon tests failing 2023-09-13 08:36:17 -07:00
Emmett Lalish c1053cf309 more fixes 2023-09-04 15:43:35 -07:00
Emmett Lalish 945fee8308 small fixes 2023-09-03 23:09:19 -07:00
Emmett Lalish 4bdd53e417 readded shifting 2023-09-03 22:20:20 -07:00
Emmett Lalish 1b1cb5fda0 starting shift 2023-08-31 23:37:26 -07:00
Emmett Lalish 4f390c290b 47 tests passing 2023-08-31 22:31:19 -07:00
Emmett Lalish aa8611477f fixed more tests 2023-08-30 23:31:46 -07:00
Emmett Lalish 93d9b64f7c fixed MultiMerge 2023-08-30 22:14:23 -07:00
Emmett Lalish ebb96769ba another refactor, more tests passing 2023-08-30 21:39:05 -07:00
Emmett Lalish 9905a7585b rename 2023-08-30 10:09:22 -07:00
Emmett Lalish c09087bbd8 SimpleHole passes 2023-08-29 16:58:40 -07:00
Emmett Lalish 90b49380d5 fwd-bwd 2023-08-29 15:08:28 -07:00
Emmett Lalish 476c51a337 compiles 2023-08-29 14:13:06 -07:00
Emmett Lalish ee61127d72 started refactor 2023-08-15 17:09:33 -07:00
3 changed files with 336 additions and 394 deletions
+1 -1
View File
@@ -120,7 +120,7 @@
"-DMANIFOLD_USE_CUDA=OFF",
"-DMANIFOLD_PAR=TBB",
"-DCODE_COVERAGE=OFF",
"-DCMAKE_CXX_FLAGS=''" //'-fsanitize=address,undefined'"
"-DCMAKE_CXX_FLAGS='-fsanitize=address,undefined'" //''" //
],
"search.exclude": {
"**/third_party/*": true,
+330 -388
View File
@@ -208,7 +208,7 @@ class Monotones {
monotones_.push_back({poly[i].pos, //
poly[i].idx, //
0, monotones_.end(), monotones_.end(),
activePairs_.end(), activePairs_.end()});
activeEdges_.end()});
bound = glm::max(
bound, glm::max(glm::abs(poly[i].pos.x), glm::abs(poly[i].pos.y)));
@@ -310,24 +310,24 @@ class Monotones {
private:
struct VertAdj;
struct EdgePair;
enum VertType { Start, WestSide, EastSide, Merge, End, Skip };
struct Edge;
enum VertType { Start, Backward, Forward, Merge, End, Skip };
#if __has_include(<memory_resource>)
typedef std::pmr::list<VertAdj>::iterator VertItr;
typedef std::pmr::list<EdgePair>::iterator PairItr;
typedef std::pmr::list<Edge>::iterator EdgeItr;
std::pmr::monotonic_buffer_resource mbr;
std::pmr::polymorphic_allocator<int> pa{&mbr};
std::pmr::list<VertAdj> monotones_{pa}; // sweep-line list of verts
std::pmr::list<EdgePair> activePairs_{pa}; // west to east monotone edges
std::pmr::list<EdgePair> inactivePairs_{pa}; // completed monotones
std::pmr::list<VertAdj> monotones_{pa}; // sweep-line list of verts
std::pmr::list<Edge> activeEdges_{pa}; // west to east monotone edges
std::pmr::list<Edge> inactiveEdges_{pa}; // completed monotones
#else
typedef std::list<VertAdj>::iterator VertItr;
typedef std::list<EdgePair>::iterator PairItr;
typedef std::list<Edge>::iterator EdgeItr;
std::list<VertAdj> monotones_; // sweep-line list of verts
std::list<EdgePair> activePairs_; // west to east monotone edges
std::list<EdgePair> inactivePairs_; // completed monotones
std::list<VertAdj> monotones_; // sweep-line list of verts
std::list<Edge> activeEdges_; // west to east monotone edges
std::list<Edge> inactiveEdges_; // completed monotones
#endif
float precision_; // a triangle of this height or less is degenerate
@@ -343,7 +343,7 @@ class Monotones {
int mesh_idx; // This is a global index into the manifold.
int index;
VertItr left, right;
PairItr eastPair, westPair;
EdgeItr edgeL, edgeR;
bool Processed() const { return index < 0; }
void SetSkip() { index = -2; }
@@ -378,40 +378,16 @@ class Monotones {
* certain.
*/
struct EdgePair {
VertItr vWest, vEast, vMerge;
PairItr nextPair;
bool westCertain, eastCertain;
struct Edge {
VertItr south; //, chain;
EdgeItr linked, next; //, bottomEast;
bool forward, linked2east, flipped, eastCertain;
float minDegenerateY = 0;
int WestOf(VertItr vert, float precision) const {
if (vEast->pos.x + precision < vert->pos.x &&
vEast->right->pos.x + precision < vert->pos.x)
return 1;
if (vEast->pos.x - precision > vert->pos.x &&
vEast->right->pos.x - precision > vert->pos.x)
return -1;
int westOf = CCW(vEast->right->pos, vEast->pos, vert->pos, precision);
if (westOf == 0 && !vert->right->Processed())
westOf =
CCW(vEast->right->pos, vEast->pos, vert->right->pos, precision);
if (westOf == 0 && !vert->left->Processed())
westOf = CCW(vEast->right->pos, vEast->pos, vert->left->pos, precision);
return westOf;
}
VertItr North() const { return forward ? south->right : south->left; }
int EastOf(VertItr vert, float precision) const {
if (vWest->pos.x - precision > vert->pos.x &&
vWest->left->pos.x - precision > vert->pos.x)
return 1;
if (vWest->pos.x + precision < vert->pos.x &&
vWest->left->pos.x + precision < vert->pos.x)
return -1;
int eastOf = CCW(vWest->pos, vWest->left->pos, vert->pos, precision);
if (eastOf == 0 && !vert->right->Processed())
eastOf = CCW(vWest->pos, vWest->left->pos, vert->right->pos, precision);
if (eastOf == 0 && !vert->left->Processed())
eastOf = CCW(vWest->pos, vWest->left->pos, vert->left->pos, precision);
return eastOf;
return CCW(south->pos, North()->pos, vert->pos, precision);
}
};
@@ -500,38 +476,26 @@ class Monotones {
right->left = left;
}
void SetVWest(PairItr pair, VertItr vert) {
pair->vWest = vert;
vert->eastPair = pair;
void UpdateEdge(EdgeItr edge, VertItr vert) {
edge->south = vert;
vert->edgeL = edge;
vert->edgeR = edge;
}
void SetVEast(PairItr pair, VertItr vert) {
pair->vEast = vert;
vert->westPair = pair;
}
void SetEastCertainty(PairItr westPair, bool certain) {
westPair->eastCertain = certain;
std::next(westPair)->westCertain = certain;
}
PairItr GetPair(VertItr vert, VertType type) const {
// Merge returns westPair, as this is the one that will be removed.
return type == WestSide ? vert->eastPair : vert->westPair;
}
bool Coincident(glm::vec2 p0, glm::vec2 p1) const {
glm::vec2 sep = p0 - p1;
return glm::dot(sep, sep) < precision_ * precision_;
void LinkEdges(EdgeItr edge1, EdgeItr edge2) {
edge1->linked = edge2;
edge2->linked = edge1;
}
void CloseEnd(VertItr vert) {
PairItr eastPair = vert->right->eastPair;
PairItr westPair = vert->left->westPair;
SetVWest(eastPair, vert);
SetVEast(westPair, vert);
westPair->westCertain = true;
westPair->eastCertain = true;
EdgeItr edgeR = vert->right->edgeL;
EdgeItr edgeL = vert->left->edgeR;
edgeR->south = vert;
edgeL->south = vert;
vert->edgeR = edgeR;
vert->edgeL = edgeL;
edgeL->eastCertain = true;
edgeR->eastCertain = true;
}
/**
@@ -539,56 +503,67 @@ class Monotones {
* determines the topology of the vertex relative to the sweep line.
*/
VertType ProcessVert(VertItr vert) {
PairItr eastPair = vert->right->eastPair;
PairItr westPair = vert->left->westPair;
if (vert->right->Processed()) {
if (vert->left->Processed()) {
if (westPair == eastPair) {
// facing in
PRINT("End");
CloseEnd(vert);
return End;
} else if (!eastPair->westCertain || !westPair->eastCertain ||
(westPair != activePairs_.end() &&
std::next(westPair) == eastPair)) {
if (!eastPair->westCertain)
activePairs_.splice(std::next(westPair), activePairs_, eastPair);
if (!westPair->eastCertain)
activePairs_.splice(eastPair, activePairs_, westPair);
// facing out
PRINT("Merge");
CloseEnd(vert);
// westPair will be removed and eastPair takes over.
SetVWest(eastPair, westPair->vWest);
return Merge;
} else { // not neighbors
const EdgeItr edgeR = vert->right->edgeL;
const EdgeItr edgeL = vert->left->edgeR;
// if (CCW(vert->pos, vert->right->pos, vert->left->pos, precision_) ==
// 0) {
// if (edgeL->linked2east && !edgeR->linked2east) {
// activeEdges_.splice(edgeL, activeEdges_, edgeR->linked,
// std::next(edgeR));
// } else if (!edgeL->linked2east && edgeR->linked2east) {
// activeEdges_.splice(edgeR, activeEdges_, edgeL->linked,
// std::next(edgeL));
// }
// }
if (std::next(edgeR) != edgeL && std::next(edgeL) != edgeR) {
PRINT("Skip");
return Skip;
}
edgeR->south = vert;
edgeL->south = vert;
vert->edgeR = edgeR;
vert->edgeL = edgeL;
LinkEdges(edgeL->linked, edgeR->linked);
if (std::next(edgeR) == edgeL) { // facing in
PRINT("End");
return End;
} else { // facing out
PRINT("Merge");
return Merge;
}
} else {
const EdgeItr bwdEdge = vert->right->edgeL;
const EdgeItr fwdEdge = std::next(bwdEdge);
if (!vert->IsPast(vert->right, precision_) &&
!eastPair->vEast->right->IsPast(vert, precision_) &&
vert->IsPast(eastPair->vEast, precision_) &&
vert->pos.x > eastPair->vEast->right->pos.x + precision_) {
PRINT("Skip WEST");
!fwdEdge->south->right->IsPast(vert, precision_) &&
vert->IsPast(fwdEdge->south, precision_) &&
vert->pos.x > fwdEdge->south->right->pos.x + precision_) {
PRINT("Skip backward edge");
return Skip;
}
SetVWest(eastPair, vert);
PRINT("WestSide");
return WestSide;
UpdateEdge(bwdEdge, vert);
PRINT("Backward");
return Backward;
}
} else {
if (vert->left->Processed()) {
const EdgeItr fwdEdge = vert->left->edgeR;
const EdgeItr bwdEdge = std::prev(fwdEdge);
if (!vert->IsPast(vert->left, precision_) &&
!westPair->vWest->left->IsPast(vert, precision_) &&
vert->IsPast(westPair->vWest, precision_) &&
vert->pos.x < westPair->vWest->left->pos.x - precision_) {
PRINT("Skip EAST");
!bwdEdge->south->left->IsPast(vert, precision_) &&
vert->IsPast(bwdEdge->south, precision_) &&
vert->pos.x < bwdEdge->south->left->pos.x - precision_) {
PRINT("Skip forward edge");
return Skip;
}
SetVEast(westPair, vert);
PRINT("EastSide");
return EastSide;
UpdateEdge(fwdEdge, vert);
PRINT("Forward");
return Forward;
} else {
PRINT("Start");
return Start;
@@ -597,221 +572,180 @@ class Monotones {
}
/**
* Remove this pair, but save it and mark the pair it was next to. When the
* reverse sweep happens, it will be placed next to its last neighbor instead
* of using geometry.
* Remove this edge and its pair to the east, but save them and mark the edge
* they were next to. When the reverse sweep happens, it will be placed next
* to its last neighbor instead of using geometry.
*/
void RemovePair(PairItr pair) {
pair->nextPair = std::next(pair);
inactivePairs_.splice(inactivePairs_.end(), activePairs_, pair);
void RemovePair(EdgeItr westEdge) {
EdgeItr eastEdge = std::next(westEdge);
EdgeItr nextEast = std::next(eastEdge);
westEdge->next = eastEdge->next = nextEast;
inactiveEdges_.splice(inactiveEdges_.end(), activeEdges_, westEdge,
nextEast);
}
/**
* When vert is a Start, this determines if it is backwards (forming a void or
* hole). Usually the first return is adequate, but if it is degenerate, the
* function will continue to search up the neighbors until the degeneracy is
* broken and a certain answer is returned. Like CCW, this function returns 1
* for a hole, -1 for a start, and 0 only if the entire polygon degenerates to
* a line.
*/
int IsHole(VertItr vert) const {
VertItr left = vert->left;
VertItr right = vert->right;
VertItr center = vert;
// TODO: if left or right is Processed(), determine from east/west
while (left != right) {
if (Coincident(left->pos, center->pos)) {
left = left->left;
continue;
}
if (Coincident(right->pos, center->pos)) {
right = right->right;
continue;
}
if (Coincident(left->pos, right->pos)) {
vert = center;
center = left;
left = left->left;
if (left == right) break;
right = right->right;
continue;
}
int isHole = CCW(right->pos, center->pos, left->pos, precision_);
if (center != vert) {
isHole += CCW(left->pos, center->pos, vert->pos, precision_) +
CCW(vert->pos, center->pos, right->pos, precision_);
}
if (isHole != 0) return isHole;
VertType PlaceStart(VertItr vert) {
EdgeItr eastEdge = activeEdges_.begin();
while (eastEdge != activeEdges_.end() && eastEdge->EastOf(vert, 0) <= 0) {
++eastEdge;
}
glm::vec2 edgeLeft = left->pos - center->pos;
glm::vec2 edgeRight = right->pos - center->pos;
if (glm::dot(edgeLeft, edgeRight) > 0) {
if (glm::dot(edgeLeft, edgeLeft) < glm::dot(edgeRight, edgeRight)) {
center = left;
left = left->left;
bool isHole = CCW(vert->left->pos, vert->pos, vert->right->pos, 0) < 0;
const bool holeCertain =
CCW(vert->left->pos, vert->pos, vert->right->pos, precision_) != 0;
const bool shouldBeStart =
eastEdge == activeEdges_.end() || !eastEdge->forward;
if (isHole == shouldBeStart) { // invalid
if (!holeCertain) {
isHole = !isHole;
} else { // shift to a valid position
if (eastEdge != activeEdges_.end() &&
eastEdge->EastOf(vert, precision_) <= 0) {
++eastEdge;
} else if (eastEdge != activeEdges_.begin() &&
std::prev(eastEdge)->EastOf(vert, precision_) >= 0) {
--eastEdge;
} else {
center = right;
right = right->right;
}
} else {
if (left->pos.y < right->pos.y) {
left = left->left;
} else {
right = right->right;
return Skip;
}
}
}
return 0;
}
/**
* If the simple polygon connected to the input vert degenerates to a single
* line (more strict than IsHole==0), then any triangulation is admissible,
* since every possible triangle will be degenerate.
*/
bool IsColinearPoly(const VertItr start) const {
VertItr vert = start;
VertItr left = start;
VertItr right = left->right;
// Find the longest edge to improve error
float length2 = 0;
while (right != start) {
glm::vec2 edge = left->pos - right->pos;
const float l2 = glm::dot(edge, edge);
if (l2 > length2) {
length2 = l2;
vert = left;
}
left = right;
right = right->right;
}
right = vert->right;
left = vert->left;
while (left != vert) {
if (CCW(left->pos, vert->pos, right->pos, precision_) != 0) return false;
left = left->left;
}
return true;
}
/**
* Causes the verts of the simple polygon attached to the input vert to be
* skipped during the forward and backward sweeps, causing this polygon to be
* triangulated as though it is monotone.
*/
void SkipPoly(VertItr vert) {
vert->SetSkip();
VertItr right = vert->right;
while (right != vert) {
right->SetSkip();
right = right->right;
}
}
/**
* A backwards pair (hole) must be interior to a forwards pair for geometric
* validity. In this situation, this function is used to swap their east edges
* such that they become forward neighbor pairs. The outside becomes westPair
* and inside becomes eastPair.
*/
void SwapHole(PairItr outside, PairItr inside) {
VertItr tmp = outside->vEast;
SetVEast(outside, inside->vEast);
SetVEast(inside, tmp);
inside->eastCertain = outside->eastCertain;
activePairs_.splice(std::next(outside), activePairs_, inside);
SetEastCertainty(outside, true);
const EdgeItr noEdge = activeEdges_.end();
// if (eastEdge != activeEdges_.begin()) {
// const EdgeItr westEdge = std::prev(eastEdge);
// westEdge->eastCertain = false;
// }
const EdgeItr newEastEdge = activeEdges_.insert(
eastEdge, {vert, noEdge, noEdge, !isHole, false, false,
eastEdge == activeEdges_.end() ||
eastEdge->EastOf(vert, precision_) > 0});
const EdgeItr newWestEdge = activeEdges_.insert(
newEastEdge, {vert, noEdge, noEdge, isHole, true, false, holeCertain});
vert->edgeR = isHole ? newWestEdge : newEastEdge;
vert->edgeL = isHole ? newEastEdge : newWestEdge;
LinkEdges(newEastEdge, newWestEdge);
return Start;
}
/**
* This is the key function for handling east-west degeneracies, and is the
* purpose of running the sweep-line forwards and backwards. If the ordering
* of inputPair is uncertain, this function uses the edge ahead of vert to
* of inputEdge is uncertain, this function uses the edge ahead of vert to
* check if this new bit of geometric information is enough to place the pair
* with certainty. It can also invert the pair if it is determined to be a
* hole, in which case the inputPair becomes the eastPair while the pair it is
* inside of becomes the westPair.
* hole, in which case the inputEdge becomes the eastEdge while the pair it is
* inside of becomes the westEdge.
*
* This function normally returns false, but will instead return true if the
* certainties conflict, indicating this vertex is not yet geometrically valid
* and must be skipped.
*/
bool ShiftEast(const VertItr vert, const PairItr inputPair,
const bool isHole) {
if (inputPair->eastCertain) return false;
bool PlaceUncertain(const EdgeItr inputEdge) {
const VertItr vert = inputEdge->North();
EdgeItr eastEdge = std::next(inputEdge);
bool swap = false;
bool pastLinked = !inputEdge->linked2east;
PairItr potentialPair = std::next(inputPair);
while (potentialPair != activePairs_.end()) {
const int EastOf = potentialPair->EastOf(vert, precision_);
// This does not trigger a skip because ShiftWest may still succeed, and
// if not it will mark the skip.
if (EastOf > 0 && isHole) return false;
if (EastOf >= 0 && !isHole) { // in the right place
activePairs_.splice(potentialPair, activePairs_, inputPair);
SetEastCertainty(inputPair, EastOf != 0);
return false;
// Shift Eastwards
while (eastEdge != activeEdges_.end() &&
!std::prev(eastEdge)->eastCertain && eastEdge->EastOf(vert, 0) < 0) {
if (eastEdge == inputEdge->linked) {
swap = !swap;
pastLinked = true;
}
const int outside = potentialPair->WestOf(vert, precision_);
if (outside <= 0 && isHole) { // certainly a hole
SwapHole(potentialPair, inputPair);
return false;
}
++potentialPair;
++eastEdge;
swap = !swap;
}
if (isHole) return true;
if (activePairs_.size() > 1) {
activePairs_.splice(activePairs_.end(), activePairs_, inputPair);
inputPair->eastCertain =
std::prev(inputPair)->WestOf(vert, precision_) > 0;
// Shift Westwards
if (eastEdge == std::next(inputEdge)) {
pastLinked = inputEdge->linked2east;
eastEdge = inputEdge;
while (eastEdge != activeEdges_.begin()) {
--eastEdge;
if (eastEdge->eastCertain || eastEdge->EastOf(vert, 0) <= 0) {
++eastEdge;
if (eastEdge == inputEdge->linked) pastLinked = false;
break;
}
swap = !swap;
if (eastEdge == inputEdge->linked) {
swap = !swap;
pastLinked = true;
}
}
}
if (eastEdge == inputEdge ||
eastEdge == std::next(inputEdge)) { // has not moved
return false;
}
const bool linkedCertain = LinkedCertain(inputEdge);
if (linkedCertain) {
if (swap) {
return true;
} else {
activeEdges_.splice(eastEdge, activeEdges_, inputEdge);
}
} else {
inputPair->eastCertain = true;
if (!pastLinked) return true;
EdgeItr westEdge = inputEdge->linked2east ? inputEdge : inputEdge->linked;
const EdgeItr afterEdge =
std::next(inputEdge->linked2east ? inputEdge->linked : inputEdge);
if (swap) {
EdgeItr next = std::next(westEdge);
while (westEdge != afterEdge) {
westEdge->linked2east ^= true;
westEdge->flipped ^= true;
activeEdges_.splice(eastEdge, activeEdges_, westEdge);
eastEdge = westEdge;
westEdge = next++;
}
} else {
EdgeItr test = westEdge;
while (test != afterEdge) {
ASSERT(eastEdge != test++, logicErr, "WTF");
}
activeEdges_.splice(eastEdge, activeEdges_, westEdge, afterEdge);
}
}
return false;
}
/**
* Identical to the above function, but swapped to search westward instead.
*/
bool ShiftWest(const VertItr vert, const PairItr inputPair,
const bool isHole) {
if (inputPair->westCertain) return false;
PairItr potentialPair = inputPair;
while (potentialPair != activePairs_.begin()) {
--potentialPair;
const int WestOf = potentialPair->WestOf(vert, precision_);
if (WestOf > 0 && isHole) return true;
if (WestOf >= 0 && !isHole) { // in the right place
SetEastCertainty(potentialPair, WestOf != 0);
if (++potentialPair != inputPair)
activePairs_.splice(potentialPair, activePairs_, inputPair);
return false;
bool LinkedCertain(const EdgeItr edge) {
bool certain = false;
EdgeItr westEdge = edge->linked2east ? edge : edge->linked;
const EdgeItr eastEdge = edge->linked2east ? edge->linked : edge;
while (westEdge != eastEdge) {
if (westEdge->eastCertain) {
certain = true;
break;
}
ASSERT(westEdge != activeEdges_.end(), logicErr,
"Did not find linked edge!");
++westEdge;
}
return certain;
}
const int outside = potentialPair->EastOf(vert, precision_);
if (outside <= 0 && isHole) { // certainly a hole
SwapHole(potentialPair, inputPair);
return false;
void UpdateCertainty(const EdgeItr edge) {
const VertItr vert = edge->North();
if (!edge->eastCertain) {
const EdgeItr eastEdge = std::next(edge);
edge->eastCertain = eastEdge == activeEdges_.end() ||
eastEdge->EastOf(vert, precision_) > 0;
}
if (edge != activeEdges_.begin()) {
const EdgeItr westEdge = std::prev(edge);
if (!westEdge->eastCertain) {
westEdge->eastCertain = westEdge->EastOf(vert, precision_) > 0;
}
}
if (isHole) return true;
activePairs_.splice(activePairs_.begin(), activePairs_, inputPair);
const PairItr eastPair = std::next(inputPair);
if (eastPair != activePairs_.end()) {
inputPair->westCertain = eastPair->EastOf(vert, precision_) > 0;
} else {
inputPair->westCertain = true;
}
return false;
}
/**
@@ -859,40 +793,42 @@ class Monotones {
++insertAt;
}
PRINT("mesh_idx = " << vert->mesh_idx);
if (vert->Processed()) continue;
PRINT("mesh_idx = " << vert->mesh_idx);
OVERLAP_ASSERT(
skipped.empty() || !vert->IsPast(skipped.back(), precision_),
"Not Geometrically Valid! None of the skipped verts is valid.");
VertType type = ProcessVert(vert);
PairItr newPair = activePairs_.end();
bool isHole = false;
if (type == Start) {
newPair = activePairs_.insert(
activePairs_.begin(),
{vert, vert, monotones_.end(), activePairs_.end(), false, false});
SetVWest(newPair, vert);
SetVEast(newPair, vert);
const int hole = IsHole(vert);
if (hole == 0 && IsColinearPoly(vert)) {
PRINT("Skip colinear polygon");
SkipPoly(vert);
activePairs_.erase(newPair);
continue;
}
isHole = hole > 0;
type = PlaceStart(vert);
}
const PairItr pair = GetPair(vert, type);
OVERLAP_ASSERT(type == Skip || pair != activePairs_.end(),
"No active pair!");
if (type != Skip && ShiftEast(vert, pair, isHole)) type = Skip;
if (type != Skip && ShiftWest(vert, pair, isHole)) type = Skip;
if (type != Skip && !vert->right->Processed()) {
if (PlaceUncertain(vert->edgeR)) {
if (type == Start) {
activeEdges_.erase(vert->edgeL);
activeEdges_.erase(vert->edgeR);
}
type = Skip;
} else {
UpdateCertainty(vert->edgeR);
}
}
if (type != Skip && !vert->left->Processed()) {
if (PlaceUncertain(vert->edgeL)) {
if (type == Start) {
activeEdges_.erase(vert->edgeL);
activeEdges_.erase(vert->edgeR);
}
type = Skip;
} else {
UpdateCertainty(vert->edgeL);
}
}
if (type == Skip) {
OVERLAP_ASSERT(std::next(insertAt) != monotones_.end(),
@@ -902,12 +838,6 @@ class Monotones {
"Not Geometrically Valid! Tried to skip last queued vert.");
skipped.push_back(vert);
PRINT("Skipping vert");
// If a new pair was added, remove it.
if (newPair != activePairs_.end()) {
activePairs_.erase(newPair);
vert->westPair = activePairs_.end();
vert->eastPair = activePairs_.end();
}
continue;
}
@@ -917,10 +847,10 @@ class Monotones {
monotones_.splice(insertAt, monotones_, vert);
switch (type) {
case WestSide:
case Backward:
nextAttached.push(vert->left);
break;
case EastSide:
case Forward:
nextAttached.push(vert->right);
break;
case Start:
@@ -928,10 +858,10 @@ class Monotones {
nextAttached.push(vert->right);
break;
case Merge:
// Mark merge as hole for sweep-back.
pair->vMerge = vert;
RemovePair(vert->edgeL);
break;
case End:
RemovePair(pair);
RemovePair(vert->edgeR);
break;
case Skip:
break;
@@ -945,7 +875,7 @@ class Monotones {
}
#ifdef MANIFOLD_DEBUG
if (params.verbose) ListPairs();
if (params.verbose) ListActive();
#endif
}
return false;
@@ -975,6 +905,14 @@ class Monotones {
return northEast;
}
VertItr CheckSplit(VertItr vert, EdgeItr westEdge) {
if (westEdge->next != activeEdges_.end()) {
vert = SplitVerts(vert, westEdge->next->south);
westEdge->next = activeEdges_.end(); // unmark merge
}
return vert;
}
/**
* This function sweeps back, splitting the input polygons
* into monotone polygons without doing a single geometric calculation.
@@ -991,92 +929,96 @@ class Monotones {
while (vert != monotones_.begin()) {
--vert;
PRINT("mesh_idx = " << vert->mesh_idx);
if (vert->Processed()) continue;
PRINT("mesh_idx = " << vert->mesh_idx);
VertType type = ProcessVert(vert);
OVERLAP_ASSERT(type != Skip, "Skip should not happen on reverse sweep!");
PairItr westPair = GetPair(vert, type);
OVERLAP_ASSERT(type == Start || westPair != activePairs_.end(),
"No active pair!");
if (type == Merge) {
vert = CheckSplit(vert, vert->edgeR);
const EdgeItr westOf = std::prev(vert->edgeL);
CheckSplit(vert, westOf);
westOf->next = vert->edgeL;
} else if (type == End) {
CheckSplit(vert, vert->edgeR);
}
switch (type) {
case Merge: {
PairItr eastPair = std::next(westPair);
if (eastPair->vMerge != monotones_.end())
vert = SplitVerts(vert, eastPair->vMerge);
eastPair->vMerge = vert;
}
case End:
RemovePair(westPair);
case WestSide:
case EastSide:
if (westPair->vMerge != monotones_.end()) {
VertItr eastVert = SplitVerts(vert, westPair->vMerge);
if (type == WestSide) westPair->vWest = eastVert;
westPair->vMerge = monotones_.end();
}
break;
case Start: {
// Due to sweeping in the opposite direction, east and west are
// swapped and what was the next pair is now the previous pair and
// begin and end are swapped.
PairItr eastPair = westPair;
westPair = eastPair->nextPair;
activePairs_.splice(westPair == activePairs_.end()
? activePairs_.begin()
: std::next(westPair),
inactivePairs_, eastPair);
if (type == Merge || type == End) {
inactiveEdges_.splice(inactiveEdges_.end(), activeEdges_, vert->edgeR);
inactiveEdges_.splice(inactiveEdges_.end(), activeEdges_, vert->edgeL);
} else if (type == Forward) {
CheckSplit(vert, std::prev(vert->edgeL));
} else if (type == Backward) {
CheckSplit(vert, vert->edgeR);
} else if (type == Start) {
// Due to sweeping in the opposite direction, east and west are
// swapped and what was the next pair is now the previous pair and
// begin and end are swapped.
EdgeItr westEdge = vert->edgeL;
EdgeItr eastEdge = vert->edgeR;
EdgeItr eastOf = westEdge->next;
if (eastPair->vMerge == vert) { // Hole
VertItr split = westPair->vMerge != monotones_.end()
? westPair->vMerge
: westPair->vWest->pos.y < westPair->vEast->pos.y
? westPair->vWest
: westPair->vEast;
VertItr eastVert = SplitVerts(vert, split);
westPair->vMerge = monotones_.end();
eastPair->vMerge = monotones_.end();
SetVWest(eastPair, eastVert);
SetVEast(eastPair, split == westPair->vEast ? eastVert->right
: westPair->vEast);
SetVEast(westPair, vert);
} else { // Start
SetVWest(eastPair, vert);
SetVEast(eastPair, vert);
}
break;
if (std::next(eastEdge) == westEdge) std::swap(eastEdge, westEdge);
if (!westEdge->flipped) {
std::swap(westEdge, eastEdge);
eastOf = eastOf == activeEdges_.end() ? activeEdges_.begin()
: std::next(eastOf);
}
case Skip:
break;
activeEdges_.splice(eastOf, inactiveEdges_, eastEdge);
activeEdges_.splice(eastEdge, inactiveEdges_, westEdge);
westEdge->forward ^= true;
eastEdge->forward ^= true;
const bool isHole = westEdge->forward;
if (isHole) {
EdgeItr westOf = std::prev(westEdge);
VertItr split =
westOf->next != activeEdges_.end() ? westOf->next->south
: westOf->south->pos.y < eastOf->south->pos.y ? eastOf->south
: westOf->south;
VertItr eastVert = SplitVerts(vert, split);
westOf->next = activeEdges_.end();
UpdateEdge(eastEdge, eastVert);
UpdateEdge(westEdge, vert);
} else { // Start
vert->edgeL = westEdge;
vert->edgeR = eastEdge;
}
westEdge->next = activeEdges_.end();
eastEdge->next = activeEdges_.end();
}
vert->SetProcessed(true);
#ifdef MANIFOLD_DEBUG
if (params.verbose) ListPairs();
if (params.verbose) ListActive();
#endif
}
return false;
}
#ifdef MANIFOLD_DEBUG
void ListPairs() const {
std::cout << "active edges:" << std::endl;
for (const EdgePair &pair : activePairs_) {
std::cout << (pair.westCertain ? "certain " : "uncertain ");
std::cout << "edge West: S = " << pair.vWest->mesh_idx
<< ", N = " << pair.vWest->left->mesh_idx << std::endl;
if (&*(pair.vWest->eastPair) != &pair)
std::cout << "west does not point back!" << std::endl;
void ListEdge(const EdgeItr edge) const {
std::cout << (edge->forward ? "Fwd" : "Bwd");
std::cout << ": S = " << edge->south->mesh_idx
<< ", N = " << edge->North()->mesh_idx;
std::cout << (edge->next == activeEdges_.end() ? " none" : " next");
std::cout << (edge->eastCertain ? " certain" : " uncertain") << std::endl;
EdgeItr same = edge->forward ? edge->south->edgeR : edge->south->edgeL;
if (same != edge) std::cout << "edgeR does not point back!" << std::endl;
}
std::cout << (pair.eastCertain ? "certain " : "uncertain ");
std::cout << "edge East: S = " << pair.vEast->mesh_idx
<< ", N = " << pair.vEast->right->mesh_idx << std::endl;
if (&*(pair.vEast->westPair) != &pair)
std::cout << "east does not point back!" << std::endl;
void ListActive() {
std::cout << "active edges:" << std::endl;
bool forward = false;
for (auto edge = activeEdges_.begin(); edge != activeEdges_.end(); ++edge) {
ListEdge(edge);
ASSERT(edge->forward == forward, logicErr,
"Active edges are not sanely ordered!");
forward ^= true;
}
}
#endif
+5 -5
View File
@@ -60,14 +60,14 @@ void TestPoly(const Polygons &polys, int expectedNumTri,
PolygonParams().verbose = options.params.verbose;
std::vector<glm::ivec3> triangles;
EXPECT_NO_THROW(triangles = Triangulate(polys, precision));
EXPECT_EQ(triangles.size(), expectedNumTri) << "Basic";
// EXPECT_NO_THROW(triangles = Triangulate(polys, precision));
// EXPECT_EQ(triangles.size(), expectedNumTri) << "Basic";
EXPECT_NO_THROW(triangles = Triangulate(Turn180(polys), precision));
EXPECT_EQ(triangles.size(), expectedNumTri) << "Turn 180";
EXPECT_NO_THROW(triangles = Triangulate(Duplicate(polys), precision));
EXPECT_EQ(triangles.size(), 2 * expectedNumTri) << "Duplicate";
// EXPECT_NO_THROW(triangles = Triangulate(Duplicate(polys), precision));
// EXPECT_EQ(triangles.size(), 2 * expectedNumTri) << "Duplicate";
PolygonParams().verbose = false;
}
@@ -932,7 +932,7 @@ TEST(Polygon, Hole) {
TestPoly(polys, 19);
}
TEST(Polygon, DISABLED_Small) {
TEST(Polygon, Small) {
Polygons polys;
polys.push_back({
{-0.487163663, 0.00357927009}, //