blob: fa718e50960a81b1f3a0f90fe8d4c3df8d99602d [file] [log] [blame]
/*
* 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);
}