| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // This is an implementation of the triangulation algorithm described by Alain |
| // Fournier and Delfin Montuno, in "Triangulating Simple Polygons and Equivalent |
| // Problems", in the ACM Transactions on Graphics, vol. 3, no. 2, April 1984, |
| // pp. 153-174. |
| // |
| // No new vertices are created in the triangulation: triangles are constructed |
| // only from the points in the original polygon, so there is no possibility for |
| // cracks to develop from finite precision arithmetic. |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // TODO: |
| // - RemoveDegenerateTrapezoids() was added to make the code robust, but it |
| // unfortunately introduces T-vertices. Make it robust without T-vertices. |
| // - It should be easy enough to detect whether the outer contour is right- or |
| // left-handed by looking at the top vertex, which is available in the |
| // pre-sort during trapezoidization. Use this information in angleIsConvex() |
| // to allowed either handedness outer contour. In either case, though, holes |
| // need to have the opposite orientation. |
| // - SkTHeapSort was broken, so I wrote a bubble sort so that I could make other |
| // things work. Use SkQSort() instead. |
| // - The ActiveTrapezoid array does a linear search which is O(n) inefficient. |
| // Use SkSearch to implement O(log n) binary search and insertion sort. |
| // - There is no need to use SkTDArray for everything. Use SkAutoTMalloc for |
| // everything else. |
| |
| #include "SkTDArray.h" |
| #include "SkGeometry.h" |
| #include "SkTSort.h" |
| |
| // This is used to prevent runaway code bugs, and can probably be removed after |
| // the code has been proven robust. |
| #define kMaxCount 1000 |
| |
| #define DEBUG |
| #ifdef DEBUG |
| //------------------------------------------------------------------------------ |
| // Debugging support |
| //------------------------------------------------------------------------------ |
| |
| #include <cstdio> |
| #include <stdarg.h> |
| |
| static int gDebugLevel = 0; // This enables debug reporting. |
| |
| static void DebugPrintf(const char *format, ...) { |
| if (gDebugLevel > 0) { |
| va_list ap; |
| va_start(ap, format); |
| vprintf(format, ap); |
| va_end(ap); |
| } |
| } |
| |
| static void FailureMessage(const char *format, ...) { |
| if (1) { |
| printf("FAILURE: "); |
| va_list ap; |
| va_start(ap, format); |
| vprintf(format, ap); |
| va_end(ap); |
| } |
| } |
| #else // !DEBUG |
| inline void DebugPrintf(const char *format, ...) {} |
| inline void FailureMessage(const char *format, ...) {} |
| #endif // DEBUG |
| |
| |
| // Forward declaration. |
| class Vertex; |
| |
| |
| //------------------------------------------------------------------------------ |
| // The Trapezoid (actually, up to two of them) is embedded into a Vertex, whose |
| // point() provides the top of the Trapezoid, whereas the bottom, and the left |
| // and right edges, are stored in the Trapezoid. The edges are represented by |
| // their tail point; the head is the successive point modulo the number of |
| // points in the polygon. Only the Y coordinate of the top and bottom are |
| // relevant. |
| //------------------------------------------------------------------------------ |
| class Trapezoid { |
| public: |
| const Vertex* left() const { return fLeft; } |
| const Vertex* right() const { return fRight; } |
| const Vertex* bottom() const { return fBottom; } |
| Vertex* left() { return fLeft; } |
| Vertex* right() { return fRight; } |
| Vertex* bottom() { return fBottom; } |
| void setLeft(Vertex *left) { fLeft = left; } |
| void setRight(Vertex *right) { fRight = right; } |
| void setBottom(Vertex *bottom) { fBottom = bottom; } |
| void nullify() { setBottom(NULL); } |
| |
| bool operator<(Trapezoid &t1) { return compare(t1) < 0; } |
| bool operator>(Trapezoid &t1) { return compare(t1) > 0; } |
| |
| private: |
| Vertex *fLeft, *fRight, *fBottom; |
| |
| // These return a number that is less than, equal to, or greater than zero |
| // depending on whether the trapezoid or point is to the left, on, or to the |
| // right. |
| SkScalar compare(const Trapezoid &t1) const; |
| SkScalar compare(const SkPoint &p) const; |
| }; |
| |
| |
| //------------------------------------------------------------------------------ |
| // The ActiveTrapezoids are a sorted list containing the currently active |
| // trapezoids, i.e. those that have the top, left, and right, but still need the |
| // bottom. This could use some optimization, to reduce search time from O(n) to |
| // O(log n). |
| //------------------------------------------------------------------------------ |
| class ActiveTrapezoids { |
| public: |
| ActiveTrapezoids() { fTrapezoids.setCount(0); } |
| |
| size_t count() const { return fTrapezoids.count(); } |
| |
| // Select an unused trapezoid from the Vertex vt, initialize it with the |
| // left and right edges, and insert it into the sorted list. |
| bool insertNewTrapezoid(Vertex *vt, Vertex *left, Vertex *right); |
| |
| // Remove the specified Trapezoids from the active list. |
| void remove(Trapezoid *t); |
| |
| // Determine whether the given point lies within any active trapezoid, and |
| // return a pointer to that Trapezoid. |
| bool withinActiveTrapezoid(const SkPoint &pt, Trapezoid **tp); |
| |
| // Find an active trapezoid that contains the given edge. |
| Trapezoid* getTrapezoidWithEdge(const Vertex *edge); |
| |
| private: |
| // Insert the specified Trapezoid into the sorted list. |
| void insert(Trapezoid *t); |
| |
| // The sorted list of active trapezoids. This is O(n), and would benefit |
| // a 2-3 tree o achieve O(log n). |
| SkTDArray<Trapezoid*> fTrapezoids; // Fournier suggests a 2-3 tree instead. |
| }; |
| |
| |
| //------------------------------------------------------------------------------ |
| // The Vertex is used to communicate information between the various phases of |
| // triangulation. |
| //------------------------------------------------------------------------------ |
| class Vertex { |
| public: |
| enum VertexType { MONOTONE, CONVEX, CONCAVE }; |
| |
| Trapezoid fTrap0; |
| Trapezoid fTrap1; |
| |
| const SkPoint &point() const { return fPoint; } |
| void setPoint(const SkPoint &point) { fPoint = point; } |
| |
| // The next and previous vertices around the polygon. |
| Vertex *next() { return fNext; } |
| Vertex *prev() { return fPrev; } |
| const Vertex *next() const { return fNext; } |
| const Vertex *prev() const { return fPrev; } |
| void setNext(Vertex *next) { fNext = next; } |
| void setPrev(Vertex *prev) { fPrev = prev; } |
| |
| void setDone(bool done) { fDone = done; } |
| bool done() const { return fDone; } |
| |
| // Trapezoid accessors return non-null for any complete trapezoids. |
| void trapezoids(Trapezoid **trap0, Trapezoid **trap1) { |
| *trap0 = (fTrap0.bottom() != NULL) ? &fTrap0 : NULL; |
| *trap1 = (fTrap1.bottom() != NULL) ? &fTrap1 : NULL; |
| } |
| |
| // Eliminate a trapezoid. |
| void nullifyTrapezoid() { |
| if (fTrap1.bottom() != NULL) { |
| DebugPrintf("Unexpected non-null second trapezoid.\n"); |
| fTrap1.nullify(); |
| return; |
| } |
| fTrap0.nullify(); |
| } |
| |
| // Determine whether the edge specified by this Vertex shares the given top |
| // and bottom. |
| bool shareEdge(Vertex *top, Vertex *bottom); |
| |
| // Determines whether the angle specified by { prev, this, next } is convex. |
| // Note that collinear is considered to be convex. |
| bool angleIsConvex(); |
| |
| // Remove this Vertex from the { prev, next } linked list. |
| void delink() { |
| Vertex *p = prev(), |
| *n = next(); |
| p->setNext(n); |
| n->setPrev(p); |
| } |
| |
| // Return a number that is less than, equal to, or greater than zero |
| // depending on whether the point is to the left, on, or to the right of the |
| // edge that has this Vertex as a base. |
| SkScalar compare(const SkPoint &pt) const; |
| |
| // Classify the vertex, and return its sorted edges. |
| VertexType classify(Vertex **e0, Vertex **e1); |
| |
| // This helps determine unimonotonicity. |
| Vertex *diagonal(); |
| |
| private: |
| SkPoint fPoint; |
| Vertex *fNext; |
| Vertex *fPrev; |
| bool fDone; |
| }; |
| |
| |
| bool Vertex::angleIsConvex() { |
| SkPoint vPrev = prev()->point() - point(), |
| vNext = next()->point() - point(); |
| // TODO(turk): There might be overflow in fixed-point. |
| return SkPoint::CrossProduct(vNext, vPrev) >= 0; |
| } |
| |
| |
| bool Vertex::shareEdge(Vertex *top, Vertex *bottom) { |
| return (((this == top) && (this->next() == bottom)) || |
| ((this == bottom) && (this->next() == top))); |
| } |
| |
| |
| SkScalar Vertex::compare(const SkPoint &pt) const { |
| SkPoint ve = next()->point() - point(), |
| vp = pt - point(); |
| SkScalar d; |
| if (ve.fY == 0) { |
| // Return twice the distance to the center of the horizontal edge. |
| d = ve.fX + pt.fX - next()->point().fX; |
| } else { |
| // Return the distance to the edge, scaled by the edge length. |
| d = SkPoint::CrossProduct(ve, vp); |
| if (ve.fY > 0) d = -d; |
| } |
| return d; |
| } |
| |
| |
| SkScalar Trapezoid::compare(const SkPoint &pt) const { |
| SkScalar d = left()->compare(pt); |
| if (d <= 0) |
| return d; // Left of the left edge. |
| d = right()->compare(pt); |
| if (d >= 0) |
| return d; // Right of the right edge. |
| return 0; // Somewhere between the left and the right edges. |
| } |
| |
| |
| |
| SkScalar Trapezoid::compare(const Trapezoid &t1) const { |
| #if 1 |
| SkScalar d = left()->compare(t1.left()->point()); |
| if (d == 0) |
| d = right()->compare(t1.right()->point()); |
| return d; |
| #else |
| SkScalar dl = left()->compare( t1.left()->point()), |
| dr = right()->compare(t1.right()->point()); |
| if (dl < 0 && dr < 0) |
| return dr; |
| if (dl > 0 && dr > 0) |
| return dl; |
| return 0; |
| #endif |
| } |
| |
| |
| Trapezoid* ActiveTrapezoids::getTrapezoidWithEdge(const Vertex *edge) { |
| DebugPrintf("Entering getTrapezoidWithEdge(): looking through %d\n", |
| fTrapezoids.count()); |
| DebugPrintf("trying to find %p: ", edge); |
| Trapezoid **tp; |
| for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) { |
| SkASSERT(tp != NULL); |
| SkASSERT(*tp != NULL); |
| DebugPrintf("%p and %p, ", (**tp).left(), (**tp).right()); |
| if ((**tp).left() == edge || (**tp).right() == edge) { |
| DebugPrintf("\ngetTrapezoidWithEdge found the trapezoid\n"); |
| return *tp; |
| } |
| } |
| DebugPrintf("getTrapezoidWithEdge found no trapezoid\n"); |
| return NULL; |
| } |
| |
| |
| bool ActiveTrapezoids::insertNewTrapezoid(Vertex *vt, |
| Vertex *left, |
| Vertex *right) { |
| DebugPrintf("Inserting a trapezoid..."); |
| if (vt->fTrap0.left() == NULL && vt->fTrap0.right() == NULL) { |
| vt->fTrap0.setLeft(left); |
| vt->fTrap0.setRight(right); |
| insert(&vt->fTrap0); |
| } else if (vt->fTrap1.left() == NULL && vt->fTrap1.right() == NULL) { |
| DebugPrintf("a second one..."); |
| vt->fTrap1.setLeft(left); |
| vt->fTrap1.setRight(right); |
| if (vt->fTrap1 < vt->fTrap0) { // Keep trapezoids sorted. |
| remove(&vt->fTrap0); |
| Trapezoid t = vt->fTrap0; |
| vt->fTrap0 = vt->fTrap1; |
| vt->fTrap1 = t; |
| insert(&vt->fTrap0); |
| } |
| insert(&vt->fTrap1); |
| } else { |
| FailureMessage("More than 2 trapezoids requested for a vertex\n"); |
| return false; |
| } |
| DebugPrintf(" done. %d incomplete trapezoids\n", fTrapezoids.count()); |
| return true; |
| } |
| |
| |
| void ActiveTrapezoids::insert(Trapezoid *t) { |
| Trapezoid **tp; |
| for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) |
| if (**tp > *t) |
| break; |
| fTrapezoids.insert(tp - fTrapezoids.begin(), 1, &t); |
| // SHOULD VERIFY THAT ALL TRAPEZOIDS ARE PROPERLY SORTED |
| } |
| |
| |
| void ActiveTrapezoids::remove(Trapezoid *t) { |
| DebugPrintf("Removing a trapezoid..."); |
| for (Trapezoid **tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) { |
| if (*tp == t) { |
| fTrapezoids.remove(tp - fTrapezoids.begin()); |
| DebugPrintf(" done.\n"); |
| return; |
| } |
| } |
| DebugPrintf(" Arghh! Panic!\n"); |
| SkASSERT(t == 0); // Cannot find t in active trapezoid list. |
| } |
| |
| |
| bool ActiveTrapezoids::withinActiveTrapezoid(const SkPoint &pt, |
| Trapezoid **trap) { |
| DebugPrintf("Entering withinActiveTrapezoid()\n"); |
| // This is where a good search data structure would be helpful. |
| Trapezoid **t; |
| for (t = fTrapezoids.begin(); t < fTrapezoids.end(); ++t) { |
| if ((**t).left()->compare(pt) <= 0) { |
| // The point is to the left of the left edge of this trapezoid. |
| DebugPrintf("withinActiveTrapezoid: Before a trapezoid\n"); |
| *trap = *t; // Return the place where a new trapezoid would go. |
| // We have a bug with the sorting -- look through everything. |
| continue; |
| // return false; // Outside all trapezoids, since they are sorted. |
| } |
| // The point is to the right of the left edge of this trapezoid. |
| |
| if ((**t).right()->compare(pt) < 0) { |
| // The point is to the left of the right edge. |
| DebugPrintf("withinActiveTrapezoid: Within an Active Trapezoid\n"); |
| *trap = *t; |
| return true; |
| } |
| } |
| |
| // The point is to the right of all trapezoids. |
| DebugPrintf("withinActiveTrapezoid: After all trapezoids\n"); |
| *trap = NULL; |
| return false; |
| } |
| |
| |
| Vertex* Vertex::diagonal() { |
| Vertex *diag = NULL; |
| if (fTrap0.bottom() != NULL) { |
| if (!fTrap0.left() ->shareEdge(this, fTrap0.bottom()) && |
| !fTrap0.right()->shareEdge(this, fTrap0.bottom()) |
| ) { |
| diag = fTrap0.bottom(); |
| fTrap0 = fTrap1; |
| fTrap1.nullify(); |
| } else if (fTrap1.bottom() != NULL && |
| !fTrap1.left() ->shareEdge(this, fTrap1.bottom()) && |
| !fTrap1.right()->shareEdge(this, fTrap1.bottom()) |
| ) { |
| diag = fTrap1.bottom(); |
| fTrap1.nullify(); |
| } |
| } |
| return diag; |
| } |
| |
| |
| // We use this to sort the edges vertically for a scan-conversion type of |
| // operation. |
| class VertexPtr { |
| public: |
| Vertex *vt; |
| }; |
| |
| |
| bool operator<(VertexPtr &v0, VertexPtr &v1) { |
| // DebugPrintf("< %p %p\n", &v0, &v1); |
| if (v0.vt->point().fY < v1.vt->point().fY) return true; |
| if (v0.vt->point().fY > v1.vt->point().fY) return false; |
| if (v0.vt->point().fX < v1.vt->point().fX) return true; |
| else return false; |
| } |
| |
| |
| bool operator>(VertexPtr &v0, VertexPtr &v1) { |
| // DebugPrintf("> %p %p\n", &v0, &v1); |
| if (v0.vt->point().fY > v1.vt->point().fY) return true; |
| if (v0.vt->point().fY < v1.vt->point().fY) return false; |
| if (v0.vt->point().fX > v1.vt->point().fX) return true; |
| else return false; |
| } |
| |
| |
| static void SetVertexPoints(size_t numPts, const SkPoint *pt, Vertex *vt) { |
| for (; numPts-- != 0; ++pt, ++vt) |
| vt->setPoint(*pt); |
| } |
| |
| |
| static void InitializeVertexTopology(size_t numPts, Vertex *v1) { |
| Vertex *v0 = v1 + numPts - 1, *v_1 = v0 - 1; |
| for (; numPts-- != 0; v_1 = v0, v0 = v1++) { |
| v0->setPrev(v_1); |
| v0->setNext(v1); |
| } |
| } |
| |
| Vertex::VertexType Vertex::classify(Vertex **e0, Vertex **e1) { |
| VertexType type; |
| SkPoint vPrev, vNext; |
| vPrev.fX = prev()->point().fX - point().fX; |
| vPrev.fY = prev()->point().fY - point().fY; |
| vNext.fX = next()->point().fX - point().fX; |
| vNext.fY = next()->point().fY - point().fY; |
| |
| // This can probably be simplified, but there are enough potential bugs, |
| // we will leave it expanded until all cases are tested appropriately. |
| if (vPrev.fY < 0) { |
| if (vNext.fY > 0) { |
| // Prev comes from above, Next goes below. |
| type = MONOTONE; |
| *e0 = prev(); |
| *e1 = this; |
| } else if (vNext.fY < 0) { |
| // The are both above: sort so that e0 is on the left. |
| type = CONCAVE; |
| if (SkPoint::CrossProduct(vPrev, vNext) <= 0) { |
| *e0 = this; |
| *e1 = prev(); |
| } else { |
| *e0 = prev(); |
| *e1 = this; |
| } |
| } else { |
| DebugPrintf("### py < 0, ny = 0\n"); |
| if (vNext.fX < 0) { |
| type = CONCAVE; |
| *e0 = this; // flat to the left |
| *e1 = prev(); // concave on the right |
| } else { |
| type = CONCAVE; |
| *e0 = prev(); // concave to the left |
| *e1 = this; // flat to the right |
| } |
| } |
| } else if (vPrev.fY > 0) { |
| if (vNext.fY < 0) { |
| // Next comes from above, Prev goes below. |
| type = MONOTONE; |
| *e0 = this; |
| *e1 = prev(); |
| } else if (vNext.fY > 0) { |
| // They are both below: sort so that e0 is on the left. |
| type = CONVEX; |
| if (SkPoint::CrossProduct(vPrev, vNext) <= 0) { |
| *e0 = prev(); |
| *e1 = this; |
| } else { |
| *e0 = this; |
| *e1 = prev(); |
| } |
| } else { |
| DebugPrintf("### py > 0, ny = 0\n"); |
| if (vNext.fX < 0) { |
| type = MONOTONE; |
| *e0 = this; // flat to the left |
| *e1 = prev(); // convex on the right - try monotone first |
| } else { |
| type = MONOTONE; |
| *e0 = prev(); // convex to the left - try monotone first |
| *e1 = this; // flat to the right |
| } |
| } |
| } else { // vPrev.fY == 0 |
| if (vNext.fY < 0) { |
| DebugPrintf("### py = 0, ny < 0\n"); |
| if (vPrev.fX < 0) { |
| type = CONCAVE; |
| *e0 = prev(); // flat to the left |
| *e1 = this; // concave on the right |
| } else { |
| type = CONCAVE; |
| *e0 = this; // concave on the left - defer |
| *e1 = prev(); // flat to the right |
| } |
| } else if (vNext.fY > 0) { |
| DebugPrintf("### py = 0, ny > 0\n"); |
| if (vPrev.fX < 0) { |
| type = MONOTONE; |
| *e0 = prev(); // flat to the left |
| *e1 = this; // convex on the right - try monotone first |
| } else { |
| type = MONOTONE; |
| *e0 = this; // convex to the left - try monotone first |
| *e1 = prev(); // flat to the right |
| } |
| } else { |
| DebugPrintf("### py = 0, ny = 0\n"); |
| // First we try concave, then monotone, then convex. |
| if (vPrev.fX <= vNext.fX) { |
| type = CONCAVE; |
| *e0 = prev(); // flat to the left |
| *e1 = this; // flat to the right |
| } else { |
| type = CONCAVE; |
| *e0 = this; // flat to the left |
| *e1 = prev(); // flat to the right |
| } |
| } |
| } |
| return type; |
| } |
| |
| |
| #ifdef DEBUG |
| static const char* GetVertexTypeString(Vertex::VertexType type) { |
| const char *typeStr = NULL; |
| switch (type) { |
| case Vertex::MONOTONE: |
| typeStr = "MONOTONE"; |
| break; |
| case Vertex::CONCAVE: |
| typeStr = "CONCAVE"; |
| break; |
| case Vertex::CONVEX: |
| typeStr = "CONVEX"; |
| break; |
| } |
| return typeStr; |
| } |
| |
| |
| static void PrintVertices(size_t numPts, Vertex *vt) { |
| DebugPrintf("\nVertices:\n"); |
| for (size_t i = 0; i < numPts; i++) { |
| Vertex *e0, *e1; |
| Vertex::VertexType type = vt[i].classify(&e0, &e1); |
| DebugPrintf("%2d: (%.7g, %.7g), prev(%d), next(%d), " |
| "type(%s), left(%d), right(%d)", |
| i, vt[i].point().fX, vt[i].point().fY, |
| vt[i].prev() - vt, vt[i].next() - vt, |
| GetVertexTypeString(type), e0 - vt, e1 - vt); |
| Trapezoid *trap[2]; |
| vt[i].trapezoids(trap, trap+1); |
| for (int j = 0; j < 2; ++j) { |
| if (trap[j] != NULL) { |
| DebugPrintf(", trap(L=%d, R=%d, B=%d)", |
| trap[j]->left() - vt, |
| trap[j]->right() - vt, |
| trap[j]->bottom() - vt); |
| } |
| } |
| DebugPrintf("\n"); |
| } |
| } |
| |
| |
| static void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) { |
| DebugPrintf("\nSorted Vertices:\n"); |
| for (size_t i = 0; i < numPts; i++) { |
| Vertex *e0, *e1; |
| Vertex *vt = vp[i].vt; |
| Vertex::VertexType type = vt->classify(&e0, &e1); |
| DebugPrintf("%2d: %2d: (%.7g, %.7g), prev(%d), next(%d), " |
| "type(%s), left(%d), right(%d)", |
| i, vt - vtBase, vt->point().fX, vt->point().fY, |
| vt->prev() - vtBase, vt->next() - vtBase, |
| GetVertexTypeString(type), e0 - vtBase, e1 - vtBase); |
| Trapezoid *trap[2]; |
| vt->trapezoids(trap, trap+1); |
| for (int j = 0; j < 2; ++j) { |
| if (trap[j] != NULL) { |
| DebugPrintf(", trap(L=%d, R=%d, B=%d)", |
| trap[j]->left() - vtBase, |
| trap[j]->right() - vtBase, |
| trap[j]->bottom() - vtBase); |
| } |
| } |
| DebugPrintf("\n"); |
| } |
| } |
| #else // !DEBUG |
| inline void PrintVertices(size_t numPts, Vertex *vt) {} |
| inline void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {} |
| #endif // !DEBUG |
| |
| |
| // SkTHeapSort is broken, so we use a bubble sort in the meantime. |
| template <typename T> |
| void BubbleSort(T *array, size_t count) { |
| bool sorted; |
| size_t count_1 = count - 1; |
| do { |
| sorted = true; |
| for (size_t i = 0; i < count_1; ++i) { |
| if (array[i + 1] < array[i]) { |
| T t = array[i]; |
| array[i] = array[i + 1]; |
| array[i + 1] = t; |
| sorted = false; |
| } |
| } |
| } while (!sorted); |
| } |
| |
| |
| // Remove zero-height trapezoids. |
| static void RemoveDegenerateTrapezoids(size_t numVt, Vertex *vt) { |
| for (; numVt-- != 0; vt++) { |
| Trapezoid *traps[2]; |
| vt->trapezoids(traps, traps+1); |
| if (traps[1] != NULL && |
| vt->point().fY >= traps[1]->bottom()->point().fY) { |
| traps[1]->nullify(); |
| traps[1] = NULL; |
| } |
| if (traps[0] != NULL && |
| vt->point().fY >= traps[0]->bottom()->point().fY) { |
| if (traps[1] != NULL) { |
| *traps[0] = *traps[1]; |
| traps[1]->nullify(); |
| } else { |
| traps[0]->nullify(); |
| } |
| } |
| } |
| } |
| |
| |
| // Enhance the polygon with trapezoids. |
| bool ConvertPointsToVertices(size_t numPts, const SkPoint *pts, Vertex *vta) { |
| DebugPrintf("ConvertPointsToVertices()\n"); |
| |
| // Clear everything. |
| DebugPrintf("Zeroing vertices\n"); |
| sk_bzero(vta, numPts * sizeof(*vta)); |
| |
| // Initialize vertices. |
| DebugPrintf("Initializing vertices\n"); |
| SetVertexPoints(numPts, pts, vta); |
| InitializeVertexTopology(numPts, vta); |
| |
| PrintVertices(numPts, vta); |
| |
| SkTDArray<VertexPtr> vtptr; |
| vtptr.setCount(numPts); |
| for (int i = numPts; i-- != 0;) |
| vtptr[i].vt = vta + i; |
| PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); |
| DebugPrintf("Sorting vertrap ptr array [%d] %p %p\n", vtptr.count(), |
| &vtptr[0], &vtptr[vtptr.count() - 1] |
| ); |
| // SkTHeapSort(vtptr.begin(), vtptr.count()); |
| BubbleSort(vtptr.begin(), vtptr.count()); |
| DebugPrintf("Done sorting\n"); |
| PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); |
| |
| DebugPrintf("Traversing sorted vertrap ptrs\n"); |
| ActiveTrapezoids incompleteTrapezoids; |
| for (VertexPtr *vtpp = vtptr.begin(); vtpp < vtptr.end(); ++vtpp) { |
| DebugPrintf("%d: sorted vertrap %d\n", |
| vtpp - vtptr.begin(), vtpp->vt - vta); |
| Vertex *vt = vtpp->vt; |
| Vertex *e0, *e1; |
| Trapezoid *t; |
| switch (vt->classify(&e0, &e1)) { |
| case Vertex::MONOTONE: |
| monotone: |
| DebugPrintf("MONOTONE %d %d\n", e0 - vta, e1 - vta); |
| // We should find one edge. |
| t = incompleteTrapezoids.getTrapezoidWithEdge(e0); |
| if (t == NULL) { // One of the edges is flat. |
| DebugPrintf("Monotone: cannot find a trapezoid with e0: " |
| "trying convex\n"); |
| goto convex; |
| } |
| t->setBottom(vt); // This trapezoid is now complete. |
| incompleteTrapezoids.remove(t); |
| |
| if (e0 == t->left()) // Replace the left edge. |
| incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right()); |
| else // Replace the right edge. |
| incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e1); |
| break; |
| |
| case Vertex::CONVEX: // Start of a new trapezoid. |
| convex: |
| // We don't expect to find any edges. |
| DebugPrintf("CONVEX %d %d\n", e0 - vta, e1 - vta); |
| if (incompleteTrapezoids.withinActiveTrapezoid( |
| vt->point(), &t)) { |
| // Complete trapezoid. |
| SkASSERT(t != NULL); |
| t->setBottom(vt); |
| incompleteTrapezoids.remove(t); |
| // Introduce two new trapezoids. |
| incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e0); |
| incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right()); |
| } else { |
| // Insert a new trapezoid. |
| incompleteTrapezoids.insertNewTrapezoid(vt, e0, e1); |
| } |
| break; |
| |
| case Vertex::CONCAVE: // End of a trapezoid. |
| DebugPrintf("CONCAVE %d %d\n", e0 - vta, e1 - vta); |
| // We should find two edges. |
| t = incompleteTrapezoids.getTrapezoidWithEdge(e0); |
| if (t == NULL) { |
| DebugPrintf("Concave: cannot find a trapezoid with e0: " |
| " trying monotone\n"); |
| goto monotone; |
| } |
| SkASSERT(t != NULL); |
| if (e0 == t->left() && e1 == t->right()) { |
| DebugPrintf( |
| "Concave edges belong to the same trapezoid.\n"); |
| // Edges belong to the same trapezoid. |
| // Complete trapezoid & transfer it from the active list. |
| t->setBottom(vt); |
| incompleteTrapezoids.remove(t); |
| } else { // Edges belong to different trapezoids |
| DebugPrintf( |
| "Concave edges belong to different trapezoids.\n"); |
| // Complete left and right trapezoids. |
| Trapezoid *s = incompleteTrapezoids.getTrapezoidWithEdge( |
| e1); |
| if (s == NULL) { |
| DebugPrintf( |
| "Concave: cannot find a trapezoid with e1: " |
| " trying monotone\n"); |
| goto monotone; |
| } |
| t->setBottom(vt); |
| s->setBottom(vt); |
| incompleteTrapezoids.remove(t); |
| incompleteTrapezoids.remove(s); |
| |
| // Merge the two trapezoids into one below this vertex. |
| incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), |
| s->right()); |
| } |
| break; |
| } |
| } |
| |
| RemoveDegenerateTrapezoids(numPts, vta); |
| |
| DebugPrintf("Done making trapezoids\n"); |
| PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta); |
| |
| size_t k = incompleteTrapezoids.count(); |
| if (k > 0) { |
| FailureMessage("%d incomplete trapezoids\n", k); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| inline void appendTriangleAtVertex(const Vertex *v, |
| SkTDArray<SkPoint> *triangles) { |
| SkPoint *p = triangles->append(3); |
| p[0] = v->prev()->point(); |
| p[1] = v->point(); |
| p[2] = v->next()->point(); |
| DebugPrintf( |
| "Appending triangle: { (%.7g, %.7g), (%.7g, %.7g), (%.7g, %.7g) }\n", |
| p[0].fX, p[0].fY, |
| p[1].fX, p[1].fY, |
| p[2].fX, p[2].fY |
| ); |
| } |
| |
| |
| static size_t CountVertices(const Vertex *first, const Vertex *last) { |
| DebugPrintf("Counting vertices: "); |
| size_t count = 1; |
| for (; first != last; first = first->next()) { |
| ++count; |
| SkASSERT(count <= kMaxCount); |
| if (count >= kMaxCount) { |
| FailureMessage("Vertices do not seem to be in a linked chain\n"); |
| break; |
| } |
| } |
| return count; |
| } |
| |
| |
| bool operator<(const SkPoint &p0, const SkPoint &p1) { |
| if (p0.fY < p1.fY) return true; |
| if (p0.fY > p1.fY) return false; |
| if (p0.fX < p1.fX) return true; |
| else return false; |
| } |
| |
| |
| static void PrintLinkedVertices(size_t n, Vertex *vertices) { |
| DebugPrintf("%d vertices:\n", n); |
| Vertex *v; |
| for (v = vertices; n-- != 0; v = v->next()) |
| DebugPrintf(" (%.7g, %.7g)\n", v->point().fX, v->point().fY); |
| if (v != vertices) |
| FailureMessage("Vertices are not in a linked chain\n"); |
| } |
| |
| |
| // Triangulate an unimonotone chain. |
| bool TriangulateMonotone(Vertex *first, Vertex *last, |
| SkTDArray<SkPoint> *triangles) { |
| DebugPrintf("TriangulateMonotone()\n"); |
| |
| size_t numVertices = CountVertices(first, last); |
| if (numVertices == kMaxCount) { |
| FailureMessage("Way too many vertices: %d:\n", numVertices); |
| PrintLinkedVertices(numVertices, first); |
| return false; |
| } |
| |
| Vertex *start = first; |
| // First find the point with the smallest Y. |
| DebugPrintf("TriangulateMonotone: finding bottom\n"); |
| int count = kMaxCount; // Maximum number of vertices. |
| for (Vertex *v = first->next(); v != first && count-- > 0; v = v->next()) |
| if (v->point() < start->point()) |
| start = v; |
| if (count <= 0) { |
| FailureMessage("TriangulateMonotone() was given disjoint chain\n"); |
| return false; // Something went wrong. |
| } |
| |
| // Start at the far end of the long edge. |
| if (start->prev()->point() < start->next()->point()) |
| start = start->next(); |
| |
| Vertex *current = start->next(); |
| while (numVertices >= 3) { |
| if (current->angleIsConvex()) { |
| DebugPrintf("Angle %p is convex\n", current); |
| // Print the vertices |
| PrintLinkedVertices(numVertices, start); |
| |
| appendTriangleAtVertex(current, triangles); |
| if (triangles->count() > kMaxCount * 3) { |
| FailureMessage("An extraordinarily large number of triangles " |
| "were generated\n"); |
| return false; |
| } |
| Vertex *save = current->prev(); |
| current->delink(); |
| current = (save == start || save == start->prev()) ? start->next() |
| : save; |
| --numVertices; |
| } else { |
| if (numVertices == 3) { |
| FailureMessage("Convexity error in TriangulateMonotone()\n"); |
| appendTriangleAtVertex(current, triangles); |
| return false; |
| } |
| DebugPrintf("Angle %p is concave\n", current); |
| current = current->next(); |
| } |
| } |
| return true; |
| } |
| |
| |
| // Split the polygon into sets of unimonotone chains, and eventually call |
| // TriangulateMonotone() to convert them into triangles. |
| bool Triangulate(Vertex *first, Vertex *last, SkTDArray<SkPoint> *triangles) { |
| DebugPrintf("Triangulate()\n"); |
| Vertex *currentVertex = first; |
| while (!currentVertex->done()) { |
| currentVertex->setDone(true); |
| Vertex *bottomVertex = currentVertex->diagonal(); |
| if (bottomVertex != NULL) { |
| Vertex *saveNext = currentVertex->next(); |
| Vertex *savePrev = bottomVertex->prev(); |
| currentVertex->setNext(bottomVertex); |
| bottomVertex->setPrev(currentVertex); |
| currentVertex->nullifyTrapezoid(); |
| bool success = Triangulate(bottomVertex, currentVertex, triangles); |
| currentVertex->setDone(false); |
| bottomVertex->setDone(false); |
| currentVertex->setNext(saveNext); |
| bottomVertex->setPrev(savePrev); |
| bottomVertex->setNext(currentVertex); |
| currentVertex->setPrev(bottomVertex); |
| return Triangulate(currentVertex, bottomVertex, triangles) |
| && success; |
| } else { |
| currentVertex = currentVertex->next(); |
| } |
| } |
| return TriangulateMonotone(first, last, triangles); |
| } |
| |
| |
| bool SkConcaveToTriangles(size_t numPts, |
| const SkPoint pts[], |
| SkTDArray<SkPoint> *triangles) { |
| DebugPrintf("SkConcaveToTriangles()\n"); |
| |
| SkTDArray<Vertex> vertices; |
| vertices.setCount(numPts); |
| if (!ConvertPointsToVertices(numPts, pts, vertices.begin())) |
| return false; |
| |
| triangles->setReserve(numPts); |
| triangles->setCount(0); |
| return Triangulate(vertices.begin(), vertices.end() - 1, triangles); |
| } |