aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
authorGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-04-10 18:28:55 +0000
committerGravatar caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-04-10 18:28:55 +0000
commitfb173424e915e696a73067d616ce4f39a407261a (patch)
tree2f2bb2ca93d0993649f6afac76b0c87137834893 /experimental
parent75589257c6ac7fc55a66502b74b8bc09c0212fea (diff)
shape ops work in progress
more quadratics work git-svn-id: http://skia.googlecode.com/svn/trunk@3643 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'experimental')
-rw-r--r--experimental/Intersection/EdgeWalker.cpp424
-rw-r--r--experimental/Intersection/EdgeWalkerQuadratics_Test.cpp6
-rw-r--r--experimental/Intersection/EdgeWalker_TestUtility.cpp17
-rw-r--r--experimental/Intersection/edge.xcodeproj/project.pbxproj28
-rw-r--r--experimental/Intersection/op.htm135
5 files changed, 393 insertions, 217 deletions
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp
index 70fa8e679c..f3dff48f97 100644
--- a/experimental/Intersection/EdgeWalker.cpp
+++ b/experimental/Intersection/EdgeWalker.cpp
@@ -16,7 +16,7 @@
#include "ShapeOps.h"
#include "TSearch.h"
-#if 0 // set to 1 for no debugging whatsoever
+#if 01 // set to 1 for no debugging whatsoever
const bool gShowDebugf = false; // FIXME: remove once debugging is complete
#define DEBUG_DUMP 0
@@ -25,13 +25,15 @@ const bool gShowDebugf = false; // FIXME: remove once debugging is complete
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ABOVE_BELOW 0
#define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ASSEMBLE 0
+#define DEBUG_BRIDGE 0
#define DEBUG_SORT_HORIZONTAL 0
#define DEBUG_OUT 0
#define DEBUG_OUT_LESS_THAN 0
#define DEBUG_ADJUST_COINCIDENT 0
#define DEBUG_BOTTOM 0
#define DEBUG_SPLIT 0
-
+#define DEBUG_STITCH_EDGE 0
#else
const bool gShowDebugf = true; // FIXME: remove once debugging is complete
@@ -41,15 +43,25 @@ const bool gShowDebugf = true; // FIXME: remove once debugging is complete
#define DEBUG_ADD_BOTTOM_TS 0
#define DEBUG_ABOVE_BELOW 01
#define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ASSEMBLE 1
+#define DEBUG_BRIDGE 1
#define DEBUG_SORT_HORIZONTAL 01
#define DEBUG_OUT 01
#define DEBUG_OUT_LESS_THAN 0
#define DEBUG_ADJUST_COINCIDENT 1
#define DEBUG_BOTTOM 0
#define DEBUG_SPLIT 1
+#define DEBUG_STITCH_EDGE 1
#endif
+#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
+static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
+#endif
+#if DEBUG_STITCH_EDGE
+static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
+#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}};
@@ -196,7 +208,29 @@ static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
sub[3].fX = SkDoubleToScalar(dst[3].x);
sub[3].fY = SkDoubleToScalar(dst[3].y);
}
-
+
+static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
+ SkRect& bounds) {
+ SkPoint dst[3];
+ QuadSubDivide(a, startT, endT, dst);
+ bounds.fLeft = bounds.fRight = dst[0].fX;
+ bounds.fTop = bounds.fBottom = dst[0].fY;
+ for (int index = 1; index < 3; ++index) {
+ bounds.growToInclude(dst[index].fX, dst[index].fY);
+ }
+}
+
+static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
+ SkRect& bounds) {
+ SkPoint dst[4];
+ CubicSubDivide(a, startT, endT, dst);
+ bounds.fLeft = bounds.fRight = dst[0].fX;
+ bounds.fTop = bounds.fBottom = dst[0].fY;
+ for (int index = 1; index < 4; ++index) {
+ bounds.growToInclude(dst[index].fX, dst[index].fY);
+ }
+}
+
/*
list of edges
bounds for edge
@@ -336,49 +370,57 @@ public:
break;
}
int closeEdgeIndex = -listIndex - 1;
+ // the curve is deferred and not added right away because the
+ // following edge may extend the first curve.
SkPoint firstPt, lastCurve[4];
uint8_t lastVerb;
+#if DEBUG_ASSEMBLE
+ int firstIndex, lastIndex;
+ const int tab = 8;
+#endif
bool doMove = true;
int edgeIndex;
do {
SkPoint* ptArray = fEdges[listIndex].fPts;
uint8_t verb = fEdges[listIndex].fVerb;
- SkPoint* start, * end;
+ SkPoint* curve[4];
if (advance < 0) {
- start = &ptArray[verb];
- end = &ptArray[0];
+ curve[0] = &ptArray[verb];
+ if (verb == SkPath::kCubic_Verb) {
+ curve[1] = &ptArray[2];
+ curve[2] = &ptArray[1];
+ }
+ curve[verb] = &ptArray[0];
} else {
- start = &ptArray[0];
- end = &ptArray[verb];
+ curve[0] = &ptArray[0];
+ if (verb == SkPath::kCubic_Verb) {
+ curve[1] = &ptArray[1];
+ curve[2] = &ptArray[2];
+ }
+ curve[verb] = &ptArray[verb];
+ }
+ if (verb == SkPath::kQuad_Verb) {
+ curve[1] = &ptArray[1];
}
if (doMove) {
- firstPt = *start;
- simple.moveTo(start->fX, start->fY);
- if (gShowDebugf) {
- SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
- start->fX, start->fY);
- }
- lastCurve[0] = *start;
- if (verb == SkPath::kQuad_Verb) {
- lastCurve[1] = ptArray[1];
- } else if (verb == SkPath::kCubic_Verb) {
- if (advance < 0) {
- lastCurve[1] = ptArray[2];
- lastCurve[2] = ptArray[1];
- } else {
- lastCurve[1] = ptArray[1];
- lastCurve[2] = ptArray[2];
- }
+ firstPt = *curve[0];
+ simple.moveTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+ SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
+ listIndex + 1, curve[0]->fX, curve[0]->fY);
+ firstIndex = listIndex;
+#endif
+ for (int index = 0; index <= verb; ++index) {
+ lastCurve[index] = *curve[index];
}
- lastCurve[verb] = *end;
- lastVerb = verb;
doMove = false;
} else {
- bool gap = lastCurve[verb] != *start;
- if (gap) {
+ bool gap = lastCurve[lastVerb] != *curve[0];
+ if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
// FIXME: see comment in bridge -- this probably
// conceals errors
- SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10);
+ SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
+ curve[0]->fY) <= 10);
switch (lastVerb) {
case SkPath::kLine_Verb:
simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
@@ -393,43 +435,41 @@ public:
lastCurve[3].fX, lastCurve[3].fY);
break;
}
- if (gShowDebugf) {
- const char* verbStr[] = {"", "line", "quad", "cubic"};
- SkDebugf("%s %sTo-1 (%g,%g)\n", __FUNCTION__,
- verbStr[lastVerb], lastCurve[lastVerb].fX,
- lastCurve[lastVerb].fY);
- }
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
+ kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
+ lastCurve[lastVerb].fY);
+#endif
}
- if (gap || lastVerb != SkPath::kLine_Verb || !extendLine(lastCurve, *end)) {
+ int firstCopy = 1;
+ if (gap || (lastVerb == SkPath::kLine_Verb &&
+ !extendLine(lastCurve, *curve[verb]))) {
// FIXME: see comment in bridge -- this probably
// conceals errors
- SkASSERT(lastCurve[lastVerb] == *start ||
- (fFill && UlpsDiff(lastCurve[lastVerb].fY, start->fY) <= 10));
- simple.lineTo(start->fX, start->fY);
- if (gShowDebugf) {
- SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
- start->fX, start->fY);
- }
- lastCurve[0] = *start;
+ SkASSERT(lastCurve[lastVerb] == *curve[0] ||
+ (fFill && UlpsDiff(lastCurve[lastVerb].fY,
+ curve[0]->fY) <= 10));
+ simple.lineTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
+ lastIndex + 1, curve[0]->fX, curve[0]->fY);
+#endif
+ firstCopy = 0;
+ } else if (lastVerb != SkPath::kLine_Verb) {
+ firstCopy = 0;
}
- if (verb == SkPath::kQuad_Verb) {
- lastCurve[1] = ptArray[1];
- } else if (verb == SkPath::kCubic_Verb) {
- if (advance < 0) {
- lastCurve[1] = ptArray[2];
- lastCurve[2] = ptArray[1];
- } else {
- lastCurve[1] = ptArray[1];
- lastCurve[2] = ptArray[2];
- }
+ for (int index = firstCopy; index <= verb; ++index) {
+ lastCurve[index] = *curve[index];
}
- lastCurve[verb] = *end;
- lastVerb = verb;
}
+ lastVerb = verb;
+#if DEBUG_ASSEMBLE
+ lastIndex = listIndex;
+#endif
if (advance < 0) {
edgeIndex = fTops[listIndex];
fTops[listIndex] = 0;
- } else {
+ } else {
edgeIndex = fBottoms[listIndex];
fBottoms[listIndex] = 0;
}
@@ -442,37 +482,44 @@ public:
}
}
if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
+ switch (lastVerb) {
+ case SkPath::kLine_Verb:
+ simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
+ break;
+ case SkPath::kQuad_Verb:
+ simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
+ lastCurve[2].fX, lastCurve[2].fY);
+ break;
+ case SkPath::kCubic_Verb:
+ simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
+ lastCurve[2].fX, lastCurve[2].fY,
+ lastCurve[3].fX, lastCurve[3].fY);
+ break;
+ }
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
+ lastIndex + 1, kLVerbStr[lastVerb],
+ lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
+#endif
if (lastCurve[lastVerb] != firstPt) {
- switch (lastVerb) {
- case SkPath::kLine_Verb:
- simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
- break;
- case SkPath::kQuad_Verb:
- simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
- lastCurve[2].fX, lastCurve[2].fY);
- break;
- case SkPath::kCubic_Verb:
- simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
- lastCurve[2].fX, lastCurve[2].fY,
- lastCurve[3].fX, lastCurve[3].fY);
- break;
- }
- if (gShowDebugf) {
- const char* verbStr[] = {"", "line", "quad", "cubic"};
- SkDebugf("%s %sTo last (%g, %g)\n", __FUNCTION__,
- verbStr[lastVerb],
- lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
- }
+ simple.lineTo(firstPt.fX, firstPt.fY);
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
+ firstIndex + 1, firstPt.fX, firstPt.fY);
+#endif
}
- simple.lineTo(firstPt.fX, firstPt.fY);
simple.close();
- if (gShowDebugf) {
- SkDebugf("%s close (%g, %g)\n", __FUNCTION__,
- firstPt.fX, firstPt.fY);
- }
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s close\n", tab, "");
+#endif
break;
}
- // if this and next edge go different directions
+ // if this and next edge go different directions
+#if DEBUG_ASSEMBLE
+ SkDebugf("%*s advance=%d edgeIndex=%d flip=%s\n", tab, "",
+ advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
+ "true" : "false");
+#endif
if (advance > 0 ^ edgeIndex < 0) {
advance = -advance;
}
@@ -618,6 +665,22 @@ public:
}
SkASSERT(fMismatches.count() == 0);
#endif
+#if DEBUG_BRIDGE
+ for (index = 0; index < count; ++index) {
+ const OutEdge& edge = fEdges[index];
+ uint8_t verb = edge.fVerb;
+ SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
+ index == 0 ? __FUNCTION__ : " ",
+ index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
+ edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
+ }
+ for (index = 0; index < count; ++index) {
+ SkDebugf(" top of % 2d connects to %s of % 2d\n", index + 1,
+ fTops[index] < 0 ? "top " : "bottom", abs(fTops[index]));
+ SkDebugf(" bottom of % 2d connects to %s of % 2d\n", index + 1,
+ fBottoms[index] < 0 ? "top " : "bottom", abs(fBottoms[index]));
+ }
+#endif
}
protected:
@@ -696,6 +759,8 @@ public:
className, fTopIntercepts);
SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
className, fBottomIntercepts);
+ SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
+ className, fExplicit);
}
#endif
@@ -781,7 +846,7 @@ struct InEdge {
return insertedAt;
}
- void addPartial(SkTArray<InEdge>& edges, int id, int ptStart, int ptEnd,
+ void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
int verbStart, int verbEnd) {
InEdge* edge = edges.push_back_n(1);
int verbCount = verbEnd - verbStart;
@@ -793,25 +858,35 @@ struct InEdge {
edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
edge->setBounds();
- edge->fID = id + edges.count();
edge->fWinding = fWinding;
edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
}
- void addSplit(SkTArray<InEdge>& edges, int id, SkPoint* pts, uint8_t verb,
- double* ts, int tCount, bool flipped) {
+ void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
+ Intercepts& intercepts, int firstT, int lastT, bool flipped) {
InEdge* edge = edges.push_back_n(1);
edge->fIntercepts.push_back_n(1);
- edge->fIntercepts[0].fTs.append(tCount, ts);
+ if (firstT == 0) {
+ *edge->fIntercepts[0].fTs.append() = 0;
+ } else {
+ *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
+ }
+ bool add1 = lastT == intercepts.fTs.count();
+ edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
+ if (add1) {
+ *edge->fIntercepts[0].fTs.append() = 1;
+ }
edge->fIntercepts[0].fExplicit = true;
- edge->fPts.append(verb, pts);
+ edge->fPts.append(verb + 1, pts);
edge->fVerbs.append(1, &verb);
- edge->setBounds();
- edge->fID = id + edges.count();
- edge->fWinding = fWinding;
+ // FIXME: bounds could be better for partial Ts
+ edge->setSubBounds();
edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
if (flipped) {
- flip();
+ edge->flipTs();
+ edge->fWinding = -fWinding;
+ } else {
+ edge->fWinding = fWinding;
}
}
@@ -831,17 +906,16 @@ struct InEdge {
return false;
}
- void complete(signed char winding, int id) {
+ void complete(signed char winding) {
setBounds();
fIntercepts.push_back_n(fVerbs.count());
if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
flip();
}
fContainsIntercepts = fIntersected = false;
- fID = id;
}
- void flip() {
+ void flip() {
size_t index;
size_t last = fPts.count() - 1;
for (index = 0; index < last; ++index, --last) {
@@ -852,6 +926,18 @@ struct InEdge {
SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
}
}
+
+ void flipTs() {
+ SkASSERT(fIntercepts.count() == 1);
+ Intercepts& intercepts = fIntercepts[0];
+ SkASSERT(intercepts.fExplicit);
+ SkTDArray<double>& ts = intercepts.fTs;
+ size_t index;
+ size_t last = ts.count() - 1;
+ for (index = 0; index < last; ++index, --last) {
+ SkTSwap<double>(ts[index], ts[last]);
+ }
+ }
void reset() {
fCached.reset();
@@ -859,7 +945,6 @@ struct InEdge {
fPts.reset();
fVerbs.reset();
fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
- fID = -1;
fWinding = 0;
fContainsIntercepts = false;
fIntersected = false;
@@ -881,8 +966,25 @@ struct InEdge {
++ptPtr;
}
}
+
+ // recompute bounds based on subrange of T values
+ void setSubBounds() {
+ SkASSERT(fIntercepts.count() == 1);
+ Intercepts& intercepts = fIntercepts[0];
+ SkASSERT(intercepts.fExplicit);
+ SkASSERT(fVerbs.count() == 1);
+ SkTDArray<double>& ts = intercepts.fTs;
+ if (fVerbs[0] == SkPath::kQuad_Verb) {
+ SkASSERT(fPts.count() == 3);
+ QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+ } else {
+ SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
+ SkASSERT(fPts.count() == 4);
+ CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+ }
+ }
- void splitInflectionPts(SkTArray<InEdge>& edges, int idStart) {
+ void splitInflectionPts(SkTArray<InEdge>& edges) {
if (!fIntersected) {
return;
}
@@ -918,11 +1020,9 @@ struct InEdge {
int nextPt = pts - fPts.begin();
int nextVerb = verbs - 1 - fVerbs.begin();
if (lastVerb < nextVerb) {
- addPartial(edges, idStart, lastPt, nextPt,
- lastVerb, nextVerb);
+ addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
#if DEBUG_SPLIT
- SkDebugf("%s addPartial 1 edge=%d\n", __FUNCTION__,
- fID);
+ SkDebugf("%s addPartial 1\n", __FUNCTION__);
#endif
}
lastPt = nextPt;
@@ -931,20 +1031,18 @@ struct InEdge {
} else {
if (firstSplit >= 0) {
if (lastSplit < firstSplit) {
- addSplit(edges, idStart, pts, verb,
- &intercepts.fTs[lastSplit],
- firstSplit - lastSplit, false);
+ addSplit(edges, pts, verb, intercepts,
+ lastSplit, firstSplit, false);
#if DEBUG_SPLIT
- SkDebugf("%s addSplit 1 edge=%d tIndex=%d,%d\n",
- __FUNCTION__, fID, lastSplit, firstSplit);
+ SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
+ __FUNCTION__, lastSplit, firstSplit);
#endif
}
- addSplit(edges, idStart, pts, verb,
- &intercepts.fTs[firstSplit],
- tIndex - firstSplit, true);
+ addSplit(edges, pts, verb, intercepts,
+ firstSplit, tIndex, true);
#if DEBUG_SPLIT
- SkDebugf("%s addSplit 2 edge=%d tIndex=%d,%d flip\n",
- __FUNCTION__, fID, firstSplit, tIndex);
+ SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
+ __FUNCTION__, firstSplit, tIndex);
#endif
lastSplit = tIndex;
firstSplit = -1;
@@ -956,20 +1054,18 @@ struct InEdge {
if (firstSplit < 0) {
firstSplit = lastSplit;
} else {
- addSplit(edges, idStart, pts, verb,
- &intercepts.fTs[lastSplit], firstSplit - lastSplit,
- false);
+ addSplit(edges, pts, verb, intercepts, lastSplit,
+ firstSplit, false);
#if DEBUG_SPLIT
- SkDebugf("%s addSplit 3 edge=%d tIndex=%d,%d\n", __FUNCTION__,
- fID, lastSplit, firstSplit);
+ SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
+ lastSplit, firstSplit);
#endif
}
- addSplit(edges, idStart, pts, verb,
- &intercepts.fTs[firstSplit], tIndex - firstSplit,
- pts[verb].fY < y);
+ addSplit(edges, pts, verb, intercepts, firstSplit,
+ tIndex, pts[verb].fY < y);
#if DEBUG_SPLIT
- SkDebugf("%s addSplit 4 edge=%d tIndex=%d,%d %s\n", __FUNCTION__,
- fID, firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
+ SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
+ firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
#endif
}
}
@@ -978,10 +1074,9 @@ struct InEdge {
int nextVerb = verbs - 1 - fVerbs.begin();
if (lastVerb < nextVerb) {
int nextPt = pts - fPts.begin();
- addPartial(edges, idStart, lastPt, nextPt,
- lastVerb, nextVerb);
+ addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
#if DEBUG_SPLIT
- SkDebugf("%s addPartial 2 edge=%d\n", __FUNCTION__, fID);
+ SkDebugf("%s addPartial 2\n", __FUNCTION__);
#endif
}
// OPTIMIZATION: reuse the edge instead of marking it empty
@@ -1015,7 +1110,7 @@ struct InEdge {
SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
className, i, fVerbs[i]);
}
- SkDebugf("%*s.fBounds=(%1.9g. %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
+ SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
className, fBounds.fLeft, fBounds.fTop,
fBounds.fRight, fBounds.fBottom);
SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
@@ -1049,7 +1144,6 @@ InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges
: fPath(path)
, fCurrentEdge(NULL)
, fEdges(edges)
- , fID(0)
, fHorizontalEdges(horizontalEdges)
, fIgnoreHorizontal(ignoreHorizontal)
, fContainsCurves(false)
@@ -1061,10 +1155,6 @@ bool containsCurves() const {
return fContainsCurves;
}
-int nextID() {
- return ++fID;
-}
-
protected:
void addEdge() {
@@ -1076,7 +1166,7 @@ void addEdge() {
bool complete() {
if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
- fCurrentEdge->complete(fWinding, nextID());
+ fCurrentEdge->complete(fWinding);
fCurrentEdge = NULL;
return true;
}
@@ -1180,7 +1270,6 @@ private:
SkPath::Verb fVerb;
int fPtCount;
int fPtOffset;
- int fID;
int8_t fWinding;
bool fIgnoreHorizontal;
bool fContainsCurves;
@@ -1729,19 +1818,17 @@ static void debugShowLineIntersection(int pts, const WorkEdge& wt,
SkPoint wtOutPt, wnOutPt;
LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
- SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
+ SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
__FUNCTION__,
wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
- wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY,
- test->fID, next->fID);
+ wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
if (pts == 2) {
SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
}
- SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g) (%d,%d)\n",
+ SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
__FUNCTION__,
wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
- wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY,
- test->fID, next->fID);
+ wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
if (pts == 2) {
SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
}
@@ -1979,10 +2066,15 @@ static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
if (edgeCount == 0) {
return;
}
+ int id = 0;
for (size_t index = 0; index < edgeCount; ++index) {
- *edgeList.append() = &edges[index];
+ InEdge& edge = edges[index];
+ if (!edge.fWinding) {
+ continue;
+ }
+ edge.fID = ++id;
+ *edgeList.append() = &edge;
}
- edgeSentinel.fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
*edgeList.append() = &edgeSentinel;
QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
}
@@ -2240,7 +2332,6 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
bool moreToDo, aboveBottom;
do {
double currentT = activePtr->t();
- SkASSERT(currentT < 1);
const SkPoint* points = wt.fPts;
double nextT;
do {
@@ -2270,32 +2361,30 @@ static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar y,
}
if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
!= clipped[verb].fY : clipped[0] != clipped[verb])) {
- if (gShowDebugf) {
- const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
- SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
- " v=%d t=(%1.9g,%1.9g)\n", tab, "",
- verbStr[verb], clipped[0].fX, clipped[0].fY,
- clipped[verb].fX, clipped[verb].fY,
- activePtr->ID(),
- activePtr->fWorkEdge.fVerb
- - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
- currentT, nextT);
- }
+#if DEBUG_STITCH_EDGE
+ SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
+ " v=%d t=(%1.9g,%1.9g)\n", tab, "",
+ kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+ clipped[verb].fX, clipped[verb].fY,
+ activePtr->ID(),
+ activePtr->fWorkEdge.fVerb
+ - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+ currentT, nextT);
+#endif
outBuilder.addCurve(clipped, (SkPath::Verb) verb,
activePtr->fWorkEdge.fEdge->fID,
activePtr->fCloseCall);
} else {
- if (gShowDebugf ) {
- const char* verbStr[] = {"", "Line", "Quad", "Cubic"};
- SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
- " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
- verbStr[verb], clipped[0].fX, clipped[0].fY,
- clipped[verb].fX, clipped[verb].fY,
- activePtr->ID(),
- activePtr->fWorkEdge.fVerb
- - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
- currentT, nextT);
- }
+#if DEBUG_STITCH_EDGE
+ SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
+ " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
+ kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+ clipped[verb].fX, clipped[verb].fY,
+ activePtr->ID(),
+ activePtr->fWorkEdge.fVerb
+ - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+ currentT, nextT);
+#endif
}
// by advancing fAbove/fBelow, the next call to sortHorizontal
// will use these values if they're still valid instead of
@@ -2369,6 +2458,7 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
SkTDArray<InEdge*> edgeList;
InEdge edgeSentinel;
+ edgeSentinel.reset();
makeEdgeList(edges, edgeSentinel, edgeList);
SkTDArray<HorizontalEdge*> horizontalList;
HorizontalEdge horizontalSentinel;
@@ -2407,13 +2497,13 @@ void simplify(const SkPath& path, bool asFill, SkPath& simple) {
currentPtr = edgeList.begin();
SkTArray<InEdge> splits;
do {
- (*currentPtr)->splitInflectionPts(splits, builder.nextID());
+ (*currentPtr)->splitInflectionPts(splits);
} while (*++currentPtr != &edgeSentinel);
if (splits.count()) {
- edges.pop_back(); // pop the sentinel
for (int index = 0; index < splits.count(); ++index) {
edges.push_back(splits[index]);
}
+ edgeList.reset();
makeEdgeList(edges, edgeSentinel, edgeList);
}
}
diff --git a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp
index 584ecdabb3..31a5a23778 100644
--- a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp
+++ b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp
@@ -58,9 +58,13 @@ static void (*simplifyTests[])() = {
static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
-static void (*firstTest)() = 0;
+static void (*firstTest)() = testSimplifyQuadratic3;
+static bool skipAll = false;
void SimplifyQuadraticPaths_Test() {
+ if (skipAll) {
+ return;
+ }
size_t index = 0;
if (firstTest) {
while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
index 0b2fd06fbf..0844458ba3 100644
--- a/experimental/Intersection/EdgeWalker_TestUtility.cpp
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -3,6 +3,7 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkPaint.h"
+#include <algorithm>
static bool gShowPath = false;
static bool gComparePaths = true;
@@ -124,7 +125,7 @@ bool drawAsciiPaths(const SkPath& one, const SkPath& two,
canvas.drawPath(one, paint);
canvas.restore();
canvas.save();
- canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1);
+ canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
canvas.drawPath(two, paint);
canvas.restore();
for (int y = 0; y < bitHeight; ++y) {
@@ -184,13 +185,19 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap,
}
}
if (!gDrawAllAsciiPaths) {
- errors = scaledDrawTheSame(one, two, 9, 7, false, bitmap, canvas);
- if (errors > 4) {
- scaledDrawTheSame(one, two, 9, 7, true, bitmap, canvas);
+ const SkRect& bounds1 = one.getBounds();
+ const SkRect& bounds2 = two.getBounds();
+ SkRect larger = bounds1;
+ larger.join(bounds2);
+ SkScalar xScale = std::max(80.0f / larger.width(), 1.0f);
+ SkScalar yScale = std::max(60.0f / larger.height(), 1.0f);
+ errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
+ if (errors > 8) {
+ scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
}
}
if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors);
- if (errors > 4 && gComparePathsAssert) {
+ if (errors > 31 && gComparePathsAssert) {
showPath(one);
showPath(two, "simplified:");
SkASSERT(0);
diff --git a/experimental/Intersection/edge.xcodeproj/project.pbxproj b/experimental/Intersection/edge.xcodeproj/project.pbxproj
index c91ac45f78..6a6ea320b0 100644
--- a/experimental/Intersection/edge.xcodeproj/project.pbxproj
+++ b/experimental/Intersection/edge.xcodeproj/project.pbxproj
@@ -94,13 +94,6 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- FE74136014F6866000056D7B /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = ECDC7853EF9A45553165AE98;
- remoteInfo = opts_ssse3;
- };
FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = FEA5F4D91497FFF6005052F9 /* ports.xcodeproj */;
@@ -108,6 +101,13 @@
remoteGlobalIDString = CDE03B47AA5CD6CE32E53995;
remoteInfo = ports;
};
+ FEE70DA6153487F200814606 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = FEF87C2413E0410900335C58 /* opts.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = ECDC7853EF9A45553165AE98 /* libopts_ssse3.a */;
+ remoteInfo = opts_ssse3;
+ };
FEED7261144DD38D0059E97B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = FEED725D144DD38D0059E97B /* views.xcodeproj */;
@@ -670,7 +670,7 @@
isa = PBXGroup;
children = (
FEF87C2C13E0410900335C58 /* libopts.a */,
- FE74136114F6866000056D7B /* libopts_ssse3.a */,
+ FEE70DA7153487F200814606 /* libopts_ssse3.a */,
);
name = Products;
sourceTree = "<group>";
@@ -791,18 +791,18 @@
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
- FE74136114F6866000056D7B /* libopts_ssse3.a */ = {
+ FEA5F4E11497FFF6005052F9 /* libports.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
- path = libopts_ssse3.a;
- remoteRef = FE74136014F6866000056D7B /* PBXContainerItemProxy */;
+ path = libports.a;
+ remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
- FEA5F4E11497FFF6005052F9 /* libports.a */ = {
+ FEE70DA7153487F200814606 /* libopts_ssse3.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
- path = libports.a;
- remoteRef = FEA5F4E01497FFF6005052F9 /* PBXContainerItemProxy */;
+ path = libopts_ssse3.a;
+ remoteRef = FEE70DA6153487F200814606 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
FEED7262144DD38D0059E97B /* libviews.a */ = {
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
index b2c91237f2..2078396bad 100644
--- a/experimental/Intersection/op.htm
+++ b/experimental/Intersection/op.htm
@@ -69,11 +69,55 @@ path.lineTo(460.782654, -112.96492);
path.lineTo(326.610535, 34.0393639);
path.close();
</div>
+<div id="test_5div">
+original:
+path.moveTo(0, 0);
+path.quadTo(20, 0, 20, 20);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 20);
+path.quadTo(0, 0, 20, 0);
+path.lineTo(0, 20);
+path.close();
+simplified:
+path.moveTo(0, 0);
+path.lineTo(5, 5);
+path.quadTo(0, 10, 0, 20);
+path.lineTo(20, 20);
+path.quadTo(20, 10, 15, 5);
+path.lineTo(20, 0);
+path.quadTo(14.1421356, 0, 10, 1.71572876);
+path.quadTo(5.85786438, 0, 0, 0);
+path.close();
+</div>
+<div id="test_6div">
+original:
+path.moveTo(0, 20);
+path.quadTo(20, 0, 40, 20);
+path.lineTo(0, 20);
+path.close();
+path.moveTo(40, 10);
+path.quadTo(20, 30, 0, 10);
+path.lineTo(40, 10);
+path.close();
+simplified:
+path.moveTo(0, 10);
+path.quadTo(2.92893219, 12.9289322, 5.85786438, 15);
+path.quadTo(2.92893219, 17.0710678, 0, 20);
+path.lineTo(40, 20);
+path.quadTo(37.0710678, 17.0710678, 34.1421356, 15);
+path.quadTo(37.0710678, 12.9289322, 40, 10);
+path.lineTo(0, 10);
+path.close();
+</div>
+
</div>
<script type="text/javascript">
var testDivs = [
+ test_5div,
+ test_6div,
test_4div,
test_3div,
test_2div,
@@ -96,16 +140,26 @@ function parse(test) {
var contourStrs = test.split("path.close();");
var pattern = /-?\d+\.*\d*/g;
for (var c in contourStrs) {
- var points = contourStrs[c].match(pattern);
- var pts = [];
- for (var wd in points) {
- var num = parseFloat(points[wd]);
- if (isNaN(num)) continue;
- pts.push(num );
+ var contour = contourStrs[c];
+ var verbStrs = contour.split("path");
+ var verbs = [];
+ for (var v in verbStrs) {
+ var verbStr = verbStrs[v];
+ var points = verbStr.match(pattern);
+ var pts = [];
+ for (var wd in points) {
+ var num = parseFloat(points[wd]);
+ if (isNaN(num)) continue;
+ pts.push(num);
+ }
+ if (pts.length > 0)
+ verbs.push(pts);
}
- contours.push(pts);
+ if (verbs.length > 0)
+ contours.push(verbs);
}
- tests.push(contours);
+ if (contours.length > 0)
+ tests.push(contours);
}
function init(test) {
@@ -116,13 +170,15 @@ function init(test) {
var xmax = -Infinity;
var ymin = Infinity;
var ymax = -Infinity;
- for (pts in test) {
- var pt = test[pts];
- for (i = 0; i < pt.length; i += 2) {
- xmin = Math.min(xmin, pt[i]);
- ymin = Math.min(ymin, pt[i + 1]);
- xmax = Math.max(xmax, pt[i]);
- ymax = Math.max(ymax, pt[i + 1]);
+ for (var contours in test) {
+ var contour = test[contours];
+ for (var verbs in contour) {
+ var verb = contour[verbs];
+ var last = verb.length;
+ xmin = Math.min(xmin, verb[0]);
+ ymin = Math.min(ymin, verb[1]);
+ xmax = Math.max(xmax, verb[last - 2]);
+ ymax = Math.max(ymax, verb[last - 1]);
}
}
var subscale = 1;
@@ -183,27 +239,46 @@ function draw(test, _at_x, _at_y, scale) {
ctx.fillText(num.toFixed(0), 0, i * unit + _at_y + 0);
}
ctx.strokeStyle = "red";
- for (pts in test) {
- var pt = test[pts];
- var x = pt[0];
- var y = pt[1];
+ var contours, verbs, pts;
+ for (contours in test) {
+ var contour = test[contours];
+ if (contours == 2) ctx.strokeStyle = "blue";
ctx.beginPath();
- ctx.moveTo(xoffset + x * unit, yoffset + y * unit);
- for (i = 2; i < pt.length; i += 2) {
- x = pt[i];
- y = pt[i + 1];
- ctx.lineTo(xoffset + x * unit, yoffset + y * unit);
+ var first = true;
+ for (verbs in contour) {
+ var verb = contour[verbs];
+ switch (verb.length) {
+ case 2:
+ if (first) {
+ ctx.moveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+ first = false;
+ } else
+ ctx.lineTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+ break;
+ case 4:
+ ctx.quadraticCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+ xoffset + verb[2] * unit, yoffset + verb[3] * unit);
+ break;
+ case 6:
+ ctx.bezierCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+ xoffset + verb[2] * unit, yoffset + verb[3] * unit,
+ xoffset + verb[4] * unit, yoffset + verb[5] * unit);
+ break;
+ }
}
ctx.stroke();
}
ctx.fillStyle="blue";
- for (pts in test) {
- var pt = test[pts];
- for (i = 0; i < pt.length; i += 2) {
- x = pt[i];
- y = pt[i + 1];
- drawPoint(x, y, xoffset, yoffset, unit);
+ for (contours in test) {
+ var contour = test[contours];
+ for (verbs in contour) {
+ var verb = contour[verbs];
+ for (i = 0; i < verb.length; i += 2) {
+ x = verb[i];
+ y = verb[i + 1];
+ drawPoint(x, y, xoffset, yoffset, unit);
+ }
}
}
}