aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/Intersection/Simplify.cpp
diff options
context:
space:
mode:
authorGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-02 20:27:02 +0000
committerGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-02 20:27:02 +0000
commit8dcf114db9762c02d217beba6e29dffa4e92d298 (patch)
tree3d55d0135db941b88684a11cd794498b1777fb94 /experimental/Intersection/Simplify.cpp
parent0985c558fc7e014f5680a7532270c6cbad2bca6a (diff)
shape ops work in progress
M Intersection/DataTypes.cpp M Intersection/QuadraticIntersection_Test.cpp M Intersection/EdgeWalker.cpp M Intersection/LineQuadraticIntersection_Test.cpp M Intersection/LineIntersection_Test.cpp M Intersection/LineIntersection.cpp D Intersection/edge.xcodeproj M Intersection/SimplifyFindTop_Test.cpp M Intersection/DataTypes.h A Intersection/SimplifyRect4x4_Test.cpp M Intersection/CubicIntersection_Test.cpp M Intersection/QuadraticUtilities.h M Intersection/LineCubicIntersection_Test.cpp A Intersection/CurveUtilities.h M Intersection/QuadraticBezierClip.cpp M Intersection/QuadraticBounds.cpp M Intersection/LineUtilities.h M Intersection/Intersection_Tests.cpp M Intersection/Simplify.cpp M Intersection/EdgeWalker_TestUtility.cpp M Intersection/QuadraticUtilities.cpp M Intersection/thingsToDo.txt M Intersection/LineUtilities.cpp M Intersection/CubicUtilities.h M Intersection/SimplifyFindNext_Test.cpp M Intersection/Intersection_Tests.h M Intersection/CubicBezierClip.cpp M Intersection/ActiveEdge_Test.cpp M Intersection/CubicBounds.cpp M Intersection/Simplify.h M Intersection/SimplifyNew_Test.cpp M Intersection/EdgeWalker_Test.h M Intersection/CubicUtilities.cpp M Intersection/op.htm M Intersection/ConvexHull.cpp D Intersection/RectUtilities.cpp M Intersection/SimplifyAddIntersectingTs_Test.cpp git-svn-id: http://skia.googlecode.com/svn/trunk@4429 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'experimental/Intersection/Simplify.cpp')
-rw-r--r--experimental/Intersection/Simplify.cpp1391
1 files changed, 954 insertions, 437 deletions
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index b57acf341d..23a0a3dc62 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -25,9 +25,12 @@
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_BRIDGE 0
+#define DEBUG_CROSS 0
#define DEBUG_DUMP 0
#define DEBUG_PATH_CONSTRUCTION 0
+#define DEBUG_WINDING 0
#define DEBUG_UNUSED 0 // set to expose unused functions
+#define DEBUG_MARK_DONE 0
#else
@@ -35,9 +38,12 @@
#define DEBUG_ADD_INTERSECTING_TS 0
#define DEBUG_BRIDGE 1
+#define DEBUG_CROSS 1
#define DEBUG_DUMP 1
#define DEBUG_PATH_CONSTRUCTION 1
+#define DEBUG_WINDING 0
#define DEBUG_UNUSED 0 // set to expose unused functions
+#define DEBUG_MARK_DONE 01
#endif
@@ -48,6 +54,10 @@ static int gContourID;
static int gSegmentID;
#endif
+#ifndef DEBUG_TEST
+#define DEBUG_TEST 0
+#endif
+
static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
Intersections& intersections) {
const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
@@ -95,24 +105,12 @@ static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
return horizontalIntersect(aLine, left, right, y, flipped, intersections);
}
-static int VLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
- SkScalar y, bool flipped, Intersections& intersections) {
- const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
- return verticalIntersect(aLine, left, right, y, flipped, intersections);
-}
-
static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
SkScalar y, bool flipped, Intersections& intersections) {
const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
}
-static int VQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
- SkScalar y, bool flipped, Intersections& intersections) {
- const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
- return verticalIntersect(aQuad, left, right, y, flipped, intersections);
-}
-
static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
SkScalar y, bool flipped, Intersections& intersections) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
@@ -120,13 +118,33 @@ static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
}
-static int VCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
- SkScalar y, bool flipped, Intersections& intersections) {
+static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
+ SkScalar x, bool flipped, Intersections& intersections) {
+ const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+ return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
+}
+
+static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
+ SkScalar x, bool flipped, Intersections& intersections) {
+ const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+ return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
+}
+
+static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
+ SkScalar x, bool flipped, Intersections& intersections) {
const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
{a[3].fX, a[3].fY}};
- return verticalIntersect(aCubic, left, right, y, flipped, intersections);
+ return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
}
+static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
+ SkScalar , SkScalar , bool , Intersections& ) = {
+ NULL,
+ VLineIntersect,
+ VQuadIntersect,
+ VCubicIntersect
+};
+
static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
double x, y;
@@ -217,6 +235,32 @@ static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
CubicYAtT
};
+static SkScalar LineDXAtT(const SkPoint a[2], double ) {
+ return a[1].fX - a[0].fX;
+}
+
+static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
+ const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+ double x;
+ dxdy_at_t(quad, t, x, *(double*) 0);
+ return SkDoubleToScalar(x);
+}
+
+static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
+ const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+ {a[3].fX, a[3].fY}};
+ double x;
+ dxdy_at_t(cubic, t, x, *(double*) 0);
+ return SkDoubleToScalar(x);
+}
+
+static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
+ NULL,
+ LineDXAtT,
+ QuadDXAtT,
+ CubicDXAtT
+};
+
static void LineSubDivide(const SkPoint a[2], double startT, double endT,
SkPoint sub[2]) {
const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
@@ -416,6 +460,10 @@ public:
return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
}
+ bool cancels(const Angle& rh) const {
+ return fDx * rh.fDx < 0 || fDy * rh.fDy < 0;
+ }
+
int end() const {
return fEnd;
}
@@ -427,7 +475,7 @@ public:
// since all angles share a point, this needs to know which point
// is the common origin, i.e., whether the center is at pts[0] or pts[verb]
// practically, this should only be called by addAngle
- void set(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
+ void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
int start, int end) {
SkASSERT(start != end);
fSegment = segment;
@@ -498,18 +546,13 @@ public:
}
Segment* segment() const {
- return fSegment;
+ return const_cast<Segment*>(fSegment);
}
int sign() const {
return SkSign32(fStart - fEnd);
}
- bool slopeEquals(const Angle& rh) const {
- return fDx == rh.fDx && fDy == rh.fDy;
-
- }
-
int start() const {
return fStart;
}
@@ -521,7 +564,7 @@ private:
SkScalar fDDy;
SkScalar fDDDx;
SkScalar fDDDy;
- Segment* fSegment;
+ const Segment* fSegment;
int fStart;
int fEnd;
};
@@ -536,13 +579,32 @@ static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
QSort<Angle>(angleList.begin(), angleList.end() - 1);
}
-// Bounds, unlike Rect, does not consider a vertical line to be empty.
+// Bounds, unlike Rect, does not consider a line to be empty.
struct Bounds : public SkRect {
static bool Intersects(const Bounds& a, const Bounds& b) {
return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
a.fTop <= b.fBottom && b.fTop <= a.fBottom;
}
+ void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
+ if (left < fLeft) {
+ fLeft = left;
+ }
+ if (top < fTop) {
+ fTop = top;
+ }
+ if (right > fRight) {
+ fRight = right;
+ }
+ if (bottom > fBottom) {
+ fBottom = bottom;
+ }
+ }
+
+ void add(const Bounds& toAdd) {
+ add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
+ }
+
bool isEmpty() {
return fLeft > fRight || fTop > fBottom
|| fLeft == fRight && fTop == fBottom
@@ -570,14 +632,13 @@ struct Bounds : public SkRect {
};
struct Span {
- double fT;
Segment* fOther;
+ mutable SkPoint const* fPt; // lazily computed as needed
+ double fT;
double fOtherT; // value at fOther[fOtherIndex].fT
- mutable SkPoint const * fPt; // lazily computed as needed
int fOtherIndex; // can't be used during intersection
- int fWinding; // accumulated from contours surrounding this one
- // OPTIMIZATION: coincident needs only 2 bits (values are -1, 0, 1)
- int fCoincident; // -1 start of coincidence, 0 no coincidence, 1 end
+ int fWindSum; // accumulated from contours surrounding this one
+ int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
bool fDone; // if set, this span to next higher T has been processed
};
@@ -589,7 +650,26 @@ public:
#endif
}
- void addAngle(SkTDArray<Angle>& angles, int start, int end) {
+ SkScalar activeTop() const {
+ SkASSERT(!done());
+ int count = fTs.count();
+ SkScalar result = SK_ScalarMax;
+ bool lastDone = true;
+ for (int index = 0; index < count; ++index) {
+ bool done = fTs[index].fDone;
+ if (!done || !lastDone) {
+ SkScalar y = yAtT(index);
+ if (result > y) {
+ result = y;
+ }
+ }
+ lastDone = done;
+ }
+ SkASSERT(result < SK_ScalarMax);
+ return result;
+ }
+
+ void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
SkASSERT(start != end);
SkPoint edge[4];
(*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
@@ -603,31 +683,34 @@ public:
}
// FIXME: this needs to defer add for aligned consecutive line segments
- SkPoint addCurveTo(int start, int end, SkPath& path) {
+ SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
SkPoint edge[4];
+ // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
(*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
+ if (active) {
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
- kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
- if (fVerb > 1) {
- SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
- }
- if (fVerb > 2) {
- SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
- }
- SkDebugf("\n");
+ SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
+ kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
+ if (fVerb > 1) {
+ SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
+ }
+ if (fVerb > 2) {
+ SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
+ }
+ SkDebugf("\n");
#endif
- switch (fVerb) {
- case SkPath::kLine_Verb:
- path.lineTo(edge[1].fX, edge[1].fY);
- break;
- case SkPath::kQuad_Verb:
- path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
- break;
- case SkPath::kCubic_Verb:
- path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
- edge[3].fX, edge[3].fY);
- break;
+ switch (fVerb) {
+ case SkPath::kLine_Verb:
+ path.lineTo(edge[1].fX, edge[1].fY);
+ break;
+ case SkPath::kQuad_Verb:
+ path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
+ break;
+ case SkPath::kCubic_Verb:
+ path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
+ edge[3].fX, edge[3].fY);
+ break;
+ }
}
return edge[fVerb];
}
@@ -637,12 +720,14 @@ public:
fBounds.set(pts, 2);
}
- const SkPoint& addMoveTo(int tIndex, SkPath& path) {
- const SkPoint& pt = xyAtT(&fTs[tIndex]);
+ const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
+ const SkPoint& pt = xyAtT(tIndex);
+ if (active) {
#if DEBUG_PATH_CONSTRUCTION
- SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
+ SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
#endif
- path.moveTo(pt.fX, pt.fY);
+ path.moveTo(pt.fX, pt.fY);
+ }
return pt;
}
@@ -657,50 +742,233 @@ public:
init(pts, SkPath::kQuad_Verb);
fBounds.setQuadBounds(pts);
}
+
+ // Defer all coincident edge processing until
+ // after normal intersections have been computed
+
+// no need to be tricky; insert in normal T order
+// resolve overlapping ts when considering coincidence later
- // edges are sorted by T, then by coincidence
- int addT(double newT, Segment& other, int coincident) {
+ // add non-coincident intersection. Resulting edges are sorted in T.
+ int addT(double newT, Segment* other) {
// FIXME: in the pathological case where there is a ton of intercepts,
// binary search?
int insertedAt = -1;
- Span* span;
size_t tCount = fTs.count();
- for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
+ for (size_t index = 0; index < tCount; ++index) {
// OPTIMIZATION: if there are three or more identical Ts, then
// the fourth and following could be further insertion-sorted so
// that all the edges are clockwise or counterclockwise.
// This could later limit segment tests to the two adjacent
// neighbors, although it doesn't help with determining which
// circular direction to go in.
- if (newT < fTs[idx2].fT || (newT == fTs[idx2].fT &&
- coincident <= fTs[idx2].fCoincident)) {
- insertedAt = idx2;
- span = fTs.insert(idx2);
- goto finish;
+ if (newT < fTs[index].fT) {
+ insertedAt = index;
+ break;
}
}
- insertedAt = tCount;
- span = fTs.append();
-finish:
+ Span* span;
+ if (insertedAt >= 0) {
+ span = fTs.insert(insertedAt);
+ } else {
+ insertedAt = tCount;
+ span = fTs.append();
+ }
span->fT = newT;
- span->fOther = &other;
+ span->fOther = other;
span->fPt = NULL;
- span->fWinding = 0;
- if (span->fDone = newT == 1) {
+ span->fWindSum = SK_MinS32;
+ span->fWindValue = 1;
+ if ((span->fDone = newT == 1)) {
++fDoneSpans;
}
- span->fCoincident = coincident;
- fCoincident |= coincident; // OPTIMIZATION: ever used?
return insertedAt;
}
- void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) {
+ // set spans from start to end to decrement by one
+ // note this walks other backwards
+ // FIMXE: there's probably an edge case that can be constructed where
+ // two span in one segment are separated by float epsilon on one span but
+ // not the other, if one segment is very small. For this
+ // case the counts asserted below may or may not be enough to separate the
+ // spans. Even if the counts work out, what if the spanw aren't correctly
+ // sorted? It feels better in such a case to match the span's other span
+ // pointer since both coincident segments must contain the same spans.
+ void addTCancel(double startT, double endT, Segment& other,
+ double oStartT, double oEndT) {
+ SkASSERT(endT - startT >= FLT_EPSILON);
+ SkASSERT(oEndT - oStartT >= FLT_EPSILON);
+ int index = 0;
+ while (startT - fTs[index].fT >= FLT_EPSILON) {
+ ++index;
+ }
+ int oCount = other.fTs.count();
+ while (other.fTs[--oCount].fT - oEndT >= FLT_EPSILON)
+ ;
+ int oIndex = oCount;
+ while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
+ ;
+ Span* test = &fTs[index];
+ Span* oTest = &other.fTs[oIndex];
+ SkDEBUGCODE(int testWindValue = test->fWindValue);
+ SkDEBUGCODE(int oTestWindValue = oTest->fWindValue);
+ SkDEBUGCODE(int startIndex = index);
+ SkTDArray<double> outsideTs;
+ SkTDArray<double> oOutsideTs;
+ do {
+ bool decrement = test->fWindValue && oTest->fWindValue;
+ Span* end = test;
+ double startT = end->fT;
+ double oStartT = oTest->fT;
+ do {
+ SkASSERT(testWindValue == end->fWindValue);
+ if (decrement) {
+ if (--(end->fWindValue) == 0) {
+ end->fDone = true;
+ ++fDoneSpans;
+ *outsideTs.append() = end->fT;
+ *outsideTs.append() = oStartT;
+ }
+ }
+ end = &fTs[++index];
+ } while (end->fT - test->fT < FLT_EPSILON);
+ SkASSERT(oCount - oIndex == index - startIndex);
+ Span* oTestStart = oTest;
+ SkDEBUGCODE(oCount = oIndex);
+ do {
+ SkASSERT(oTestWindValue == oTestStart->fWindValue);
+ if (decrement) {
+ if (--(oTestStart->fWindValue) == 0) {
+ oTestStart->fDone = true;
+ ++other.fDoneSpans;
+ *oOutsideTs.append() = oTestStart->fT;
+ *oOutsideTs.append() = startT;
+ }
+ }
+ if (!oIndex) {
+ break;
+ }
+ oTestStart = &other.fTs[--oIndex];
+ } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
+ test = end;
+ oTest = oTestStart;
+ } while (test->fT < endT - FLT_EPSILON);
+ SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
+#if 0
+ if (!done() && outsideTs.count()) {
+ addTOutsides(outsideTs, &other, oStartT);
+ }
+ if (!other.done() && oOutsideTs.count()) {
+ other.addTOutsides(oOutsideTs, this, startT);
+ }
+#endif
+ }
+
+ // set spans from start to end to increment the greater by one and decrement
+ // the lesser
+ void addTCoincident(double startT, double endT, Segment& other,
+ double oStartT, double oEndT) {
+ SkASSERT(endT - startT >= FLT_EPSILON);
+ SkASSERT(oEndT - oStartT >= FLT_EPSILON);
+ int index = 0;
+ while (startT - fTs[index].fT >= FLT_EPSILON) {
+ ++index;
+ }
+ int oIndex = 0;
+ while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
+ ++oIndex;
+ }
+ Span* test = &fTs[index];
+ Span* oTest = &other.fTs[oIndex];
+ SkDEBUGCODE(int testWindValue = test->fWindValue);
+ SkDEBUGCODE(int oTestWindValue = oTest->fWindValue);
+ SkTDArray<double> outsideTs;
+ SkTDArray<double> oOutsideTs;
+ do {
+ bool decrementOther = test->fWindValue >= oTest->fWindValue;
+ Span* end = test;
+ double startT = end->fT;
+ double oStartT = oTest->fT;
+ do {
+ SkASSERT(testWindValue == end->fWindValue);
+ if (decrementOther) {
+ ++(end->fWindValue);
+ } else {
+ if (--(end->fWindValue) == 0) {
+ end->fDone = true;
+ ++fDoneSpans;
+ *outsideTs.append() = end->fT;
+ *outsideTs.append() = oStartT;
+ }
+ }
+ end = &fTs[++index];
+ } while (end->fT - test->fT < FLT_EPSILON);
+ Span* oEnd = oTest;
+ do {
+ SkASSERT(oTestWindValue == oEnd->fWindValue);
+ if (decrementOther) {
+ if (--(oEnd->fWindValue) == 0) {
+ oEnd->fDone = true;
+ ++other.fDoneSpans;
+ *oOutsideTs.append() = oEnd->fT;
+ *oOutsideTs.append() = startT;
+ }
+ } else {
+ ++(oEnd->fWindValue);
+ }
+ oEnd = &other.fTs[++oIndex];
+ } while (oEnd->fT - oTest->fT < FLT_EPSILON);
+ test = end;
+ oTest = oEnd;
+ } while (test->fT < endT - FLT_EPSILON);
+ SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
+ SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
+ if (!done() && outsideTs.count()) {
+ addTOutsides(outsideTs, &other, oEndT);
+ }
+ if (!other.done() && oOutsideTs.count()) {
+ other.addTOutsides(oOutsideTs, this, endT);
+ }
+ }
+
+ void addTOutsides(const SkTDArray<double>& outsideTs, Segment* other,
+ double otherEnd) {
+ int count = outsideTs.count();
+ double endT = 0;
+ int endSpan = 0;
+ for (int index = 0; index < count; index += 2) {
+ double t = outsideTs[index];
+ double otherT = outsideTs[index + 1];
+ if (t > 1 - FLT_EPSILON) {
+ return;
+ }
+ if (t - endT > FLT_EPSILON) {
+ endSpan = addTPair(t, other, otherT);
+ }
+ do {
+ endT = fTs[++endSpan].fT;
+ } while (endT - t < FLT_EPSILON);
+ }
+ addTPair(endT, other, otherEnd);
+ }
+
+ int addTPair(double t, Segment* other, double otherT) {
+ int insertedAt = addT(t, other);
+ int otherInsertedAt = other->addT(otherT, this);
+ addOtherT(insertedAt, otherT, otherInsertedAt);
+ other->addOtherT(otherInsertedAt, t, insertedAt);
+ return insertedAt;
+ }
+
+ void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
// add edge leading into junction
- addAngle(angles, end, start);
+ if (fTs[SkMin32(end, start)].fWindValue > 0) {
+ addAngle(angles, end, start);
+ }
// add edge leading away from junction
int step = SkSign32(end - start);
int tIndex = nextSpan(end, step);
- if (tIndex >= 0) {
+ if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
addAngle(angles, end, tIndex);
}
}
@@ -710,15 +978,14 @@ finish:
}
void buildAngles(int index, SkTDArray<Angle>& angles) const {
- SkASSERT(!done());
double referenceT = fTs[index].fT;
int lesser = index;
- while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
+ while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
buildAnglesInner(lesser, angles);
}
do {
buildAnglesInner(index, angles);
- } while (++index < fTs.count() && referenceT == fTs[index].fT);
+ } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
}
void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
@@ -731,15 +998,22 @@ finish:
int oIndex = span->fOtherIndex;
// if done == -1, prior span has already been processed
int step = 1;
- int next = other->nextSpanEnd(oIndex, step);
+ int next = other->nextSpan(oIndex, step);
if (next < 0) {
step = -step;
- next = other->nextSpanEnd(oIndex, step);
+ next = other->nextSpan(oIndex, step);
}
- oIndex = other->coincidentEnd(oIndex, -step);
// add candidate into and away from junction
other->addTwoAngles(next, oIndex, angles);
- }
+ }
+
+ // OPTIMIZATION: inefficient, refactor
+ bool cancels(const Segment& other) const {
+ SkTDArray<Angle> angles;
+ addAngle(angles, 0, fTs.count() - 1);
+ other.addAngle(angles, 0, other.fTs.count() - 1);
+ return angles[0].cancels(angles[1]);
+ }
// figure out if the segment's ascending T goes clockwise or not
// not enough context to write this as shown
@@ -752,45 +1026,41 @@ finish:
return false;
}
- static bool Coincident(const Angle* current, const Angle* next) {
- const Segment* segment = current->segment();
- const Span& span = segment->fTs[current->start()];
- if (!span.fCoincident) {
- return false;
- }
- const Segment* nextSegment = next->segment();
- const Span& nextSpan = nextSegment->fTs[next->start()];
- if (!nextSpan.fCoincident) {
- return false;
- }
- // use angle dx/dy instead of other since 3 or more may be coincident
- return current->slopeEquals(*next);
- }
-
- static bool CoincidentCancels(const Angle* current, const Angle* next) {
- return SkSign32(current->start() - current->end())
- != SkSign32(next->start() - next->end());
- }
-
- int coincidentEnd(int from, int step) const {
- double fromT = fTs[from].fT;
- int count = fTs.count();
- int to = from;
- while (step > 0 ? ++to < count : --to >= 0) {
- const Span& span = fTs[to];
- if (fromT != span.fT) {
- // FIXME: we assume that if the T changes, we don't care about
- // coincident -- but in nextSpan, we require that both the T
- // and actual loc change to represent a span. This asymettry may
- // be OK or may be trouble -- if trouble, probably will need to
- // detect coincidence earlier or sort differently
- break;
+ int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
+ int start = 0;
+ int bestT = -1;
+ SkScalar top = bounds().fTop;
+ SkScalar bottom = bounds().fBottom;
+ int end;
+ do {
+ end = nextSpan(start, 1);
+ SkPoint edge[4];
+ // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
+ // work with the original data directly
+ (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
+ // start here; intersect ray starting at basePt with edge
+ Intersections intersections;
+ int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
+ false, intersections);
+ if (pts == 0) {
+ continue;
}
- if (span.fCoincident == step) {
- return to;
+ if (pts > 1 && fVerb == SkPath::kLine_Verb) {
+ // if the intersection is edge on, wait for another one
+ continue;
}
- }
- return from;
+ SkASSERT(pts == 1); // FIXME: more code required to disambiguate
+ SkPoint pt;
+ double foundT = intersections.fT[0][0];
+ (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
+ if (bestY < pt.fY) {
+ bestY = pt.fY;
+ bestT = foundT < 1 ? start : end;
+ hitT = foundT;
+ }
+ start = end;
+ } while (fTs[end].fT != 1);
+ return bestT;
}
bool done() const {
@@ -798,37 +1068,46 @@ finish:
return fDoneSpans == fTs.count();
}
+ // so the span needs to contain the pairing info found here
+ // this should include the winding computed for the edge, and
+ // what edge it connects to, and whether it is discarded
+ // (maybe discarded == abs(winding) > 1) ?
+ // only need derivatives for duration of sorting, add a new struct
+ // for pairings, remove extra spans that have zero length and
+ // reference an unused other
+ // for coincident, the last span on the other may be marked done
+ // (always?)
+
+ // if loop is exhausted, contour may be closed.
+ // FIXME: pass in close point so we can check for closure
+
+ // given a segment, and a sense of where 'inside' is, return the next
+ // segment. If this segment has an intersection, or ends in multiple
+ // segments, find the mate that continues the outside.
+ // note that if there are multiples, but no coincidence, we can limit
+ // choices to connections in the correct direction
+
+ // mark found segments as done
+
// start is the index of the beginning T of this edge
// it is guaranteed to have an end which describes a non-zero length (?)
// winding -1 means ccw, 1 means cw
- // step is in/out -1 or 1
- // spanIndex is returned
+ // firstFind allows coincident edges to be treated differently
Segment* findNext(int winding, const int startIndex, const int endIndex,
- int& nextStart, int& nextEnd) {
+ int& nextStart, int& nextEnd, bool firstFind) {
SkASSERT(startIndex != endIndex);
int count = fTs.count();
SkASSERT(startIndex < endIndex ? startIndex < count - 1
: startIndex > 0);
-
int step = SkSign32(endIndex - startIndex);
- int end = nextSpanEnd(startIndex, step);
+ int end = nextSpan(startIndex, step);
SkASSERT(end >= 0);
-
- // preflight for coincidence -- if present, it may change winding
- // considerations and whether reversed edges can be followed
-
- // Discard opposing direction candidates if no coincidence was found.
Span* endSpan = &fTs[end];
Segment* other;
- if (isSimple(end, step)) {
+ if (isSimple(end)) {
// mark the smaller of startIndex, endIndex done, and all adjacent
// spans with the same T value (but not 'other' spans)
markDone(SkMin32(startIndex, endIndex), winding);
- // move in winding direction until edge in correct direction
- // balance wrong direction edges before finding correct one
- // this requres that the intersection is angularly sorted
- // for a single intersection, special case -- choose the opposite
- // edge that steps the same
other = endSpan->fOther;
nextStart = endSpan->fOtherIndex;
nextEnd = nextStart + step;
@@ -857,106 +1136,65 @@ finish:
}
}
// back up if prior edge is coincident with firstIndex
- int prior = firstIndex;
- do {
- if (--prior < 0) {
- prior = angleCount - 1;
- }
- SkASSERT(prior != angleIndex);
- if (!Coincident(sorted[prior], sorted[firstIndex])) {
- break;
- }
- firstIndex = prior;
- angle = sorted[prior];
- } while (true);
-
- // put some thought into handling coincident edges
- // 1) defer the initial moveTo/curveTo until we know that firstIndex
- // isn't coincident (although maybe findTop could tell us that)
- // 2) allow the below to mark and skip coincident pairs
- // 3) return something (null?) if all segments cancel each other out
- // 4) if coincident edges don't cancel, figure out which to return (follow)
-
+ // adjustFirst(sorted, firstIndex, winding, firstFind);
SkASSERT(firstIndex >= 0);
int startWinding = winding;
- int nextIndex = firstIndex;
- const Angle* nextAngle;
- Segment* nextSegment;
+ int nextIndex = firstIndex + 1;
+ int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+ const Angle* foundAngle = NULL;
+ // bool alreadyMarked = angle->segment()->fTs[SkMin32(angle->start(),
+ // angle->end())].fDone;
+ // iterate through the angle, and compute everyone's winding
+ bool firstEdge = true;
do {
- if (++nextIndex == angleCount) {
+ if (nextIndex == angleCount) {
nextIndex = 0;
}
- SkASSERT(nextIndex != firstIndex); // should never wrap around
- nextAngle = sorted[nextIndex];
+ const Angle* nextAngle = sorted[nextIndex];
int maxWinding = winding;
- winding -= nextAngle->sign();
- nextSegment = nextAngle->segment();
- if (nextSegment->done()) {
- if (!winding) {
- break;
- }
- continue;
- }
- bool pairCoincides = Coincident(angle, nextAngle);
- bool pairCancels = pairCoincides
- && CoincidentCancels(angle, nextAngle);
- if (pairCancels) {
- angle->segment()->markAndChaseCoincident(angle->start(),
- angle->end(), nextSegment);
- return NULL;
- }
+ Segment* nextSegment = nextAngle->segment();
+ int windValue = nextSegment->windValue(nextAngle);
+ SkASSERT(windValue > 0);
+ winding -= nextAngle->sign() * windValue;
+ firstEdge = false;
if (!winding) {
- break;
- }
- if (pairCoincides) {
- bool markNext = abs(maxWinding) < abs(winding);
- if (markNext) {
- nextSegment->markDone(SkMin32(nextAngle->start(),
- nextAngle->end()), winding);
- } else {
- angle->segment()->markDone(SkMin32(angle->start(),
- angle->end()), maxWinding);
+ if (!foundAngle) {
+ foundAngle = nextAngle;
}
+ goto doNext;
+ }
+ if (nextSegment->done()) {
+ goto doNext;
}
// if the winding is non-zero, nextAngle does not connect to
// current chain. If we haven't done so already, mark the angle
// as done, record the winding value, and mark connected unambiguous
// segments as well.
- else if (nextSegment->winding(nextAngle) == 0) {
+ if (nextSegment->winding(nextAngle) == SK_MinS32) {
if (abs(maxWinding) < abs(winding)) {
maxWinding = winding;
}
- nextSegment->markAndChaseWinding(nextAngle, maxWinding);
+ if (foundAngle) {
+ nextSegment->markAndChaseWinding(nextAngle, maxWinding);
+ } else {
+ nextSegment->markAndChaseDone(nextAngle, maxWinding);
+ }
}
- } while ((angle = nextAngle)); // always true
- markDone(SkMin32(startIndex, endIndex), startWinding);
- nextStart = nextAngle->start();
- nextEnd = nextAngle->end();
- return nextSegment;
+ doNext:
+ angle = nextAngle;
+ } while (++nextIndex != lastIndex);
+ // if (!alreadyMarked) {
+ sorted[firstIndex]->segment()->
+ markDone(SkMin32(startIndex, endIndex), startWinding);
+ // }
+ if (!foundAngle) {
+ return NULL;
+ }
+ nextStart = foundAngle->start();
+ nextEnd = foundAngle->end();
+ return foundAngle->segment();
}
-
- // so the span needs to contain the pairing info found here
- // this should include the winding computed for the edge, and
- // what edge it connects to, and whether it is discarded
- // (maybe discarded == abs(winding) > 1) ?
- // only need derivatives for duration of sorting, add a new struct
- // for pairings, remove extra spans that have zero length and
- // reference an unused other
- // for coincident, the last span on the other may be marked done
- // (always?)
-
- // if loop is exhausted, contour may be closed.
- // FIXME: pass in close point so we can check for closure
-
- // given a segment, and a sense of where 'inside' is, return the next
- // segment. If this segment has an intersection, or ends in multiple
- // segments, find the mate that continues the outside.
- // note that if there are multiples, but no coincidence, we can limit
- // choices to connections in the correct direction
-
- // mark found segments as done
-
// FIXME: this is tricky code; needs its own unit test
void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
int count = fTs.count();
@@ -985,7 +1223,7 @@ finish:
// so, the span from here to there is coincident.
for (int index = matchIndex + 1; index < count; ++index) {
Span* test = &fTs[index];
- if (test->fCoincident) {
+ if (test->fDone) {
continue;
}
Segment* tOther = test->fOther;
@@ -1007,7 +1245,7 @@ finish:
double moStartT, moEndT;
for (int moIndex = 0; moIndex < moCount; ++moIndex) {
Span& moSpan = mOther->fTs[moIndex];
- if (moSpan.fCoincident) {
+ if (moSpan.fDone) {
continue;
}
if (moSpan.fOther == this) {
@@ -1060,11 +1298,16 @@ finish:
|| !tOther->isLinear(toStart, toEnd)) {
continue;
}
- // FIXME: may need to resort if we depend on coincidence first, last
- mOther->fTs[moStart].fCoincident = -1;
- tOther->fTs[toStart].fCoincident = -1;
- mOther->fTs[moEnd].fCoincident = 1;
- tOther->fTs[toEnd].fCoincident = 1;
+ // FIXME: defer implementation until the rest works
+ // this may share code with regular coincident detection
+ SkASSERT(0);
+ #if 0
+ if (flipped) {
+ mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
+ } else {
+ mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
+ }
+ #endif
}
}
@@ -1077,10 +1320,10 @@ finish:
SkASSERT(!done());
int firstT;
int lastT;
- int firstCoinT;
SkPoint topPt;
topPt.fY = SK_ScalarMax;
int count = fTs.count();
+ // see if either end is not done since we want smaller Y of the pair
bool lastDone = true;
for (int index = 0; index < count; ++index) {
const Span& span = fTs[index];
@@ -1089,22 +1332,13 @@ finish:
if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
&& topPt.fX > intercept.fX)) {
topPt = intercept;
- firstT = lastT = firstCoinT = index;
+ firstT = lastT = index;
} else if (topPt == intercept) {
lastT = index;
- if (span.fCoincident) {
- firstCoinT = index;
- }
}
}
lastDone = span.fDone;
}
- // if there's only a pair of segments, go with the endpoint chosen above
- if (firstT == lastT) {
- tIndex = firstT;
- endIndex = firstT > 0 ? tIndex - 1 : tIndex + 1;
- return this;
- }
// sort the edges to find the leftmost
int step = 1;
int end = nextSpan(firstT, step);
@@ -1117,7 +1351,7 @@ finish:
// look for left-ness from tLeft to firstT (matching y of other)
SkTDArray<Angle> angles;
SkASSERT(firstT - end != 0);
- addTwoAngles(end, firstCoinT, angles);
+ addTwoAngles(end, firstT, angles);
buildAngles(firstT, angles);
SkTDArray<Angle*> sorted;
sortAngles(angles, sorted);
@@ -1150,93 +1384,27 @@ finish:
Span& oSpan = other->fTs[o];
if (oT == oSpan.fT && this == oSpan.fOther) {
iSpan.fOtherIndex = o;
+ break;
}
}
}
}
// OPTIMIZATION: uses tail recursion. Unwise?
- void innerCoincidentChase(int step, Segment* other) {
- // find other at index
- SkASSERT(!done());
- const Span* start = NULL;
- const Span* end = NULL;
- int index, startIndex, endIndex;
- int count = fTs.count();
- for (index = 0; index < count; ++index) {
- const Span& span = fTs[index];
- if (!span.fCoincident || span.fOther != other) {
- continue;
- }
- if (!start) {
- if (span.fDone) {
- continue;
- }
- startIndex = index;
- start = &span;
- } else {
- SkASSERT(!end);
- endIndex = index;
- end = &span;
- }
- }
- if (!end) {
+ void innerChaseDone(int index, int step, int winding) {
+ int end = nextSpan(index, step);
+ if (multipleSpans(end, step)) {
return;
}
- Segment* next;
- Segment* nextOther;
- if (step < 0) {
- next = start->fT == 0 ? NULL : this;
- nextOther = other->fTs[start->fOtherIndex].fT == 1 ? NULL : other;
- } else {
- next = end->fT == 1 ? NULL : this;
- nextOther = other->fTs[end->fOtherIndex].fT == 0 ? NULL : other;
- }
- SkASSERT(!next || !nextOther);
- for (index = 0; index < count; ++index) {
- const Span& span = fTs[index];
- if (span.fCoincident || span.fOther == other) {
- continue;
- }
- bool checkNext = !next && (step < 0 ? span.fT == 0
- && span.fOtherT == 1 : span.fT == 1 && span.fOtherT == 0);
- bool checkOther = !nextOther && (step < 0 ? span.fT == start->fT
- && span.fOtherT == 0 : span.fT == end->fT && span.fOtherT == 1);
- if (!checkNext && !checkOther) {
- continue;
- }
- Segment* oSegment = span.fOther;
- if (oSegment->done()) {
- continue;
- }
- int oCount = oSegment->fTs.count();
- for (int oIndex = 0; oIndex < oCount; ++oIndex) {
- const Span& oSpan = oSegment->fTs[oIndex];
- if (oSpan.fT > 0 && oSpan.fT < 1) {
- continue;
- }
- if (!oSpan.fCoincident) {
- continue;
- }
- if (checkNext && (oSpan.fT == 0 ^ step < 0)) {
- next = oSegment;
- checkNext = false;
- }
- if (checkOther && (oSpan.fT == 1 ^ step < 0)) {
- nextOther = oSegment;
- checkOther = false;
- }
- }
- }
- markDone(SkMin32(startIndex, endIndex), 0);
- other->markDone(SkMin32(start->fOtherIndex, end->fOtherIndex), 0);
- if (next && nextOther) {
- next->innerCoincidentChase(step, nextOther);
- }
+ const Span& endSpan = fTs[end];
+ Segment* other = endSpan.fOther;
+ index = endSpan.fOtherIndex;
+ int otherEnd = other->nextSpan(index, step);
+ other->innerChaseDone(index, step, winding);
+ other->markDone(SkMin32(index, otherEnd), winding);
}
- // OPTIMIZATION: uses tail recursion. Unwise?
- void innerChase(int index, int step, int winding) {
+ void innerChaseWinding(int index, int step, int winding) {
int end = nextSpan(index, step);
if (multipleSpans(end, step)) {
return;
@@ -1245,15 +1413,19 @@ finish:
Segment* other = endSpan.fOther;
index = endSpan.fOtherIndex;
int otherEnd = other->nextSpan(index, step);
- other->innerChase(index, step, winding);
- other->markDone(SkMin32(index, otherEnd), winding);
+ int min = SkMin32(index, otherEnd);
+ if (other->fTs[min].fWindSum != SK_MinS32) {
+ SkASSERT(other->fTs[index].fWindSum == winding);
+ return;
+ }
+ other->innerChaseWinding(index, step, winding);
+ other->markWinding(min, winding);
}
void init(const SkPoint pts[], SkPath::Verb verb) {
fPts = pts;
fVerb = verb;
fDoneSpans = 0;
- fCoincident = 0;
}
bool intersected() const {
@@ -1276,27 +1448,19 @@ finish:
}
}
- bool isSimple(int index, int step) const {
+ bool isSimple(int end) const {
int count = fTs.count();
if (count == 2) {
return true;
}
- double spanT = fTs[index].fT;
- if (spanT > 0 && spanT < 1) {
- return false;
- }
- if (step < 0) {
- const Span& secondSpan = fTs[1];
- if (secondSpan.fT == 0) {
- return false;
- }
- return xyAtT(&fTs[0]) != xyAtT(&secondSpan);
+ double t = fTs[end].fT;
+ if (t < FLT_EPSILON) {
+ return fTs[1].fT >= FLT_EPSILON;
}
- const Span& penultimateSpan = fTs[count - 2];
- if (penultimateSpan.fT == 1) {
- return false;
+ if (t > 1 - FLT_EPSILON) {
+ return fTs[count - 2].fT <= 1 - FLT_EPSILON;
}
- return xyAtT(&fTs[count - 1]) != xyAtT(&penultimateSpan);
+ return false;
}
bool isHorizontal() const {
@@ -1311,51 +1475,101 @@ finish:
return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
}
- void markAndChaseCoincident(int index, int endIndex, Segment* other) {
- int step = SkSign32(endIndex - index);
- innerCoincidentChase(step, other);
- }
-
// this span is excluded by the winding rule -- chase the ends
// as long as they are unambiguous to mark connections as done
// and give them the same winding value
- void markAndChaseWinding(const Angle* angle, int winding) {
+ void markAndChaseDone(const Angle* angle, int winding) {
int index = angle->start();
int endIndex = angle->end();
int step = SkSign32(endIndex - index);
- innerChase(index, step, winding);
+ innerChaseDone(index, step, winding);
markDone(SkMin32(index, endIndex), winding);
}
+ void markAndChaseWinding(const Angle* angle, int winding) {
+ int index = angle->start();
+ int endIndex = angle->end();
+ int min = SkMin32(index, endIndex);
+ int step = SkSign32(endIndex - index);
+ innerChaseWinding(index, step, winding);
+ markWinding(min, winding);
+ }
+
// FIXME: this should also mark spans with equal (x,y)
+ // This may be called when the segment is already marked done. While this
+ // wastes time, it shouldn't do any more than spin through the T spans.
+ // OPTIMIZATION: abort on first done found (assuming that this code is
+ // always called to mark segments done).
void markDone(int index, int winding) {
- SkASSERT(!done());
+ // SkASSERT(!done());
double referenceT = fTs[index].fT;
int lesser = index;
- while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
+ while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
Span& span = fTs[lesser];
- // FIXME: this needs to assert that all are not done or one or more are
- // coincident
- // SkASSERT(!span.fDone || span.fCoincident);
if (span.fDone) {
continue;
}
+ #if DEBUG_MARK_DONE
+ const SkPoint& pt = xyAtT(&span);
+ SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
+ __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
+ #endif
span.fDone = true;
- SkASSERT(!span.fWinding || span.fWinding == winding);
- span.fWinding = winding;
+ SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ span.fWindSum = winding;
fDoneSpans++;
}
do {
Span& span = fTs[index];
- // SkASSERT(!span.fDone || span.fCoincident);
+ // SkASSERT(!span.fDone);
if (span.fDone) {
continue;
}
+ #if DEBUG_MARK_DONE
+ const SkPoint& pt = xyAtT(&span);
+ SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
+ __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
+ #endif
span.fDone = true;
- SkASSERT(!span.fWinding || span.fWinding == winding);
- span.fWinding = winding;
+ SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ span.fWindSum = winding;
fDoneSpans++;
- } while (++index < fTs.count() && referenceT == fTs[index].fT);
+ } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
+ }
+
+ void markWinding(int index, int winding) {
+ SkASSERT(!done());
+ double referenceT = fTs[index].fT;
+ int lesser = index;
+ while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
+ Span& span = fTs[lesser];
+ if (span.fDone) {
+ continue;
+ }
+ SkASSERT(span.fWindValue == 1 || winding == 0);
+ SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ #if DEBUG_MARK_DONE
+ const SkPoint& pt = xyAtT(&span);
+ SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
+ __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
+ #endif
+ span.fWindSum = winding;
+ }
+ do {
+ Span& span = fTs[index];
+ // SkASSERT(!span.fDone || span.fCoincident);
+ if (span.fDone) {
+ continue;
+ }
+ SkASSERT(span.fWindValue == 1 || winding == 0);
+ SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+ #if DEBUG_MARK_DONE
+ const SkPoint& pt = xyAtT(&span);
+ SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
+ __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
+ #endif
+ span.fWindSum = winding;
+ } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
}
bool multipleSpans(int end, int step) const {
@@ -1368,18 +1582,14 @@ finish:
// coincidence is found when the beginning Ts contain -step and the end
// contains step. When it is looking for the beginning of the next, the
// first Ts found can be ignored and the last Ts should contain -step.
+ // OPTIMIZATION: probably should split into two functions
int nextSpan(int from, int step) const {
const Span& fromSpan = fTs[from];
int count = fTs.count();
int to = from;
while (step > 0 ? ++to < count : --to >= 0) {
const Span& span = fTs[to];
- if (fromSpan.fT == span.fT) {
- continue;
- }
- const SkPoint& loc = xyAtT(&span);
- const SkPoint& fromLoc = xyAtT(&fromSpan);
- if (fromLoc == loc) {
+ if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
continue;
}
return to;
@@ -1387,16 +1597,6 @@ finish:
return -1;
}
- // once past current span, if step>0, look for coicident==1
- // if step<0, look for coincident==-1
- int nextSpanEnd(int from, int step) const {
- int result = nextSpan(from, step);
- if (result < 0) {
- return result;
- }
- return coincidentEnd(result, step);
- }
-
const SkPoint* pts() const {
return fPts;
}
@@ -1411,6 +1611,11 @@ finish:
const Span& span(int tIndex) const {
return fTs[tIndex];
}
+
+ int spanSign(int startIndex, int endIndex) const {
+ return startIndex < endIndex ? -fTs[startIndex].fWindValue :
+ fTs[endIndex].fWindValue;
+ }
// OPTIMIZATION: mark as debugging only if used solely by tests
double t(int tIndex) const {
@@ -1424,18 +1629,64 @@ finish:
SkPath::Verb verb() const {
return fVerb;
}
+
+ // if the only remaining spans are small, ignore them, and mark done
+ bool virtuallyDone() {
+ int count = fTs.count();
+ double previous = 0;
+ bool previousDone = fTs[0].fDone;
+ for (int index = 1; index < count; ++index) {
+ Span& span = fTs[index];
+ double t = span.fT;
+ if (t - previous < FLT_EPSILON) {
+ if (span.fDone && !previousDone) {
+ int prior = --index;
+ int winding = span.fWindSum;
+ do {
+ Span& priorSpan = fTs[prior];
+ priorSpan.fDone = true;
+ priorSpan.fWindSum = winding;
+ fDoneSpans++;
+ } while (--prior >= 0 && t - fTs[prior].fT < FLT_EPSILON);
+ }
+ } else if (!previousDone) {
+ return false;
+ }
+ previous = t;
+ previousDone = span.fDone;
+ }
+ SkASSERT(done());
+ return true;
+ }
+
+ int winding(int tIndex) const {
+ return fTs[tIndex].fWindSum;
+ }
- bool winding(const Angle* angle) {
+ int winding(const Angle* angle) const {
int start = angle->start();
int end = angle->end();
int index = SkMin32(start, end);
- Span& span = fTs[index];
- return span.fWinding;
+ return winding(index);
+ }
+
+ int windValue(int tIndex) const {
+ return fTs[tIndex].fWindValue;
+ }
+
+ int windValue(const Angle* angle) const {
+ int start = angle->start();
+ int end = angle->end();
+ int index = SkMin32(start, end);
+ return windValue(index);
+ }
+
+ SkScalar xAtT(const Span* span) const {
+ return xyAtT(span).fX;
}
- SkScalar xAtT(double t) const {
- SkASSERT(t >= 0 && t <= 1);
- return (*SegmentXAtT[fVerb])(fPts, t);
+ const SkPoint& xyAtT(int index) const {
+ return xyAtT(&fTs[index]);
}
const SkPoint& xyAtT(const Span* span) const {
@@ -1452,10 +1703,13 @@ finish:
}
return *span->fPt;
}
+
+ SkScalar yAtT(int index) const {
+ return yAtT(&fTs[index]);
+ }
- SkScalar yAtT(double t) const {
- SkASSERT(t >= 0 && t <= 1);
- return (*SegmentYAtT[fVerb])(fPts, t);
+ SkScalar yAtT(const Span* span) const {
+ return xyAtT(span).fY;
}
#if DEBUG_DUMP
@@ -1466,10 +1720,10 @@ finish:
SkPoint out;
(*SegmentXYAtT[fVerb])(fPts, t(i), &out);
SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
- " otherT=%1.9g winding=%d\n",
+ " otherT=%1.9g windSum=%d\n",
tab + sizeof(className), className, fID,
kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
- fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWinding);
+ fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
}
SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
tab + sizeof(className), className, fID,
@@ -1486,14 +1740,17 @@ private:
// be allocated as needed instead of always initialized -- though maybe
// the initialization is lightweight enough that it hardly matters
mutable SkTDArray<SkPoint> fIntersections;
- // FIXME: coincident only needs two bits (-1, 0, 1)
- int fCoincident; // non-zero if some coincident span inside
int fDoneSpans; // used for quick check that segment is finished
#if DEBUG_DUMP
int fID;
#endif
};
+struct Coincidence {
+ Segment* fSegments[2];
+ double fTs[2][2];
+};
+
class Contour {
public:
Contour() {
@@ -1509,6 +1766,26 @@ public:
: fBounds.fTop < rh.fBounds.fTop;
}
+ void addCoincident(int index, Contour* other, int otherIndex,
+ const Intersections& ts, bool swap) {
+ Coincidence& coincidence = *fCoincidences.append();
+ coincidence.fSegments[0] = &fSegments[index];
+ coincidence.fSegments[1] = &other->fSegments[otherIndex];
+ coincidence.fTs[swap][0] = ts.fT[0][0];
+ coincidence.fTs[swap][1] = ts.fT[0][1];
+ coincidence.fTs[!swap][0] = ts.fT[1][0];
+ coincidence.fTs[!swap][1] = ts.fT[1][1];
+ }
+
+ void addCross(const Contour* crosser) {
+#ifdef DEBUG_CROSS
+ for (int index = 0; index < fCrosses.count(); ++index) {
+ SkASSERT(fCrosses[index] != crosser);
+ }
+#endif
+ *fCrosses.append() = crosser;
+ }
+
void addCubic(const SkPoint pts[4]) {
fSegments.push_back().addCubic(pts);
fContainsCurves = true;
@@ -1518,6 +1795,10 @@ public:
fSegments.push_back().addLine(pts);
return fSegments.count();
}
+
+ void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
+ fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
+ }
int addQuad(const SkPoint pts[3]) {
fSegments.push_back().addQuad(pts);
@@ -1525,6 +1806,11 @@ public:
return fSegments.count();
}
+ int addT(int segIndex, double newT, Contour* other, int otherIndex) {
+ containsIntercepts();
+ return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
+ }
+
const Bounds& bounds() const {
return fBounds;
}
@@ -1538,6 +1824,48 @@ public:
fContainsIntercepts = true;
}
+ const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
+ int &tIndex, double& hitT) {
+ int segmentCount = fSegments.count();
+ const Segment* bestSegment = NULL;
+ for (int test = 0; test < segmentCount; ++test) {
+ Segment* testSegment = &fSegments[test];
+ const SkRect& bounds = testSegment->bounds();
+ if (bounds.fTop < bestY) {
+ continue;
+ }
+ if (bounds.fTop > basePt.fY) {
+ continue;
+ }
+ if (bounds.fLeft > basePt.fX) {
+ continue;
+ }
+ if (bounds.fRight < basePt.fX) {
+ continue;
+ }
+ double testHitT;
+ int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
+ if (testT >= 0) {
+ bestSegment = testSegment;
+ tIndex = testT;
+ hitT = testHitT;
+ }
+ }
+ return bestSegment;
+ }
+
+ bool crosses(const Contour* crosser) const {
+ if (this == crosser) {
+ return true;
+ }
+ for (int index = 0; index < fCrosses.count(); ++index) {
+ if (fCrosses[index] == crosser) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void findTooCloseToCall(int winding) {
int segmentCount = fSegments.count();
for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
@@ -1556,44 +1884,118 @@ public:
fSegments.reset();
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
fContainsCurves = fContainsIntercepts = false;
+ fWindingSum = -1;
}
+ void resolveCoincidence(int winding) {
+ int count = fCoincidences.count();
+ for (int index = 0; index < count; ++index) {
+ Coincidence& coincidence = fCoincidences[index];
+ Segment* thisOne = coincidence.fSegments[0];
+ Segment* other = coincidence.fSegments[1];
+ double startT = coincidence.fTs[0][0];
+ double endT = coincidence.fTs[0][1];
+ if (startT > endT) {
+ SkTSwap<double>(startT, endT);
+ }
+ SkASSERT(endT - startT >= FLT_EPSILON);
+ double oStartT = coincidence.fTs[1][0];
+ double oEndT = coincidence.fTs[1][1];
+ if (oStartT > oEndT) {
+ SkTSwap<double>(oStartT, oEndT);
+ }
+ SkASSERT(oEndT - oStartT >= FLT_EPSILON);
+ if (winding > 0 || thisOne->cancels(*other)) {
+ thisOne->addTCancel(startT, endT, *other, oStartT, oEndT);
+ } else {
+ thisOne->addTCoincident(startT, endT, *other, oStartT, oEndT);
+ }
+ }
+ }
+
+ const SkTArray<Segment>& segments() {
+ return fSegments;
+ }
+
+ void setWinding(int winding) {
+ SkASSERT(fWindingSum < 0);
+ fWindingSum = winding;
+ }
+
// OPTIMIZATION: feel pretty uneasy about this. It seems like once again
// we need to sort and walk edges in y, but that on the surface opens the
// same can of worms as before. But then, this is a rough sort based on
// segments' top, and not a true sort, so it could be ameniable to regular
// sorting instead of linear searching. Still feel like I'm missing something
- Segment* topSegment() {
+ Segment* topSegment(SkScalar& bestY) {
int segmentCount = fSegments.count();
SkASSERT(segmentCount > 0);
int best = -1;
Segment* bestSegment = NULL;
while (++best < segmentCount) {
Segment* testSegment = &fSegments[best];
+ #if 0 // FIXME: remove if not needed
+ if (testSegment->virtuallyDone()) {
+ continue;
+ }
+ #else
if (testSegment->done()) {
continue;
}
+ #endif
bestSegment = testSegment;
break;
}
if (!bestSegment) {
return NULL;
}
- SkScalar bestTop = bestSegment->bounds().fTop;
+ SkScalar bestTop = bestSegment->activeTop();
for (int test = best + 1; test < segmentCount; ++test) {
Segment* testSegment = &fSegments[test];
if (testSegment->done()) {
continue;
}
- SkScalar testTop = testSegment->bounds().fTop;
+ if (testSegment->bounds().fTop > bestTop) {
+ continue;
+ }
+ SkScalar testTop = testSegment->activeTop();
if (bestTop > testTop) {
bestTop = testTop;
bestSegment = testSegment;
}
}
+ bestY = bestTop;
return bestSegment;
}
+ int updateSegment(int index, const SkPoint* pts) {
+ Segment& segment = fSegments[index];
+ segment.updatePts(pts);
+ return segment.verb() + 1;
+ }
+
+ int winding() {
+ if (fWindingSum >= 0) {
+ return fWindingSum;
+ }
+ // check peers
+ int count = fCrosses.count();
+ for (int index = 0; index < count; ++index) {
+ const Contour* crosser = fCrosses[index];
+ if (0 <= crosser->fWindingSum) {
+ fWindingSum = crosser->fWindingSum;
+ break;
+ }
+ }
+ return fWindingSum;
+ }
+
+#if DEBUG_TEST
+ SkTArray<Segment>& debugSegments() {
+ return fSegments;
+ }
+#endif
+
#if DEBUG_DUMP
void dump() {
int i;
@@ -1627,17 +2029,18 @@ protected:
}
fBounds = fSegments.front().bounds();
for (int index = 1; index < count; ++index) {
- fBounds.growToInclude(fSegments[index].bounds());
+ fBounds.add(fSegments[index].bounds());
}
}
-public:
- SkTArray<Segment> fSegments; // not worth accessor functions?
-
private:
+ SkTArray<Segment> fSegments;
+ SkTDArray<Coincidence> fCoincidences;
+ SkTDArray<const Contour*> fCrosses;
Bounds fBounds;
bool fContainsIntercepts;
bool fContainsCurves;
+ int fWindingSum; // initial winding number outside
#if DEBUG_DUMP
int fID;
#endif
@@ -1661,7 +2064,7 @@ EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
protected:
void complete() {
- if (fCurrentContour && fCurrentContour->fSegments.count()) {
+ if (fCurrentContour && fCurrentContour->segments().count()) {
fCurrentContour->complete();
fCurrentContour = NULL;
}
@@ -1755,25 +2158,24 @@ void walk() {
SkASSERT(fCurrentContour);
}
complete();
- if (fCurrentContour && !fCurrentContour->fSegments.count()) {
+ if (fCurrentContour && !fCurrentContour->segments().count()) {
fContours.pop_back();
}
// correct pointers in contours since fReducePts may have moved as it grew
int cIndex = 0;
- fCurrentContour = &fContours[0];
int extraCount = fExtra.count();
- SkASSERT(fExtra[0] == -1);
+ SkASSERT(extraCount == 0 || fExtra[0] == -1);
int eIndex = 0;
int rIndex = 0;
while (++eIndex < extraCount) {
int offset = fExtra[eIndex];
if (offset < 0) {
- fCurrentContour = &fContours[++cIndex];
+ ++cIndex;
continue;
}
- Segment& segment = fCurrentContour->fSegments[offset - 1];
- segment.updatePts(&fReducePts[rIndex]);
- rIndex += segment.verb() + 1;
+ fCurrentContour = &fContours[cIndex];
+ rIndex += fCurrentContour->updateSegment(offset - 1,
+ &fReducePts[rIndex]);
}
fExtra.reset(); // we're done with this
}
@@ -1797,11 +2199,15 @@ public:
kQuad_Segment = SkPath::kQuad_Verb,
kCubic_Segment = SkPath::kCubic_Verb,
};
+
+ void addCoincident(Work& other, const Intersections& ts, bool swap) {
+ fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
+ }
// FIXME: does it make sense to write otherIndex now if we're going to
// fix it up later?
void addOtherT(int index, double otherT, int otherIndex) {
- fContour->fSegments[fIndex].addOtherT(index, otherT, otherIndex);
+ fContour->addOtherT(fIndex, index, otherT, otherIndex);
}
// Avoid collapsing t values that are close to the same since
@@ -1809,10 +2215,8 @@ public:
// be nearly equal, any problems caused by this should be taken care
// of later.
// On the edge or out of range values are negative; add 2 to get end
- int addT(double newT, const Work& other, int coincident) {
- fContour->containsIntercepts();
- return fContour->fSegments[fIndex].addT(newT,
- other.fContour->fSegments[other.fIndex], coincident);
+ int addT(double newT, const Work& other) {
+ return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
}
bool advance() {
@@ -1824,7 +2228,7 @@ public:
}
const Bounds& bounds() const {
- return fContour->fSegments[fIndex].bounds();
+ return fContour->segments()[fIndex].bounds();
}
const SkPoint* cubic() const {
@@ -1834,7 +2238,7 @@ public:
void init(Contour* contour) {
fContour = contour;
fIndex = 0;
- fLast = contour->fSegments.count();
+ fLast = contour->segments().count();
}
SkScalar left() const {
@@ -1852,7 +2256,7 @@ public:
}
const SkPoint* pts() const {
- return fContour->fSegments[fIndex].pts();
+ return fContour->segments()[fIndex].pts();
}
SkScalar right() const {
@@ -1864,7 +2268,7 @@ public:
}
SegmentType segmentType() const {
- const Segment& segment = fContour->fSegments[fIndex];
+ const Segment& segment = fContour->segments()[fIndex];
SegmentType type = (SegmentType) segment.verb();
if (type != kLine_Segment) {
return type;
@@ -1888,7 +2292,7 @@ public:
}
SkPath::Verb verb() const {
- return fContour->fSegments[fIndex].verb();
+ return fContour->segments()[fIndex].verb();
}
SkScalar x() const {
@@ -1904,7 +2308,7 @@ public:
}
bool yFlipped() const {
- return y() != pts()[0].fX;
+ return y() != pts()[0].fY;
}
protected:
@@ -1959,6 +2363,7 @@ static bool addIntersectTs(Contour* test, Contour* next) {
}
Work wt;
wt.init(test);
+ bool foundCommonContour = test == next;
do {
Work wn;
wn.init(next);
@@ -2116,70 +2521,155 @@ static bool addIntersectTs(Contour* test, Contour* next) {
default:
SkASSERT(0);
}
+ if (!foundCommonContour && pts > 0) {
+ test->addCross(next);
+ next->addCross(test);
+ foundCommonContour = true;
+ }
// in addition to recording T values, record matching segment
- int testCoin;
- int nextCoin;
if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
&& wt.segmentType() <= Work::kLine_Segment) {
- // pass coincident so that smaller T is -1, larger T is 1
- testCoin = ts.fT[swap][0] < ts.fT[swap][1] ? -1 : 1;
- nextCoin = ts.fT[!swap][0] < ts.fT[!swap][1] ? -1 : 1;
- } else {
- testCoin = nextCoin = 0;
+ wt.addCoincident(wn, ts, swap);
+ continue;
}
for (int pt = 0; pt < pts; ++pt) {
SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
- int testTAt = wt.addT(ts.fT[swap][pt], wn, testCoin);
- int nextTAt = wn.addT(ts.fT[!swap][pt], wt, nextCoin);
+ int testTAt = wt.addT(ts.fT[swap][pt], wn);
+ int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
- testCoin = -testCoin;
- nextCoin = -nextCoin;
}
} while (wn.advance());
} while (wt.advance());
return true;
}
+// resolve any coincident pairs found while intersecting, and
// see if coincidence is formed by clipping non-concident segments
static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
int contourCount = contourList.count();
for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
Contour* contour = contourList[cIndex];
+ contour->resolveCoincidence(winding);
+ }
+ for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+ Contour* contour = contourList[cIndex];
contour->findTooCloseToCall(winding);
}
}
+// project a ray from the top of the contour up and see if it hits anything
+// note: when we compute line intersections, we keep track of whether
+// two contours touch, so we need only look at contours not touching this one.
+// OPTIMIZATION: sort contourList vertically to avoid linear walk
+static int innerContourCheck(SkTDArray<Contour*>& contourList,
+ Contour* baseContour, const SkPoint& basePt) {
+ int contourCount = contourList.count();
+ int winding = 0;
+ SkScalar bestY = SK_ScalarMin;
+ for (int cTest = 0; cTest < contourCount; ++cTest) {
+ Contour* contour = contourList[cTest];
+ if (basePt.fY < contour->bounds().fTop) {
+ continue;
+ }
+ if (bestY > contour->bounds().fBottom) {
+ continue;
+ }
+ if (baseContour->crosses(contour)) {
+ continue;
+ }
+ int tIndex;
+ double tHit;
+ const Segment* test = contour->crossedSegment(basePt, bestY, tIndex,
+ tHit);
+ if (!test) {
+ continue;
+ }
+ // If the ray hit the end of a span, we need to construct the wheel of
+ // angles to find the span closest to the ray -- even if there are just
+ // two spokes on the wheel.
+ if (tHit == test->t(tIndex)) {
+ SkTDArray<Angle> angles;
+ int end = test->nextSpan(tIndex, 1);
+ if (end < 0) {
+ end = test->nextSpan(tIndex, -1);
+ }
+ test->addTwoAngles(tIndex, end, angles);
+ // test->buildAnglesInner(tIndex, angles);
+ test->buildAngles(tIndex, angles);
+ SkTDArray<Angle*> sorted;
+ sortAngles(angles, sorted);
+ const Angle* angle = sorted[0];
+ test = angle->segment();
+ SkScalar testDx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
+ if (testDx == 0) {
+ angle = *(sorted.end() - 1);
+ test = angle->segment();
+ SkASSERT((*SegmentDXAtT[test->verb()])(test->pts(), tHit) != 0);
+ }
+ tIndex = angle->start(); // lesser Y
+ winding = test->winding(SkMin32(tIndex, angle->end()));
+ #if DEBUG_WINDING
+ SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
+ #endif
+ } else {
+ winding = test->winding(tIndex);
+ #if DEBUG_WINDING
+ SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
+ #endif
+ }
+ // see if a + change in T results in a +/- change in X (compute x'(T))
+ SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
+ #if DEBUG_WINDING
+ SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
+ #endif
+ SkASSERT(dx != 0);
+ if (winding * dx > 0) { // if same signs, result is negative
+ winding += dx > 0 ? -1 : 1;
+ #if DEBUG_WINDING
+ SkDebugf("%s 3 winding=%d\n", __FUNCTION__, winding);
+ #endif
+ }
+ }
+ baseContour->setWinding(winding);
+ return winding;
+}
// OPTIMIZATION: not crazy about linear search here to find top active y.
// seems like we should break down and do the sort, or maybe sort each
// contours' segments?
// Once the segment array is built, there's no reason I can think of not to
// sort it in Y. hmmm
+// FIXME: return the contour found to pass to inner contour check
static Segment* findTopContour(SkTDArray<Contour*>& contourList,
- int contourCount) {
+ Contour*& topContour) {
+ int contourCount = contourList.count();
int cIndex = 0;
Segment* topStart;
+ SkScalar bestY = SK_ScalarMax;
+ Contour* contour;
do {
- Contour* topContour = contourList[cIndex];
- topStart = topContour->topSegment();
+ contour = contourList[cIndex];
+ topStart = contour->topSegment(bestY);
} while (!topStart && ++cIndex < contourCount);
if (!topStart) {
return NULL;
}
- SkScalar top = topStart->bounds().fTop;
- for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
- Contour* contour = contourList[cTest];
- if (top < contour->bounds().fTop) {
+ topContour = contour;
+ while (++cIndex < contourCount) {
+ contour = contourList[cIndex];
+ if (bestY < contour->bounds().fTop) {
continue;
}
- Segment* test = contour->topSegment();
- if (top > test->bounds().fTop) {
- cIndex = cTest;
- topStart = test;
- top = test->bounds().fTop;
+ SkScalar testY = SK_ScalarMax;
+ Segment* test = contour->topSegment(testY);
+ if (!test || bestY <= testY) {
+ continue;
}
+ topContour = contour;
+ topStart = test;
+ bestY = testY;
}
return topStart;
}
@@ -2194,36 +2684,65 @@ static Segment* findTopContour(SkTDArray<Contour*>& contourList,
// since we start with leftmost top edge, we'll traverse through a
// smaller angle counterclockwise to get to the next edge.
static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
- int contourCount = contourList.count();
+ // after findTopContour has already been called once, check if
+ // result of subsequent findTopContour has no winding set
+ bool firstContour = true;
do {
- Segment* topStart = findTopContour(contourList, contourCount);
+ Contour* topContour;
+ Segment* topStart = findTopContour(contourList, topContour);
if (!topStart) {
break;
}
- // FIXME: if this contour is inside others, winding will not be zero initially
- int winding = 0; // zero says there are no contours outside this one
// Start at the top. Above the top is outside, below is inside.
// follow edges to intersection by changing the index by direction.
int index, endIndex;
Segment* current = topStart->findTop(index, endIndex);
- winding += SkSign32(index - endIndex);
+ int winding;
+ int contourWinding;
+ if (firstContour) {
+ topContour->setWinding(0);
+ contourWinding = 0;
+ firstContour = false;
+ winding = 0;
+ } else {
+ winding = topContour->winding();
+ #if DEBUG_WINDING
+ SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
+ #endif
+ if (!winding) {
+ const SkPoint& topPoint = current->xyAtT(endIndex);
+ winding = innerContourCheck(contourList, topContour, topPoint);
+ #if DEBUG_WINDING
+ SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
+ #endif
+ }
+ }
const SkPoint* firstPt = NULL;
SkPoint lastPt;
+ bool active = winding >= -1 && winding <= 1;
+ bool firstTime = true;
+ int spanWinding = current->spanSign(index, endIndex);
+ #if DEBUG_WINDING
+ SkDebugf("%s spanWinding=%d\n", __FUNCTION__, startWinding);
+ #endif
do {
SkASSERT(!current->done());
int nextStart, nextEnd;
- Segment* next = current->findNext(winding, index, endIndex,
- nextStart, nextEnd);
+ Segment* next = current->findNext(winding + spanWinding, index,
+ endIndex, nextStart, nextEnd, firstTime);
if (!next) {
break;
}
if (!firstPt) {
- firstPt = &current->addMoveTo(index, simple);
+ firstPt = &current->addMoveTo(index, simple, active);
}
- lastPt = current->addCurveTo(index, endIndex, simple);
+ lastPt = current->addCurveTo(index, endIndex, simple, active);
current = next;
index = nextStart;
endIndex = nextEnd;
+ spanWinding = SkSign32(spanWinding) * next->windValue(
+ SkMin32(nextStart, nextEnd));
+ firstTime = false;
} while (*firstPt != lastPt);
if (firstPt) {
#if DEBUG_PATH_CONSTRUCTION
@@ -2232,8 +2751,6 @@ static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
simple.close();
}
} while (true);
- // FIXME: more work to be done for contained (but not intersecting)
- // segments
}
static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {