/*************************************************************************/ /* Copyright (c) 2011-2021 Ivan Fratric and contributors. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "polypartition.h" #include TPPLPoly::TPPLPoly() { hole = false; numpoints = 0; points = NULL; } TPPLPoly::~TPPLPoly() { if (points) { delete[] points; } } void TPPLPoly::Clear() { if (points) { delete[] points; } hole = false; numpoints = 0; points = NULL; } void TPPLPoly::Init(long numpoints) { Clear(); this->numpoints = numpoints; points = new TPPLPoint[numpoints]; } void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { Init(3); points[0] = p1; points[1] = p2; points[2] = p3; } TPPLPoly::TPPLPoly(const TPPLPoly &src) : TPPLPoly() { hole = src.hole; numpoints = src.numpoints; if (numpoints > 0) { points = new TPPLPoint[numpoints]; memcpy(points, src.points, numpoints * sizeof(TPPLPoint)); } } TPPLPoly &TPPLPoly::operator=(const TPPLPoly &src) { Clear(); hole = src.hole; numpoints = src.numpoints; if (numpoints > 0) { points = new TPPLPoint[numpoints]; memcpy(points, src.points, numpoints * sizeof(TPPLPoint)); } return *this; } TPPLOrientation TPPLPoly::GetOrientation() const { long i1, i2; tppl_float area = 0; for (i1 = 0; i1 < numpoints; i1++) { i2 = i1 + 1; if (i2 == numpoints) { i2 = 0; } area += points[i1].x * points[i2].y - points[i1].y * points[i2].x; } if (area > 0) { return TPPL_ORIENTATION_CCW; } if (area < 0) { return TPPL_ORIENTATION_CW; } return TPPL_ORIENTATION_NONE; } void TPPLPoly::SetOrientation(TPPLOrientation orientation) { TPPLOrientation polyorientation = GetOrientation(); if (polyorientation != TPPL_ORIENTATION_NONE && polyorientation != orientation) { Invert(); } } void TPPLPoly::Invert() { std::reverse(points, points + numpoints); } TPPLPartition::PartitionVertex::PartitionVertex() : previous(NULL), next(NULL) { } TPPLPoint TPPLPartition::Normalize(const TPPLPoint &p) { TPPLPoint r; tppl_float n = sqrt(p.x * p.x + p.y * p.y); if (n != 0) { r = p / n; } else { r.x = 0; r.y = 0; } return r; } tppl_float TPPLPartition::Distance(const TPPLPoint &p1, const TPPLPoint &p2) { tppl_float dx, dy; dx = p2.x - p1.x; dy = p2.y - p1.y; return (sqrt(dx * dx + dy * dy)); } // Checks if two lines intersect. int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22) { if ((p11.x == p21.x) && (p11.y == p21.y)) { return 0; } if ((p11.x == p22.x) && (p11.y == p22.y)) { return 0; } if ((p12.x == p21.x) && (p12.y == p21.y)) { return 0; } if ((p12.x == p22.x) && (p12.y == p22.y)) { return 0; } TPPLPoint v1ort, v2ort, v; tppl_float dot11, dot12, dot21, dot22; v1ort.x = p12.y - p11.y; v1ort.y = p11.x - p12.x; v2ort.x = p22.y - p21.y; v2ort.y = p21.x - p22.x; v = p21 - p11; dot21 = v.x * v1ort.x + v.y * v1ort.y; v = p22 - p11; dot22 = v.x * v1ort.x + v.y * v1ort.y; v = p11 - p21; dot11 = v.x * v2ort.x + v.y * v2ort.y; v = p12 - p21; dot12 = v.x * v2ort.x + v.y * v2ort.y; if (dot11 * dot12 > 0) { return 0; } if (dot21 * dot22 > 0) { return 0; } return 1; } // Removes holes from inpolys by merging them with non-holes. int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) { TPPLPolyList polys; TPPLPolyList::Element *holeiter, *polyiter, *iter, *iter2; long i, i2, holepointindex, polypointindex; TPPLPoint holepoint, polypoint, bestpolypoint; TPPLPoint linep1, linep2; TPPLPoint v1, v2; TPPLPoly newpoly; bool hasholes; bool pointvisible; bool pointfound; // Check for the trivial case of no holes. hasholes = false; for (iter = inpolys->front(); iter; iter = iter->next()) { if (iter->get().IsHole()) { hasholes = true; break; } } if (!hasholes) { for (iter = inpolys->front(); iter; iter = iter->next()) { outpolys->push_back(iter->get()); } return 1; } polys = *inpolys; while (1) { // Find the hole point with the largest x. hasholes = false; for (iter = polys.front(); iter; iter = iter->next()) { if (!iter->get().IsHole()) { continue; } if (!hasholes) { hasholes = true; holeiter = iter; holepointindex = 0; } for (i = 0; i < iter->get().GetNumPoints(); i++) { if (iter->get().GetPoint(i).x > holeiter->get().GetPoint(holepointindex).x) { holeiter = iter; holepointindex = i; } } } if (!hasholes) { break; } holepoint = holeiter->get().GetPoint(holepointindex); pointfound = false; for (iter = polys.front(); iter; iter = iter->next()) { if (iter->get().IsHole()) { continue; } for (i = 0; i < iter->get().GetNumPoints(); i++) { if (iter->get().GetPoint(i).x <= holepoint.x) { continue; } if (!InCone(iter->get().GetPoint((i + iter->get().GetNumPoints() - 1) % (iter->get().GetNumPoints())), iter->get().GetPoint(i), iter->get().GetPoint((i + 1) % (iter->get().GetNumPoints())), holepoint)) { continue; } polypoint = iter->get().GetPoint(i); if (pointfound) { v1 = Normalize(polypoint - holepoint); v2 = Normalize(bestpolypoint - holepoint); if (v2.x > v1.x) { continue; } } pointvisible = true; for (iter2 = polys.front(); iter2; iter2->next()) { if (iter2->get().IsHole()) { continue; } for (i2 = 0; i2 < iter2->get().GetNumPoints(); i2++) { linep1 = iter2->get().GetPoint(i2); linep2 = iter2->get().GetPoint((i2 + 1) % (iter2->get().GetNumPoints())); if (Intersects(holepoint, polypoint, linep1, linep2)) { pointvisible = false; break; } } if (!pointvisible) { break; } } if (pointvisible) { pointfound = true; bestpolypoint = polypoint; polyiter = iter; polypointindex = i; } } } if (!pointfound) { return 0; } newpoly.Init(holeiter->get().GetNumPoints() + polyiter->get().GetNumPoints() + 2); i2 = 0; for (i = 0; i <= polypointindex; i++) { newpoly[i2] = polyiter->get().GetPoint(i); i2++; } for (i = 0; i <= holeiter->get().GetNumPoints(); i++) { newpoly[i2] = holeiter->get().GetPoint((i + holepointindex) % holeiter->get().GetNumPoints()); i2++; } for (i = polypointindex; i < polyiter->get().GetNumPoints(); i++) { newpoly[i2] = polyiter->get().GetPoint(i); i2++; } polys.erase(holeiter); polys.erase(polyiter); polys.push_back(newpoly); } for (iter = polys.front(); iter; iter = iter->next()) { outpolys->push_back(iter->get()); } return 1; } bool TPPLPartition::IsConvex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { tppl_float tmp; tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); if (tmp > 0) { return 1; } else { return 0; } } bool TPPLPartition::IsReflex(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) { tppl_float tmp; tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); if (tmp < 0) { return 1; } else { return 0; } } bool TPPLPartition::IsInside(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { if (IsConvex(p1, p, p2)) { return false; } if (IsConvex(p2, p, p3)) { return false; } if (IsConvex(p3, p, p1)) { return false; } return true; } bool TPPLPartition::InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p) { bool convex; convex = IsConvex(p1, p2, p3); if (convex) { if (!IsConvex(p1, p2, p)) { return false; } if (!IsConvex(p2, p3, p)) { return false; } return true; } else { if (IsConvex(p1, p2, p)) { return true; } if (IsConvex(p2, p3, p)) { return true; } return false; } } bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) { TPPLPoint p1, p2, p3; p1 = v->previous->p; p2 = v->p; p3 = v->next->p; return InCone(p1, p2, p3, p); } void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) { PartitionVertex *v1 = NULL, *v3 = NULL; v1 = v->previous; v3 = v->next; v->isConvex = !IsReflex(v1->p, v->p, v3->p); } void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) { long i; PartitionVertex *v1 = NULL, *v3 = NULL; TPPLPoint vec1, vec3; v1 = v->previous; v3 = v->next; v->isConvex = IsConvex(v1->p, v->p, v3->p); vec1 = Normalize(v1->p - v->p); vec3 = Normalize(v3->p - v->p); v->angle = vec1.x * vec3.x + vec1.y * vec3.y; if (v->isConvex) { v->isEar = true; for (i = 0; i < numvertices; i++) { if ((vertices[i].p.x == v->p.x) && (vertices[i].p.y == v->p.y)) { continue; } if ((vertices[i].p.x == v1->p.x) && (vertices[i].p.y == v1->p.y)) { continue; } if ((vertices[i].p.x == v3->p.x) && (vertices[i].p.y == v3->p.y)) { continue; } if (IsInside(v1->p, v->p, v3->p, vertices[i].p)) { v->isEar = false; break; } } } else { v->isEar = false; } } // Triangulation by ear removal. int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) { if (!poly->Valid()) { return 0; } long numvertices; PartitionVertex *vertices = NULL; PartitionVertex *ear = NULL; TPPLPoly triangle; long i, j; bool earfound; if (poly->GetNumPoints() < 3) { return 0; } if (poly->GetNumPoints() == 3) { triangles->push_back(*poly); return 1; } numvertices = poly->GetNumPoints(); vertices = new PartitionVertex[numvertices]; for (i = 0; i < numvertices; i++) { vertices[i].isActive = true; vertices[i].p = poly->GetPoint(i); if (i == (numvertices - 1)) { vertices[i].next = &(vertices[0]); } else { vertices[i].next = &(vertices[i + 1]); } if (i == 0) { vertices[i].previous = &(vertices[numvertices - 1]); } else { vertices[i].previous = &(vertices[i - 1]); } } for (i = 0; i < numvertices; i++) { UpdateVertex(&vertices[i], vertices, numvertices); } for (i = 0; i < numvertices - 3; i++) { earfound = false; // Find the most extruded ear. for (j = 0; j < numvertices; j++) { if (!vertices[j].isActive) { continue; } if (!vertices[j].isEar) { continue; } if (!earfound) { earfound = true; ear = &(vertices[j]); } else { if (vertices[j].angle > ear->angle) { ear = &(vertices[j]); } } } if (!earfound) { delete[] vertices; return 0; } triangle.Triangle(ear->previous->p, ear->p, ear->next->p); triangles->push_back(triangle); ear->isActive = false; ear->previous->next = ear->next; ear->next->previous = ear->previous; if (i == numvertices - 4) { break; } UpdateVertex(ear->previous, vertices, numvertices); UpdateVertex(ear->next, vertices, numvertices); } for (i = 0; i < numvertices; i++) { if (vertices[i].isActive) { triangle.Triangle(vertices[i].previous->p, vertices[i].p, vertices[i].next->p); triangles->push_back(triangle); break; } } delete[] vertices; return 1; } int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) { TPPLPolyList outpolys; TPPLPolyList::Element *iter; if (!RemoveHoles(inpolys, &outpolys)) { return 0; } for (iter = outpolys.front(); iter; iter = iter->next()) { if (!Triangulate_EC(&(iter->get()), triangles)) { return 0; } } return 1; } int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) { if (!poly->Valid()) { return 0; } TPPLPolyList triangles; TPPLPolyList::Element *iter1, *iter2; TPPLPoly *poly1 = NULL, *poly2 = NULL; TPPLPoly newpoly; TPPLPoint d1, d2, p1, p2, p3; long i11, i12, i21, i22, i13, i23, j, k; bool isdiagonal; long numreflex; // Check if the poly is already convex. numreflex = 0; for (i11 = 0; i11 < poly->GetNumPoints(); i11++) { if (i11 == 0) { i12 = poly->GetNumPoints() - 1; } else { i12 = i11 - 1; } if (i11 == (poly->GetNumPoints() - 1)) { i13 = 0; } else { i13 = i11 + 1; } if (IsReflex(poly->GetPoint(i12), poly->GetPoint(i11), poly->GetPoint(i13))) { numreflex = 1; break; } } if (numreflex == 0) { parts->push_back(*poly); return 1; } if (!Triangulate_EC(poly, &triangles)) { return 0; } for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) { poly1 = &(iter1->get()); for (i11 = 0; i11 < poly1->GetNumPoints(); i11++) { d1 = poly1->GetPoint(i11); i12 = (i11 + 1) % (poly1->GetNumPoints()); d2 = poly1->GetPoint(i12); isdiagonal = false; for (iter2 = iter1; iter2; iter2 = iter2->next()) { if (iter1 == iter2) { continue; } poly2 = &(iter2->get()); for (i21 = 0; i21 < poly2->GetNumPoints(); i21++) { if ((d2.x != poly2->GetPoint(i21).x) || (d2.y != poly2->GetPoint(i21).y)) { continue; } i22 = (i21 + 1) % (poly2->GetNumPoints()); if ((d1.x != poly2->GetPoint(i22).x) || (d1.y != poly2->GetPoint(i22).y)) { continue; } isdiagonal = true; break; } if (isdiagonal) { break; } } if (!isdiagonal) { continue; } p2 = poly1->GetPoint(i11); if (i11 == 0) { i13 = poly1->GetNumPoints() - 1; } else { i13 = i11 - 1; } p1 = poly1->GetPoint(i13); if (i22 == (poly2->GetNumPoints() - 1)) { i23 = 0; } else { i23 = i22 + 1; } p3 = poly2->GetPoint(i23); if (!IsConvex(p1, p2, p3)) { continue; } p2 = poly1->GetPoint(i12); if (i12 == (poly1->GetNumPoints() - 1)) { i13 = 0; } else { i13 = i12 + 1; } p3 = poly1->GetPoint(i13); if (i21 == 0) { i23 = poly2->GetNumPoints() - 1; } else { i23 = i21 - 1; } p1 = poly2->GetPoint(i23); if (!IsConvex(p1, p2, p3)) { continue; } newpoly.Init(poly1->GetNumPoints() + poly2->GetNumPoints() - 2); k = 0; for (j = i12; j != i11; j = (j + 1) % (poly1->GetNumPoints())) { newpoly[k] = poly1->GetPoint(j); k++; } for (j = i22; j != i21; j = (j + 1) % (poly2->GetNumPoints())) { newpoly[k] = poly2->GetPoint(j); k++; } triangles.erase(iter2); iter1->get() = newpoly; poly1 = &(iter1->get()); i11 = -1; continue; } } for (iter1 = triangles.front(); iter1; iter1 = iter1->next()) { parts->push_back(iter1->get()); } return 1; } int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) { TPPLPolyList outpolys; TPPLPolyList::Element *iter; if (!RemoveHoles(inpolys, &outpolys)) { return 0; } for (iter = outpolys.front(); iter; iter = iter->next()) { if (!ConvexPartition_HM(&(iter->get()), parts)) { return 0; } } return 1; } // Minimum-weight polygon triangulation by dynamic programming. // Time complexity: O(n^3) // Space complexity: O(n^2) int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) { if (!poly->Valid()) { return 0; } long i, j, k, gap, n; DPState **dpstates = NULL; TPPLPoint p1, p2, p3, p4; long bestvertex; tppl_float weight, minweight, d1, d2; Diagonal diagonal, newdiagonal; DiagonalList diagonals; TPPLPoly triangle; int ret = 1; n = poly->GetNumPoints(); dpstates = new DPState *[n]; for (i = 1; i < n; i++) { dpstates[i] = new DPState[i]; } // Initialize states and visibility. for (i = 0; i < (n - 1); i++) { p1 = poly->GetPoint(i); for (j = i + 1; j < n; j++) { dpstates[j][i].visible = true; dpstates[j][i].weight = 0; dpstates[j][i].bestvertex = -1; if (j != (i + 1)) { p2 = poly->GetPoint(j); // Visibility check. if (i == 0) { p3 = poly->GetPoint(n - 1); } else { p3 = poly->GetPoint(i - 1); } if (i == (n - 1)) { p4 = poly->GetPoint(0); } else { p4 = poly->GetPoint(i + 1); } if (!InCone(p3, p1, p4, p2)) { dpstates[j][i].visible = false; continue; } if (j == 0) { p3 = poly->GetPoint(n - 1); } else { p3 = poly->GetPoint(j - 1); } if (j == (n - 1)) { p4 = poly->GetPoint(0); } else { p4 = poly->GetPoint(j + 1); } if (!InCone(p3, p2, p4, p1)) { dpstates[j][i].visible = false; continue; } for (k = 0; k < n; k++) { p3 = poly->GetPoint(k); if (k == (n - 1)) { p4 = poly->GetPoint(0); } else { p4 = poly->GetPoint(k + 1); } if (Intersects(p1, p2, p3, p4)) { dpstates[j][i].visible = false; break; } } } } } dpstates[n - 1][0].visible = true; dpstates[n - 1][0].weight = 0; dpstates[n - 1][0].bestvertex = -1; for (gap = 2; gap < n; gap++) { for (i = 0; i < (n - gap); i++) { j = i + gap; if (!dpstates[j][i].visible) { continue; } bestvertex = -1; for (k = (i + 1); k < j; k++) { if (!dpstates[k][i].visible) { continue; } if (!dpstates[j][k].visible) { continue; } if (k <= (i + 1)) { d1 = 0; } else { d1 = Distance(poly->GetPoint(i), poly->GetPoint(k)); } if (j <= (k + 1)) { d2 = 0; } else { d2 = Distance(poly->GetPoint(k), poly->GetPoint(j)); } weight = dpstates[k][i].weight + dpstates[j][k].weight + d1 + d2; if ((bestvertex == -1) || (weight < minweight)) { bestvertex = k; minweight = weight; } } if (bestvertex == -1) { for (i = 1; i < n; i++) { delete[] dpstates[i]; } delete[] dpstates; return 0; } dpstates[j][i].bestvertex = bestvertex; dpstates[j][i].weight = minweight; } } newdiagonal.index1 = 0; newdiagonal.index2 = n - 1; diagonals.push_back(newdiagonal); while (!diagonals.is_empty()) { diagonal = diagonals.front()->get(); diagonals.pop_front(); bestvertex = dpstates[diagonal.index2][diagonal.index1].bestvertex; if (bestvertex == -1) { ret = 0; break; } triangle.Triangle(poly->GetPoint(diagonal.index1), poly->GetPoint(bestvertex), poly->GetPoint(diagonal.index2)); triangles->push_back(triangle); if (bestvertex > (diagonal.index1 + 1)) { newdiagonal.index1 = diagonal.index1; newdiagonal.index2 = bestvertex; diagonals.push_back(newdiagonal); } if (diagonal.index2 > (bestvertex + 1)) { newdiagonal.index1 = bestvertex; newdiagonal.index2 = diagonal.index2; diagonals.push_back(newdiagonal); } } for (i = 1; i < n; i++) { delete[] dpstates[i]; } delete[] dpstates; return ret; } void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates) { Diagonal newdiagonal; DiagonalList *pairs = NULL; long w2; w2 = dpstates[a][b].weight; if (w > w2) { return; } pairs = &(dpstates[a][b].pairs); newdiagonal.index1 = i; newdiagonal.index2 = j; if (w < w2) { pairs->clear(); pairs->push_front(newdiagonal); dpstates[a][b].weight = w; } else { if ((!pairs->is_empty()) && (i <= pairs->front()->get().index1)) { return; } while ((!pairs->is_empty()) && (pairs->front()->get().index2 >= j)) { pairs->pop_front(); } pairs->push_front(newdiagonal); } } void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { DiagonalList *pairs = NULL; DiagonalList::Element *iter, *lastiter; long top; long w; if (!dpstates[i][j].visible) { return; } top = j; w = dpstates[i][j].weight; if (k - j > 1) { if (!dpstates[j][k].visible) { return; } w += dpstates[j][k].weight + 1; } if (j - i > 1) { pairs = &(dpstates[i][j].pairs); iter = pairs->back(); lastiter = pairs->back(); while (iter != pairs->front()) { iter--; if (!IsReflex(vertices[iter->get().index2].p, vertices[j].p, vertices[k].p)) { lastiter = iter; } else { break; } } if (lastiter == pairs->back()) { w++; } else { if (IsReflex(vertices[k].p, vertices[i].p, vertices[lastiter->get().index1].p)) { w++; } else { top = lastiter->get().index1; } } } UpdateState(i, k, w, top, j, dpstates); } void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) { DiagonalList *pairs = NULL; DiagonalList::Element *iter, *lastiter; long top; long w; if (!dpstates[j][k].visible) { return; } top = j; w = dpstates[j][k].weight; if (j - i > 1) { if (!dpstates[i][j].visible) { return; } w += dpstates[i][j].weight + 1; } if (k - j > 1) { pairs = &(dpstates[j][k].pairs); iter = pairs->front(); if ((!pairs->is_empty()) && (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p))) { lastiter = iter; while (iter) { if (!IsReflex(vertices[i].p, vertices[j].p, vertices[iter->get().index1].p)) { lastiter = iter; iter = iter->next(); } else { break; } } if (IsReflex(vertices[lastiter->get().index2].p, vertices[k].p, vertices[i].p)) { w++; } else { top = lastiter->get().index2; } } else { w++; } } UpdateState(i, k, w, j, top, dpstates); } int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) { if (!poly->Valid()) { return 0; } TPPLPoint p1, p2, p3, p4; PartitionVertex *vertices = NULL; DPState2 **dpstates = NULL; long i, j, k, n, gap; DiagonalList diagonals, diagonals2; Diagonal diagonal, newdiagonal; DiagonalList *pairs = NULL, *pairs2 = NULL; DiagonalList::Element *iter, *iter2; int ret; TPPLPoly newpoly; List indices; List::Element *iiter; bool ijreal, jkreal; n = poly->GetNumPoints(); vertices = new PartitionVertex[n]; dpstates = new DPState2 *[n]; for (i = 0; i < n; i++) { dpstates[i] = new DPState2[n]; } // Initialize vertex information. for (i = 0; i < n; i++) { vertices[i].p = poly->GetPoint(i); vertices[i].isActive = true; if (i == 0) { vertices[i].previous = &(vertices[n - 1]); } else { vertices[i].previous = &(vertices[i - 1]); } if (i == (poly->GetNumPoints() - 1)) { vertices[i].next = &(vertices[0]); } else { vertices[i].next = &(vertices[i + 1]); } } for (i = 1; i < n; i++) { UpdateVertexReflexity(&(vertices[i])); } // Initialize states and visibility. for (i = 0; i < (n - 1); i++) { p1 = poly->GetPoint(i); for (j = i + 1; j < n; j++) { dpstates[i][j].visible = true; if (j == i + 1) { dpstates[i][j].weight = 0; } else { dpstates[i][j].weight = 2147483647; } if (j != (i + 1)) { p2 = poly->GetPoint(j); // Visibility check. if (!InCone(&vertices[i], p2)) { dpstates[i][j].visible = false; continue; } if (!InCone(&vertices[j], p1)) { dpstates[i][j].visible = false; continue; } for (k = 0; k < n; k++) { p3 = poly->GetPoint(k); if (k == (n - 1)) { p4 = poly->GetPoint(0); } else { p4 = poly->GetPoint(k + 1); } if (Intersects(p1, p2, p3, p4)) { dpstates[i][j].visible = false; break; } } } } } for (i = 0; i < (n - 2); i++) { j = i + 2; if (dpstates[i][j].visible) { dpstates[i][j].weight = 0; newdiagonal.index1 = i + 1; newdiagonal.index2 = i + 1; dpstates[i][j].pairs.push_back(newdiagonal); } } dpstates[0][n - 1].visible = true; vertices[0].isConvex = false; // By convention. for (gap = 3; gap < n; gap++) { for (i = 0; i < n - gap; i++) { if (vertices[i].isConvex) { continue; } k = i + gap; if (dpstates[i][k].visible) { if (!vertices[k].isConvex) { for (j = i + 1; j < k; j++) { TypeA(i, j, k, vertices, dpstates); } } else { for (j = i + 1; j < (k - 1); j++) { if (vertices[j].isConvex) { continue; } TypeA(i, j, k, vertices, dpstates); } TypeA(i, k - 1, k, vertices, dpstates); } } } for (k = gap; k < n; k++) { if (vertices[k].isConvex) { continue; } i = k - gap; if ((vertices[i].isConvex) && (dpstates[i][k].visible)) { TypeB(i, i + 1, k, vertices, dpstates); for (j = i + 2; j < k; j++) { if (vertices[j].isConvex) { continue; } TypeB(i, j, k, vertices, dpstates); } } } } // Recover solution. ret = 1; newdiagonal.index1 = 0; newdiagonal.index2 = n - 1; diagonals.push_front(newdiagonal); while (!diagonals.is_empty()) { diagonal = diagonals.front()->get(); diagonals.pop_front(); if ((diagonal.index2 - diagonal.index1) <= 1) { continue; } pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs); if (pairs->is_empty()) { ret = 0; break; } if (!vertices[diagonal.index1].isConvex) { iter = pairs->back(); iter--; j = iter->get().index2; newdiagonal.index1 = j; newdiagonal.index2 = diagonal.index2; diagonals.push_front(newdiagonal); if ((j - diagonal.index1) > 1) { if (iter->get().index1 != iter->get().index2) { pairs2 = &(dpstates[diagonal.index1][j].pairs); while (1) { if (pairs2->is_empty()) { ret = 0; break; } iter2 = pairs2->back(); iter2--; if (iter->get().index1 != iter2->get().index1) { pairs2->pop_back(); } else { break; } } if (ret == 0) { break; } } newdiagonal.index1 = diagonal.index1; newdiagonal.index2 = j; diagonals.push_front(newdiagonal); } } else { iter = pairs->front(); j = iter->get().index1; newdiagonal.index1 = diagonal.index1; newdiagonal.index2 = j; diagonals.push_front(newdiagonal); if ((diagonal.index2 - j) > 1) { if (iter->get().index1 != iter->get().index2) { pairs2 = &(dpstates[j][diagonal.index2].pairs); while (1) { if (pairs2->is_empty()) { ret = 0; break; } iter2 = pairs2->front(); if (iter->get().index2 != iter2->get().index2) { pairs2->pop_front(); } else { break; } } if (ret == 0) { break; } } newdiagonal.index1 = j; newdiagonal.index2 = diagonal.index2; diagonals.push_front(newdiagonal); } } } if (ret == 0) { for (i = 0; i < n; i++) { delete[] dpstates[i]; } delete[] dpstates; delete[] vertices; return ret; } newdiagonal.index1 = 0; newdiagonal.index2 = n - 1; diagonals.push_front(newdiagonal); while (!diagonals.is_empty()) { diagonal = diagonals.front()->get(); diagonals.pop_front(); if ((diagonal.index2 - diagonal.index1) <= 1) { continue; } indices.clear(); diagonals2.clear(); indices.push_back(diagonal.index1); indices.push_back(diagonal.index2); diagonals2.push_front(diagonal); while (!diagonals2.is_empty()) { diagonal = diagonals2.front()->get(); diagonals2.pop_front(); if ((diagonal.index2 - diagonal.index1) <= 1) { continue; } ijreal = true; jkreal = true; pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs); if (!vertices[diagonal.index1].isConvex) { iter = pairs->back(); iter--; j = iter->get().index2; if (iter->get().index1 != iter->get().index2) { ijreal = false; } } else { iter = pairs->front(); j = iter->get().index1; if (iter->get().index1 != iter->get().index2) { jkreal = false; } } newdiagonal.index1 = diagonal.index1; newdiagonal.index2 = j; if (ijreal) { diagonals.push_back(newdiagonal); } else { diagonals2.push_back(newdiagonal); } newdiagonal.index1 = j; newdiagonal.index2 = diagonal.index2; if (jkreal) { diagonals.push_back(newdiagonal); } else { diagonals2.push_back(newdiagonal); } indices.push_back(j); } //std::sort(indices.begin(), indices.end()); indices.sort(); newpoly.Init((long)indices.size()); k = 0; for (iiter = indices.front(); iiter != indices.back(); iiter = iiter->next()) { newpoly[k] = vertices[iiter->get()].p; k++; } parts->push_back(newpoly); } for (i = 0; i < n; i++) { delete[] dpstates[i]; } delete[] dpstates; delete[] vertices; return ret; } // Creates a monotone partition of a list of polygons that // can contain holes. Triangulates a set of polygons by // first partitioning them into monotone polygons. // Time complexity: O(n*log(n)), n is the number of vertices. // Space complexity: O(n) // The algorithm used here is outlined in the book // "Computational Geometry: Algorithms and Applications" // by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars. int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) { TPPLPolyList::Element *iter; MonotoneVertex *vertices = NULL; long i, numvertices, vindex, vindex2, newnumvertices, maxnumvertices; long polystartindex, polyendindex; TPPLPoly *poly = NULL; MonotoneVertex *v = NULL, *v2 = NULL, *vprev = NULL, *vnext = NULL; ScanLineEdge newedge; bool error = false; numvertices = 0; for (iter = inpolys->front(); iter; iter = iter->next()) { numvertices += iter->get().GetNumPoints(); } maxnumvertices = numvertices * 3; vertices = new MonotoneVertex[maxnumvertices]; newnumvertices = numvertices; polystartindex = 0; for (iter = inpolys->front(); iter; iter = iter->next()) { poly = &(iter->get()); polyendindex = polystartindex + poly->GetNumPoints() - 1; for (i = 0; i < poly->GetNumPoints(); i++) { vertices[i + polystartindex].p = poly->GetPoint(i); if (i == 0) { vertices[i + polystartindex].previous = polyendindex; } else { vertices[i + polystartindex].previous = i + polystartindex - 1; } if (i == (poly->GetNumPoints() - 1)) { vertices[i + polystartindex].next = polystartindex; } else { vertices[i + polystartindex].next = i + polystartindex + 1; } } polystartindex = polyendindex + 1; } // Construct the priority queue. long *priority = new long[numvertices]; for (i = 0; i < numvertices; i++) { priority[i] = i; } std::sort(priority, &(priority[numvertices]), VertexSorter(vertices)); // Determine vertex types. TPPLVertexType *vertextypes = new TPPLVertexType[maxnumvertices]; for (i = 0; i < numvertices; i++) { v = &(vertices[i]); vprev = &(vertices[v->previous]); vnext = &(vertices[v->next]); if (Below(vprev->p, v->p) && Below(vnext->p, v->p)) { if (IsConvex(vnext->p, vprev->p, v->p)) { vertextypes[i] = TPPL_VERTEXTYPE_START; } else { vertextypes[i] = TPPL_VERTEXTYPE_SPLIT; } } else if (Below(v->p, vprev->p) && Below(v->p, vnext->p)) { if (IsConvex(vnext->p, vprev->p, v->p)) { vertextypes[i] = TPPL_VERTEXTYPE_END; } else { vertextypes[i] = TPPL_VERTEXTYPE_MERGE; } } else { vertextypes[i] = TPPL_VERTEXTYPE_REGULAR; } } // Helpers. long *helpers = new long[maxnumvertices]; // Binary search tree that holds edges intersecting the scanline. // Note that while set doesn't actually have to be implemented as // a tree, complexity requirements for operations are the same as // for the balanced binary search tree. Set edgeTree; // Store iterators to the edge tree elements. // This makes deleting existing edges much faster. Set::Element **edgeTreeIterators, *edgeIter; edgeTreeIterators = new Set::Element *[maxnumvertices]; //Pair::iterator, bool> edgeTreeRet; for (i = 0; i < numvertices; i++) { edgeTreeIterators[i] = nullptr; } // For each vertex. for (i = 0; i < numvertices; i++) { vindex = priority[i]; v = &(vertices[vindex]); vindex2 = vindex; v2 = v; // Depending on the vertex type, do the appropriate action. // Comments in the following sections are copied from // "Computational Geometry: Algorithms and Applications". // Notation: e_i = e subscript i, v_i = v subscript i, etc. switch (vertextypes[vindex]) { case TPPL_VERTEXTYPE_START: // Insert e_i in T and set helper(e_i) to v_i. newedge.p1 = v->p; newedge.p2 = vertices[v->next].p; newedge.index = vindex; //edgeTreeRet = edgeTree.insert(newedge); //edgeTreeIterators[vindex] = edgeTreeRet.first; edgeTreeIterators[vindex] = edgeTree.insert(newedge); helpers[vindex] = vindex; break; case TPPL_VERTEXTYPE_END: if (edgeTreeIterators[v->previous] == edgeTree.back()) { error = true; break; } // If helper(e_i - 1) is a merge vertex if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { // Insert the diagonal connecting vi to helper(e_i - 1) in D. AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], vertextypes, edgeTreeIterators, &edgeTree, helpers); } // Delete e_i - 1 from T edgeTree.erase(edgeTreeIterators[v->previous]); break; case TPPL_VERTEXTYPE_SPLIT: // Search in T to find the edge e_j directly left of v_i. newedge.p1 = v->p; newedge.p2 = v->p; edgeIter = edgeTree.lower_bound(newedge); if (edgeIter == nullptr || edgeIter == edgeTree.front()) { error = true; break; } edgeIter--; // Insert the diagonal connecting vi to helper(e_j) in D. AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index], vertextypes, edgeTreeIterators, &edgeTree, helpers); vindex2 = newnumvertices - 2; v2 = &(vertices[vindex2]); // helper(e_j) in v_i. helpers[edgeIter->get().index] = vindex; // Insert e_i in T and set helper(e_i) to v_i. newedge.p1 = v2->p; newedge.p2 = vertices[v2->next].p; newedge.index = vindex2; //edgeTreeRet = edgeTree.insert(newedge); //edgeTreeIterators[vindex2] = edgeTreeRet.first; edgeTreeIterators[vindex2] = edgeTree.insert(newedge); helpers[vindex2] = vindex2; break; case TPPL_VERTEXTYPE_MERGE: if (edgeTreeIterators[v->previous] == edgeTree.back()) { error = true; break; } // if helper(e_i - 1) is a merge vertex if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { // Insert the diagonal connecting vi to helper(e_i - 1) in D. AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], vertextypes, edgeTreeIterators, &edgeTree, helpers); vindex2 = newnumvertices - 2; v2 = &(vertices[vindex2]); } // Delete e_i - 1 from T. edgeTree.erase(edgeTreeIterators[v->previous]); // Search in T to find the edge e_j directly left of v_i. newedge.p1 = v->p; newedge.p2 = v->p; edgeIter = edgeTree.lower_bound(newedge); if (edgeIter == nullptr || edgeIter == edgeTree.front()) { error = true; break; } edgeIter--; // If helper(e_j) is a merge vertex. if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) { // Insert the diagonal connecting v_i to helper(e_j) in D. AddDiagonal(vertices, &newnumvertices, vindex2, helpers[edgeIter->get().index], vertextypes, edgeTreeIterators, &edgeTree, helpers); } // helper(e_j) <- v_i helpers[edgeIter->get().index] = vindex2; break; case TPPL_VERTEXTYPE_REGULAR: // If the interior of P lies to the right of v_i. if (Below(v->p, vertices[v->previous].p)) { if (edgeTreeIterators[v->previous] == edgeTree.back()) { error = true; break; } // If helper(e_i - 1) is a merge vertex. if (vertextypes[helpers[v->previous]] == TPPL_VERTEXTYPE_MERGE) { // Insert the diagonal connecting v_i to helper(e_i - 1) in D. AddDiagonal(vertices, &newnumvertices, vindex, helpers[v->previous], vertextypes, edgeTreeIterators, &edgeTree, helpers); vindex2 = newnumvertices - 2; v2 = &(vertices[vindex2]); } // Delete e_i - 1 from T. edgeTree.erase(edgeTreeIterators[v->previous]); // Insert e_i in T and set helper(e_i) to v_i. newedge.p1 = v2->p; newedge.p2 = vertices[v2->next].p; newedge.index = vindex2; //edgeTreeRet = edgeTree.insert(newedge); //edgeTreeIterators[vindex2] = edgeTreeRet.first; edgeTreeIterators[vindex2] = edgeTree.insert(newedge); helpers[vindex2] = vindex; } else { // Search in T to find the edge e_j directly left of v_i. newedge.p1 = v->p; newedge.p2 = v->p; edgeIter = edgeTree.lower_bound(newedge); if (edgeIter == nullptr || edgeIter == edgeTree.front()) { error = true; break; } edgeIter = edgeIter->prev(); // If helper(e_j) is a merge vertex. if (vertextypes[helpers[edgeIter->get().index]] == TPPL_VERTEXTYPE_MERGE) { // Insert the diagonal connecting v_i to helper(e_j) in D. AddDiagonal(vertices, &newnumvertices, vindex, helpers[edgeIter->get().index], vertextypes, edgeTreeIterators, &edgeTree, helpers); } // helper(e_j) <- v_i. helpers[edgeIter->get().index] = vindex; } break; } if (error) break; } char *used = new char[newnumvertices]; memset(used, 0, newnumvertices * sizeof(char)); if (!error) { // Return result. long size; TPPLPoly mpoly; for (i = 0; i < newnumvertices; i++) { if (used[i]) { continue; } v = &(vertices[i]); vnext = &(vertices[v->next]); size = 1; while (vnext != v) { vnext = &(vertices[vnext->next]); size++; } mpoly.Init(size); v = &(vertices[i]); mpoly[0] = v->p; vnext = &(vertices[v->next]); size = 1; used[i] = 1; used[v->next] = 1; while (vnext != v) { mpoly[size] = vnext->p; used[vnext->next] = 1; vnext = &(vertices[vnext->next]); size++; } monotonePolys->push_back(mpoly); } } // Cleanup. delete[] vertices; delete[] priority; delete[] vertextypes; delete[] edgeTreeIterators; delete[] helpers; delete[] used; if (error) { return 0; } else { return 1; } } // Adds a diagonal to the doubly-connected list of vertices. void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2, TPPLVertexType *vertextypes, Set::Element **edgeTreeIterators, Set *edgeTree, long *helpers) { long newindex1, newindex2; newindex1 = *numvertices; (*numvertices)++; newindex2 = *numvertices; (*numvertices)++; vertices[newindex1].p = vertices[index1].p; vertices[newindex2].p = vertices[index2].p; vertices[newindex2].next = vertices[index2].next; vertices[newindex1].next = vertices[index1].next; vertices[vertices[index2].next].previous = newindex2; vertices[vertices[index1].next].previous = newindex1; vertices[index1].next = newindex2; vertices[newindex2].previous = index1; vertices[index2].next = newindex1; vertices[newindex1].previous = index2; // Update all relevant structures. vertextypes[newindex1] = vertextypes[index1]; edgeTreeIterators[newindex1] = edgeTreeIterators[index1]; helpers[newindex1] = helpers[index1]; if (edgeTreeIterators[newindex1] != edgeTree->back()) { edgeTreeIterators[newindex1]->get().index = newindex1; } vertextypes[newindex2] = vertextypes[index2]; edgeTreeIterators[newindex2] = edgeTreeIterators[index2]; helpers[newindex2] = helpers[index2]; if (edgeTreeIterators[newindex2] != edgeTree->back()) { edgeTreeIterators[newindex2]->get().index = newindex2; } } bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) { if (p1.y < p2.y) { return true; } else if (p1.y == p2.y) { if (p1.x < p2.x) { return true; } } return false; } // Sorts in the falling order of y values, if y is equal, x is used instead. bool TPPLPartition::VertexSorter::operator()(long index1, long index2) { if (vertices[index1].p.y > vertices[index2].p.y) { return true; } else if (vertices[index1].p.y == vertices[index2].p.y) { if (vertices[index1].p.x > vertices[index2].p.x) { return true; } } return false; } bool TPPLPartition::ScanLineEdge::IsConvex(const TPPLPoint &p1, const TPPLPoint &p2, const TPPLPoint &p3) const { tppl_float tmp; tmp = (p3.y - p1.y) * (p2.x - p1.x) - (p3.x - p1.x) * (p2.y - p1.y); if (tmp > 0) { return 1; } return 0; } bool TPPLPartition::ScanLineEdge::operator<(const ScanLineEdge &other) const { if (other.p1.y == other.p2.y) { if (p1.y == p2.y) { return (p1.y < other.p1.y); } return IsConvex(p1, p2, other.p1); } else if (p1.y == p2.y) { return !IsConvex(other.p1, other.p2, p1); } else if (p1.y < other.p1.y) { return !IsConvex(other.p1, other.p2, p1); } else { return IsConvex(p1, p2, other.p1); } } // Triangulates monotone polygon. // Time complexity: O(n) // Space complexity: O(n) int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles) { if (!inPoly->Valid()) { return 0; } long i, i2, j, topindex, bottomindex, leftindex, rightindex, vindex; TPPLPoint *points = NULL; long numpoints; TPPLPoly triangle; numpoints = inPoly->GetNumPoints(); points = inPoly->GetPoints(); // Trivial case. if (numpoints == 3) { triangles->push_back(*inPoly); return 1; } topindex = 0; bottomindex = 0; for (i = 1; i < numpoints; i++) { if (Below(points[i], points[bottomindex])) { bottomindex = i; } if (Below(points[topindex], points[i])) { topindex = i; } } // Check if the poly is really monotone. i = topindex; while (i != bottomindex) { i2 = i + 1; if (i2 >= numpoints) { i2 = 0; } if (!Below(points[i2], points[i])) { return 0; } i = i2; } i = bottomindex; while (i != topindex) { i2 = i + 1; if (i2 >= numpoints) { i2 = 0; } if (!Below(points[i], points[i2])) { return 0; } i = i2; } char *vertextypes = new char[numpoints]; long *priority = new long[numpoints]; // Merge left and right vertex chains. priority[0] = topindex; vertextypes[topindex] = 0; leftindex = topindex + 1; if (leftindex >= numpoints) { leftindex = 0; } rightindex = topindex - 1; if (rightindex < 0) { rightindex = numpoints - 1; } for (i = 1; i < (numpoints - 1); i++) { if (leftindex == bottomindex) { priority[i] = rightindex; rightindex--; if (rightindex < 0) { rightindex = numpoints - 1; } vertextypes[priority[i]] = -1; } else if (rightindex == bottomindex) { priority[i] = leftindex; leftindex++; if (leftindex >= numpoints) { leftindex = 0; } vertextypes[priority[i]] = 1; } else { if (Below(points[leftindex], points[rightindex])) { priority[i] = rightindex; rightindex--; if (rightindex < 0) { rightindex = numpoints - 1; } vertextypes[priority[i]] = -1; } else { priority[i] = leftindex; leftindex++; if (leftindex >= numpoints) { leftindex = 0; } vertextypes[priority[i]] = 1; } } } priority[i] = bottomindex; vertextypes[bottomindex] = 0; long *stack = new long[numpoints]; long stackptr = 0; stack[0] = priority[0]; stack[1] = priority[1]; stackptr = 2; // For each vertex from top to bottom trim as many triangles as possible. for (i = 2; i < (numpoints - 1); i++) { vindex = priority[i]; if (vertextypes[vindex] != vertextypes[stack[stackptr - 1]]) { for (j = 0; j < (stackptr - 1); j++) { if (vertextypes[vindex] == 1) { triangle.Triangle(points[stack[j + 1]], points[stack[j]], points[vindex]); } else { triangle.Triangle(points[stack[j]], points[stack[j + 1]], points[vindex]); } triangles->push_back(triangle); } stack[0] = priority[i - 1]; stack[1] = priority[i]; stackptr = 2; } else { stackptr--; while (stackptr > 0) { if (vertextypes[vindex] == 1) { if (IsConvex(points[vindex], points[stack[stackptr - 1]], points[stack[stackptr]])) { triangle.Triangle(points[vindex], points[stack[stackptr - 1]], points[stack[stackptr]]); triangles->push_back(triangle); stackptr--; } else { break; } } else { if (IsConvex(points[vindex], points[stack[stackptr]], points[stack[stackptr - 1]])) { triangle.Triangle(points[vindex], points[stack[stackptr]], points[stack[stackptr - 1]]); triangles->push_back(triangle); stackptr--; } else { break; } } } stackptr++; stack[stackptr] = vindex; stackptr++; } } vindex = priority[i]; for (j = 0; j < (stackptr - 1); j++) { if (vertextypes[stack[j + 1]] == 1) { triangle.Triangle(points[stack[j]], points[stack[j + 1]], points[vindex]); } else { triangle.Triangle(points[stack[j + 1]], points[stack[j]], points[vindex]); } triangles->push_back(triangle); } delete[] priority; delete[] vertextypes; delete[] stack; return 1; } int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) { TPPLPolyList monotone; TPPLPolyList::Element *iter; if (!MonotonePartition(inpolys, &monotone)) { return 0; } for (iter = monotone.front(); iter; iter = iter->next()) { if (!TriangulateMonotone(&(iter->get()), triangles)) { return 0; } } return 1; } int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles) { TPPLPolyList polys; polys.push_back(*poly); return Triangulate_MONO(&polys, triangles); }