From 78e17130f396d8b2157116c2504e357192f87ed1 Mon Sep 17 00:00:00 2001 From: "caryclark@google.com" Date: Tue, 17 Apr 2012 11:40:34 +0000 Subject: [PATCH] work in progress git-svn-id: http://skia.googlecode.com/svn/trunk@3702 2bbb7eff-a529-9590-31e7-b0007b416f81 --- experimental/Intersection/ActiveEdge_Test.cpp | 24 +- experimental/Intersection/CurveIntersection.h | 1 + experimental/Intersection/EdgeWalker.cpp | 442 +++++++++++------- .../EdgeWalkerPolygon4x4_Test.cpp | 51 +- .../EdgeWalkerQuadratics_Test.cpp | 183 +++++++- experimental/Intersection/EdgeWalker_Test.h | 19 +- .../Intersection/EdgeWalker_TestUtility.cpp | 38 +- .../Intersection/Intersection_Tests.cpp | 13 +- .../Intersection/Intersection_Tests.h | 1 + .../Intersection/LineParameterization.cpp | 23 + .../Intersection/QuadraticUtilities.cpp | 11 +- experimental/Intersection/ShapeOps.h | 2 +- experimental/Intersection/op.htm | 138 +++++- 13 files changed, 729 insertions(+), 217 deletions(-) diff --git a/experimental/Intersection/ActiveEdge_Test.cpp b/experimental/Intersection/ActiveEdge_Test.cpp index d89510f1d4..fe7d664e17 100755 --- a/experimental/Intersection/ActiveEdge_Test.cpp +++ b/experimental/Intersection/ActiveEdge_Test.cpp @@ -47,6 +47,26 @@ SkPoint leftRight[][4] = { size_t leftRightCount = sizeof(leftRight) / sizeof(leftRight[0]); +// older code that worked mostly +static bool operator_less_than(const UnitTest::ActiveEdge& lh, + const UnitTest::ActiveEdge& rh) { + if (rh.fAbove.fY - lh.fAbove.fY > lh.fBelow.fY - rh.fAbove.fY + && lh.fBelow.fY < rh.fBelow.fY + || lh.fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - lh.fAbove.fY + && rh.fBelow.fY < lh.fBelow.fY) { + const SkPoint& check = rh.fBelow.fY <= lh.fBelow.fY + && lh.fBelow != rh.fBelow ? rh.fBelow : + rh.fAbove; + return (check.fY - lh.fAbove.fY) * (lh.fBelow.fX - lh.fAbove.fX) + < (lh.fBelow.fY - lh.fAbove.fY) * (check.fX - lh.fAbove.fX); + } + const SkPoint& check = lh.fBelow.fY <= rh.fBelow.fY + && lh.fBelow != rh.fBelow ? lh.fBelow : lh.fAbove; + return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX) + < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX); +} + + void ActiveEdge_Test() { UnitTest::InEdge leftIn, rightIn; UnitTest::ActiveEdge left, right; @@ -58,9 +78,9 @@ void ActiveEdge_Test() { right.fAbove = leftRight[x][2]; right.fBelow = leftRight[x][3]; SkASSERT(left < right); - SkASSERT(left.operator_less_than(right)); + SkASSERT(operator_less_than(left, right)); SkASSERT(!(right < left)); - SkASSERT(!right.operator_less_than(left)); + SkASSERT(!operator_less_than(right, left)); } } diff --git a/experimental/Intersection/CurveIntersection.h b/experimental/Intersection/CurveIntersection.h index 25c696c48d..1da2d9b19b 100644 --- a/experimental/Intersection/CurveIntersection.h +++ b/experimental/Intersection/CurveIntersection.h @@ -14,6 +14,7 @@ int convex_hull(const Cubic& cubic, char order[4]); bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]); bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2); bool implicit_matches(const _Line& line1, const _Line& line2); +bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps); bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2); void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst); void sub_divide(const _Line& src, double t1, double t2, _Line& dst); diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp index f3dff48f97..1bf3c8bf50 100644 --- a/experimental/Intersection/EdgeWalker.cpp +++ b/experimental/Intersection/EdgeWalker.cpp @@ -16,42 +16,49 @@ #include "ShapeOps.h" #include "TSearch.h" -#if 01 // set to 1 for no debugging whatsoever -const bool gShowDebugf = false; // FIXME: remove once debugging is complete +#undef SkASSERT +#define SkASSERT(cond) while (!(cond)) { sk_throw(); } + +// FIXME: remove once debugging is complete +#if 0 // set to 1 for no debugging whatsoever + +const bool gRunTestsInOneThread = true; -#define DEBUG_DUMP 0 -#define DEBUG_ADD 0 -#define DEBUG_ADD_INTERSECTING_TS 0 -#define DEBUG_ADD_BOTTOM_TS 0 -#define DEBUG_ABOVE_BELOW 0 #define DEBUG_ACTIVE_LESS_THAN 0 +#define DEBUG_ADD 0 +#define DEBUG_ADD_BOTTOM_TS 0 +#define DEBUG_ADD_INTERSECTING_TS 0 +#define DEBUG_ADJUST_COINCIDENT 0 #define DEBUG_ASSEMBLE 0 +#define DEBUG_BOTTOM 0 #define DEBUG_BRIDGE 0 +#define DEBUG_DUMP 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 +#define DEBUG_TRIM_LINE 0 + +#else + +const bool gRunTestsInOneThread = true; -#define DEBUG_DUMP 01 -#define DEBUG_ADD 01 -#define DEBUG_ADD_INTERSECTING_TS 0 -#define DEBUG_ADD_BOTTOM_TS 0 -#define DEBUG_ABOVE_BELOW 01 #define DEBUG_ACTIVE_LESS_THAN 0 +#define DEBUG_ADD 01 +#define DEBUG_ADD_BOTTOM_TS 0 +#define DEBUG_ADD_INTERSECTING_TS 0 +#define DEBUG_ADJUST_COINCIDENT 1 #define DEBUG_ASSEMBLE 1 +#define DEBUG_BOTTOM 0 #define DEBUG_BRIDGE 1 +#define DEBUG_DUMP 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 +#define DEBUG_TRIM_LINE 1 #endif @@ -182,7 +189,8 @@ static void LineSubDivide(const SkPoint a[2], double startT, double endT, static void QuadSubDivide(const SkPoint a[3], double startT, double endT, SkPoint sub[3]) { - const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}}; + const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, + {a[2].fX, a[2].fY}}; Quadratic dst; sub_divide(aQuad, startT, endT, dst); sub[0].fX = SkDoubleToScalar(dst[0].x); @@ -195,8 +203,8 @@ static void QuadSubDivide(const SkPoint a[3], double startT, double endT, static void CubicSubDivide(const SkPoint a[4], double startT, double endT, SkPoint sub[4]) { - 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}}; + 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}}; Cubic dst; sub_divide(aCubic, startT, endT, dst); sub[0].fX = SkDoubleToScalar(dst[0].x); @@ -231,6 +239,45 @@ static void CubicSubBounds(const SkPoint a[4], double startT, double endT, } } +static SkPath::Verb QuadReduceOrder(SkPoint a[4]) { + const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, + {a[2].fX, a[2].fY}}; + Quadratic dst; + int order = reduceOrder(aQuad, dst); + for (int index = 0; index < order; ++index) { + a[index].fX = SkDoubleToScalar(dst[index].x); + a[index].fY = SkDoubleToScalar(dst[index].y); + } + if (order == 1) { // FIXME: allow returning points, caller should discard + a[1] = a[0]; + return (SkPath::Verb) order; + } + return (SkPath::Verb) (order - 1); +} + +static SkPath::Verb CubicReduceOrder(SkPoint a[4]) { + 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}}; + Cubic dst; + int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed); + for (int index = 0; index < order; ++index) { + a[index].fX = SkDoubleToScalar(dst[index].x); + a[index].fY = SkDoubleToScalar(dst[index].y); + } + if (order == 1) { // FIXME: allow returning points, caller should discard + a[1] = a[0]; + return (SkPath::Verb) order; + } + return (SkPath::Verb) (order - 1); +} + +static bool IsCoincident(const SkPoint a[2], const SkPoint& above, + const SkPoint& below) { + const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}}; + const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}}; + return implicit_matches_ulps(aLine, bLine, 32); +} + /* list of edges bounds for edge @@ -346,10 +393,10 @@ public: * (edge.fPts[1].fX - edge.fPts[0].fX) / (edge.fPts[1].fY - edge.fPts[0].fY); edge.fPts[1].fY = y; - if (gShowDebugf) { - SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id, - edge.fPts[1].fX, y); - } +#if DEBUG_TRIM_LINE + SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id, + edge.fPts[1].fX, y); +#endif return true; } return false; @@ -442,8 +489,9 @@ public: #endif } int firstCopy = 1; - if (gap || (lastVerb == SkPath::kLine_Verb && - !extendLine(lastCurve, *curve[verb]))) { + if (gap || (lastVerb == SkPath::kLine_Verb + && (verb != SkPath::kLine_Verb + || !extendLine(lastCurve, *curve[verb])))) { // FIXME: see comment in bridge -- this probably // conceals errors SkASSERT(lastCurve[lastVerb] == *curve[0] || @@ -1173,15 +1221,14 @@ bool complete() { return false; } -int direction(int count) { - fPtCount = count; +int direction(SkPath::Verb verb) { + fPtCount = verb + 1; if (fIgnoreHorizontal && isHorizontal()) { return 0; } - int last = count - 1; - return fPts[0].fY == fPts[last].fY - ? fPts[0].fX == fPts[last].fX ? 0 : fPts[0].fX < fPts[last].fX - ? 1 : -1 : fPts[0].fY < fPts[last].fY ? 1 : -1; + return fPts[0].fY == fPts[verb].fY + ? fPts[0].fX == fPts[verb].fX ? 0 : fPts[0].fX < fPts[verb].fX + ? 1 : -1 : fPts[0].fY < fPts[verb].fY ? 1 : -1; } bool isHorizontal() { @@ -1211,15 +1258,17 @@ void walk() { startEdge(); continue; case SkPath::kLine_Verb: - winding = direction(2); + winding = direction(SkPath::kLine_Verb); break; case SkPath::kQuad_Verb: - winding = direction(3); - fContainsCurves = true; + fVerb = QuadReduceOrder(fPts); + winding = direction(fVerb); + fContainsCurves |= fVerb == SkPath::kQuad_Verb; break; case SkPath::kCubic_Verb: - winding = direction(4); - fContainsCurves = true; + fVerb = CubicReduceOrder(fPts); + winding = direction(fVerb); + fContainsCurves |= fVerb >= SkPath::kQuad_Verb; break; case SkPath::kClose_Verb: SkASSERT(fCurrentEdge); @@ -1291,12 +1340,20 @@ struct WorkEdge { fPts += *fVerb++; return fVerb != fEdge->fVerbs.end(); } + + const SkPoint* lastPoints() const { + SkASSERT(fPts >= fEdge->fPts.begin() + lastVerb()); + return &fPts[-lastVerb()]; + } SkPath::Verb lastVerb() const { SkASSERT(fVerb > fEdge->fVerbs.begin()); return (SkPath::Verb) fVerb[-1]; } + const SkPoint* points() const { + return fPts; + } SkPath::Verb verb() const { return (SkPath::Verb) *fVerb; @@ -1323,6 +1380,8 @@ struct WorkEdge { // as active edges are introduced, only local sorting should be required class ActiveEdge { public: + // this logic must be kept in sync with tooCloseToCall + // callers expect this to only read fAbove, fBelow bool operator<(const ActiveEdge& rh) const { double topD = fAbove.fX - rh.fAbove.fX; if (rh.fAbove.fY < fAbove.fY) { @@ -1344,49 +1403,6 @@ public: return (fabs(topD) > fabs(botD) ? topD : botD) < 0; } - // OPTIMIZATION: fold return statements into one - bool operator_less_than(const ActiveEdge& rh) const { - if (rh.fAbove.fY - fAbove.fY > fBelow.fY - rh.fAbove.fY - && fBelow.fY < rh.fBelow.fY - || fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - fAbove.fY - && rh.fBelow.fY < fBelow.fY) { - // FIXME: need to compute distance, not check for points equal - const SkPoint& check = rh.fBelow.fY <= fBelow.fY - && fBelow != rh.fBelow ? rh.fBelow : - rh.fAbove; - #if DEBUG_ACTIVE_LESS_THAN - SkDebugf("%s 1 %c %cthis (edge=%d) {%g,%g %g,%g}" - " < rh (edge=%d) {%g,%g %g,%g} upls=(%d)\n", __FUNCTION__, - rh.fBelow.fY <= fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A', - (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX) - < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX) ? ' ' - : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY, - rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY, - UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX), - (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX))); - #endif - return (check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX) - < (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX); - } - // FIXME: need to compute distance, not check for points equal - const SkPoint& check = fBelow.fY <= rh.fBelow.fY - && fBelow != rh.fBelow ? fBelow : fAbove; - #if DEBUG_ACTIVE_LESS_THAN - SkDebugf("%s 2 %c %cthis (edge=%d) {%g,%g %g,%g}" - " < rh (edge=%d) {%g,%g %g,%g} upls=(%d) (%d,%d)\n", __FUNCTION__, - fBelow.fY <= rh.fBelow.fY && fBelow != rh.fBelow ? 'B' : 'A', - (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX) - < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX) - ? ' ' : '!', ID(), fAbove.fX, fAbove.fY, fBelow.fX, fBelow.fY, - rh.ID(), rh.fAbove.fX, rh.fAbove.fY, rh.fBelow.fX, rh.fBelow.fY, - UlpsDiff((rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX), - (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX)), - UlpsDiff(fBelow.fX, rh.fBelow.fX), UlpsDiff(fBelow.fY, rh.fBelow.fY)); - #endif - return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX) - < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX); - } - // If a pair of edges are nearly coincident for some span, add a T in the // edge so it can be shortened to match the other edge. Note that another // approach is to trim the edge after it is added to the OutBuilder list -- @@ -1417,6 +1433,7 @@ public: SkTSwap(left, right); } double ts[2]; + SkASSERT(fWorkEdge.fVerb[0] == SkPath::kLine_Verb); int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts); SkASSERT(pts == 1); // An ActiveEdge or WorkEdge has no need to modify the T values computed @@ -1514,16 +1531,14 @@ public: (*xyAtTFunc)(fWorkEdge.fPts, tBelow, &fBelow); } } - #if DEBUG_ABOVE_BELOW fTAbove = tAbove; fTBelow = tBelow; - #endif } bool done(SkScalar bottom) const { return fDone || fYBottom >= bottom; } - + void fixBelow() { if (fFixBelow) { switch (fWorkEdge.verb()) { @@ -1567,27 +1582,39 @@ public: // It's unclear how to do this -- seems more complicated than recording the // t values, since the same t values could exist intersecting non-coincident // edges. - bool isCoincidentWith(const ActiveEdge* edge, SkScalar y) const { + bool isCoincidentWith(const ActiveEdge* edge) const { if (fAbove != edge->fAbove || fBelow != edge->fBelow) { return false; } - uint8_t verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb(); - uint8_t edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb() + SkPath::Verb verb = fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb(); + SkPath::Verb edgeVerb = edge->fDone ? edge->fWorkEdge.lastVerb() : edge->fWorkEdge.verb(); if (verb != edgeVerb) { return false; } switch (verb) { - case SkPath::kLine_Verb: { + case SkPath::kLine_Verb: return true; - } default: - // FIXME: add support for all curve types + // FIXME: add support for quads, cubics SkASSERT(0); + return false; } return false; } + bool isUnordered(const ActiveEdge* edge) const { + return fAbove == edge->fAbove && fBelow == edge->fBelow; + } + + SkPath::Verb lastVerb() const { + return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb(); + } + + const SkPoint* lastPoints() const { + return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points(); + } + // The shortest close call edge should be moved into a position where // it contributes if the winding is transitioning to or from zero. bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const { @@ -1625,35 +1652,23 @@ public: } return false; } + + bool swapUnordered(const ActiveEdge* edge, SkScalar bottom) const { + SkASSERT(lastVerb() != SkPath::kLine_Verb + || edge->lastVerb() != SkPath::kLine_Verb); + if (fDone || edge->fDone) { + return false; + } + ActiveEdge thisWork, edgeWork; + extractAboveBelow(thisWork); + edge->extractAboveBelow(edgeWork); + return edgeWork < thisWork; + } bool tooCloseToCall(const ActiveEdge* edge) const { int ulps; - // FIXME: the first variation works better (or at least causes fewer tests - // to fail than the second, although the second's logic better matches the - // current sort criteria. Need to track down the cause of the crash, and - // see if the second case isn't somehow buggy. -#if 01 - // FIXME: don't compare points for equality - // OPTIMIZATION: refactor to make one call to UlpsDiff - if (edge->fAbove.fY - fAbove.fY > fBelow.fY - edge->fAbove.fY - && fBelow.fY < edge->fBelow.fY - || fAbove.fY - edge->fAbove.fY < edge->fBelow.fY - fAbove.fY - && edge->fBelow.fY < fBelow.fY) { - const SkPoint& check = edge->fBelow.fY <= fBelow.fY - && fBelow != edge->fBelow ? edge->fBelow : - edge->fAbove; - ulps = UlpsDiff((check.fY - fAbove.fY) * (fBelow.fX - fAbove.fX), - (fBelow.fY - fAbove.fY) * (check.fX - fAbove.fX)); - } else { - const SkPoint& check = fBelow.fY <= edge->fBelow.fY - && fBelow != edge->fBelow ? fBelow : fAbove; - ulps = UlpsDiff((edge->fBelow.fY - edge->fAbove.fY) - * (check.fX - edge->fAbove.fX), - (check.fY - edge->fAbove.fY) - * (edge->fBelow.fX - edge->fAbove.fX)); - } -#else double t1, t2, b1, b2; + // This logic must be kept in sync with operator < if (edge->fAbove.fY < fAbove.fY) { t1 = (edge->fBelow.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX); t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fBelow.fX - edge->fAbove.fX); @@ -1683,8 +1698,49 @@ public: SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(), ulps); #endif -#endif - return ulps >= 0 && ulps <= 32; + if (ulps < 0 || ulps > 32) { + return false; + } + SkPath::Verb verb = lastVerb(); + SkPath::Verb edgeVerb = edge->lastVerb(); + if (verb == SkPath::kLine_Verb && edgeVerb == SkPath::kLine_Verb) { + return true; + } + if (verb != SkPath::kLine_Verb && edgeVerb != SkPath::kLine_Verb) { + return false; + } + + double ts[2]; + bool isLine = true; + bool curveQuad = true; + if (verb == SkPath::kCubic_Verb) { + ts[0] = (fTAbove * 2 + fTBelow) / 3; + ts[1] = (fTAbove + fTBelow * 2) / 3; + curveQuad = isLine = false; + } else if (edgeVerb == SkPath::kCubic_Verb) { + ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3; + ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3; + curveQuad = false; + } else if (verb == SkPath::kQuad_Verb) { + ts[0] = fTAbove; + ts[1] = (fTAbove + fTBelow) / 2; + isLine = false; + } else { + SkASSERT(edgeVerb == SkPath::kQuad_Verb); + ts[0] = edge->fTAbove; + ts[1] = (edge->fTAbove + edge->fTBelow) / 2; + } + const SkPoint* curvePts = isLine ? edge->lastPoints() : lastPoints(); + const ActiveEdge* lineEdge = isLine ? this : edge; + SkPoint curveSample[2]; + for (int index = 0; index < 2; ++index) { + if (curveQuad) { + QuadXYAtT(curvePts, ts[index], &curveSample[index]); + } else { + CubicXYAtT(curvePts, ts[index], &curveSample[index]); + } + } + return IsCoincident(curveSample, lineEdge->fAbove, lineEdge->fBelow); } double nextT() const { @@ -1715,15 +1771,35 @@ public: return fWorkEdge.fEdge->fID; } +private: + // utility used only by swapUnordered + void extractAboveBelow(ActiveEdge& extracted) const { + SkPoint curve[4]; + switch (lastVerb()) { + case SkPath::kLine_Verb: + extracted.fAbove = fAbove; + extracted.fBelow = fBelow; + return; + case SkPath::kQuad_Verb: + QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve); + break; + case SkPath::kCubic_Verb: + CubicSubDivide(lastPoints(), fTAbove, fTBelow, curve); + break; + default: + SkASSERT(0); + } + extracted.fAbove = curve[0]; + extracted.fBelow = curve[1]; + } + public: WorkEdge fWorkEdge; const SkTDArray* fTs; SkPoint fAbove; SkPoint fBelow; -#if DEBUG_ABOVE_BELOW - double fTAbove; + double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics double fTBelow; -#endif SkScalar fYBottom; int fCoincident; int fTIndex; @@ -2170,49 +2246,91 @@ static SkScalar adjustCoincident(SkTDArray& edgeList, if (edgeCount == 0) { return bottom; } - ActiveEdge* activePtr = edgeList[0]; + ActiveEdge* activePtr, * nextPtr = edgeList[0]; size_t index; bool foundCoincident = false; int firstIndex = 0; for (index = 1; index < edgeCount; ++index) { - ActiveEdge* nextPtr = edgeList[index]; + activePtr = nextPtr; + nextPtr = edgeList[index]; + if (firstIndex != index - 1 && !activePtr->fSkip) { + if (nextPtr->fWorkEdge.verb() == SkPath::kLine_Verb + && activePtr->isUnordered(nextPtr)) { + start here; + // swap the line with the curve + // back up to the previous edge and retest + SkTSwap(edgeList[index - 1], edgeList[index]); + SkASSERT(index > 1); + index -= 2; + continue; + } + } bool closeCall = false; activePtr->fCoincident = firstIndex; - if (activePtr->isCoincidentWith(nextPtr, y) + if (activePtr->isCoincidentWith(nextPtr) || (closeCall = activePtr->tooCloseToCall(nextPtr))) { activePtr->fSkip = nextPtr->fSkip = foundCoincident = true; activePtr->fCloseCall = nextPtr->fCloseCall = closeCall; + } else if (activePtr->isUnordered(nextPtr)) { + foundCoincident = true; } else { firstIndex = index; } - activePtr = nextPtr; } - activePtr->fCoincident = firstIndex; + nextPtr->fCoincident = firstIndex; if (!foundCoincident) { return bottom; } int winding = 0; - activePtr = edgeList[0]; + nextPtr = edgeList[0]; for (index = 1; index < edgeCount; ++index) { int priorWinding = winding; winding += activePtr->fWorkEdge.winding(); - ActiveEdge* nextPtr = edgeList[index]; - if (activePtr->fCoincident == nextPtr->fCoincident) { - // the coincident edges may not have been sorted above -- advance - // the edges and resort if needed - // OPTIMIZE: if sorting is done incrementally as new edges are added - // and not all at once as is done here, fold this test into the - // current less than test. + activePtr = nextPtr; + nextPtr = edgeList[index]; + SkASSERT(activePtr == edgeList[index - 1]); + SkASSERT(nextPtr == edgeList[index]); + if (activePtr->fCoincident != nextPtr->fCoincident) { + continue; + } + // the coincident edges may not have been sorted above -- advance + // the edges and resort if needed + // OPTIMIZE: if sorting is done incrementally as new edges are added + // and not all at once as is done here, fold this test into the + // current less than test. + while ((!activePtr->fSkip || !nextPtr->fSkip) + && activePtr->fCoincident == nextPtr->fCoincident) { + if (activePtr->swapUnordered(nextPtr, bottom)) { + winding -= activePtr->fWorkEdge.winding(); + SkASSERT(activePtr == edgeList[index - 1]); + SkASSERT(nextPtr == edgeList[index]); + SkTSwap(edgeList[index - 1], edgeList[index]); + if (--index == 0) { + winding += activePtr->fWorkEdge.winding(); + break; + } + // back up one + activePtr = edgeList[index - 1]; + continue; + } + SkASSERT(activePtr == edgeList[index - 1]); + SkASSERT(nextPtr == edgeList[index]); + break; + } + if (activePtr->fSkip && nextPtr->fSkip) { if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr, priorWinding, winding, windingMask) : activePtr->swapCoincident(nextPtr, bottom)) { winding -= activePtr->fWorkEdge.winding(); + SkASSERT(activePtr == edgeList[index - 1]); + SkASSERT(nextPtr == edgeList[index]); SkTSwap(edgeList[index - 1], edgeList[index]); SkTSwap(activePtr, nextPtr); winding += activePtr->fWorkEdge.winding(); + SkASSERT(activePtr == edgeList[index - 1]); + SkASSERT(nextPtr == edgeList[index]); } } - activePtr = nextPtr; } int firstCoincidentWinding = 0; ActiveEdge* firstCoincident = NULL; @@ -2221,8 +2339,9 @@ static SkScalar adjustCoincident(SkTDArray& edgeList, for (index = 1; index < edgeCount; ++index) { int priorWinding = winding; winding += activePtr->fWorkEdge.winding(); - ActiveEdge* nextPtr = edgeList[index]; - if (activePtr->fCoincident == nextPtr->fCoincident) { + nextPtr = edgeList[index]; + if (activePtr->fSkip && nextPtr->fSkip + && activePtr->fCoincident == nextPtr->fCoincident) { if (!firstCoincident) { firstCoincident = activePtr; firstCoincidentWinding = priorWinding; @@ -2294,32 +2413,26 @@ static void stitchEdge(SkTDArray& edgeList, SkScalar y, ActiveEdge** activeHandle = edgeList.begin() - 1; ActiveEdge** lastActive = edgeList.end(); const int tab = 7; // FIXME: debugging only - if (gShowDebugf) { - SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom); - } +#if DEBUG_STITCH_EDGE + SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom); +#endif while (++activeHandle != lastActive) { ActiveEdge* activePtr = *activeHandle; const WorkEdge& wt = activePtr->fWorkEdge; int lastWinding = winding; winding += wt.winding(); - if (gShowDebugf) { - SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d" -#if DEBUG_ABOVE_BELOW - " above=%1.9g below=%1.9g" +#if DEBUG_STITCH_EDGE + SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d" + " above=%1.9g below=%1.9g\n", + tab-4, "", activePtr->ID(), lastWinding, + winding, activePtr->fSkip, activePtr->fCloseCall, + activePtr->fTAbove, activePtr->fTBelow); #endif - "\n", - tab-4, "", activePtr->ID(), lastWinding, - winding, activePtr->fSkip, activePtr->fCloseCall -#if DEBUG_ABOVE_BELOW - , activePtr->fTAbove, activePtr->fTBelow -#endif - ); - } if (activePtr->done(bottom)) { - if (gShowDebugf) { - SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "", - activePtr->fDone, activePtr->fYBottom); - } +#if DEBUG_STITCH_EDGE + SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "", + activePtr->fDone, activePtr->fYBottom); +#endif continue; } int opener = (lastWinding & windingMask) == 0; @@ -2328,10 +2441,10 @@ static void stitchEdge(SkTDArray& edgeList, SkScalar y, bool inWinding = opener | closer; SkPoint clippedPts[4]; const SkPoint* clipped = NULL; - uint8_t verb = wt.verb(); bool moreToDo, aboveBottom; do { double currentT = activePtr->t(); + uint8_t verb = wt.verb(); const SkPoint* points = wt.fPts; double nextT; do { @@ -2389,14 +2502,13 @@ static void stitchEdge(SkTDArray& edgeList, SkScalar y, // by advancing fAbove/fBelow, the next call to sortHorizontal // will use these values if they're still valid instead of // recomputing - if (clipped[1].fY > activePtr->fBelow.fY + if (clipped[verb].fY > activePtr->fBelow.fY && bottom >= activePtr->fBelow.fY ) { activePtr->fAbove = activePtr->fBelow; - activePtr->fBelow = clipped[1]; - #if DEBUG_ABOVE_BELOW - activePtr->fTAbove = activePtr->fTBelow; + activePtr->fBelow = clipped[verb]; + activePtr->fTAbove = activePtr->fTBelow < 1 + ? activePtr->fTBelow : 0; activePtr->fTBelow = nextT; - #endif } currentT = nextT; moreToDo = activePtr->advanceT(); @@ -2423,11 +2535,15 @@ static void stitchEdge(SkTDArray& edgeList, SkScalar y, activePtr->fSkip = false; activePtr->fCloseCall = false; aboveBottom &= !activePtr->fCloseCall; - } else { + } +#if DEBUG_STITCH_EDGE + else { if (activePtr->fSkip || activePtr->fCloseCall) { - if (gShowDebugf) SkDebugf("== %1.9g\n", clippedPts[0].fY); + SkDebugf("%s skip or close == %1.9g\n", __FUNCTION__, + clippedPts[0].fY); } } +#endif } while (moreToDo & aboveBottom); } while ((moreToDo || activePtr->advance()) & aboveBottom); } diff --git a/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp index 7054683c53..c9d6bddfd6 100755 --- a/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp +++ b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp @@ -5,43 +5,12 @@ #include #include -struct State { - State() { - bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100); - bitmap.allocPixels(); - canvas = new SkCanvas(bitmap); - } - - int a; - int b; - int c; - int d; - pthread_t threadID; - SkCanvas* canvas; - SkBitmap bitmap; - bool abcIsATriangle; -}; - -void createThread(State* statePtr, void* (*test)(void* )) { - int threadError = pthread_create(&statePtr->threadID, NULL, test, - (void*) statePtr); - SkASSERT(!threadError); -} - -void waitForCompletion(State threadState[], int& threadIndex) { - for (int index = 0; index < threadIndex; ++index) { - pthread_join(threadState[index].threadID, NULL); - } - SkDebugf("."); - threadIndex = 0; -} - static void* testSimplify4x4QuadralateralsMain(void* data) { char pathStr[1024]; bzero(pathStr, sizeof(pathStr)); SkASSERT(data); - State& state = *(State*) data; + State4& state = *(State4*) data; int ax = state.a & 0x03; int ay = state.a >> 2; int bx = state.b & 0x03; @@ -105,17 +74,17 @@ static void* testSimplify4x4QuadralateralsMain(void* data) return NULL; } -const int maxThreads = gShowDebugf ? 1 : 24; +const int maxThreads = gRunTestsInOneThread ? 1 : 24; void Simplify4x4QuadralateralsThreaded_Test() { - State threadState[maxThreads]; + State4 threadState[maxThreads]; int threadIndex = 0; for (int a = 0; a < 16; ++a) { for (int b = a ; b < 16; ++b) { for (int c = b ; c < 16; ++c) { for (int d = c; d < 16; ++d) { - State* statePtr = &threadState[threadIndex]; + State4* statePtr = &threadState[threadIndex]; statePtr->a = a; statePtr->b = b; statePtr->c = c; @@ -140,7 +109,7 @@ static void* testSimplify4x4NondegeneratesMain(void* data) { char pathStr[1024]; bzero(pathStr, sizeof(pathStr)); SkASSERT(data); - State& state = *(State*) data; + State4& state = *(State4*) data; int ax = state.a & 0x03; int ay = state.a >> 2; int bx = state.b & 0x03; @@ -193,7 +162,7 @@ static void* testSimplify4x4NondegeneratesMain(void* data) { } void SimplifyNondegenerate4x4TrianglesThreaded_Test() { - State threadState[maxThreads]; + State4 threadState[maxThreads]; int threadIndex = 0; for (int a = 0; a < 15; ++a) { int ax = a & 0x03; @@ -210,7 +179,7 @@ void SimplifyNondegenerate4x4TrianglesThreaded_Test() { if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) { continue; } - State* statePtr = &threadState[threadIndex]; + State4* statePtr = &threadState[threadIndex]; statePtr->a = a; statePtr->b = b; statePtr->c = c; @@ -232,7 +201,7 @@ static void* testSimplify4x4DegeneratesMain(void* data) { char pathStr[1024]; bzero(pathStr, sizeof(pathStr)); SkASSERT(data); - State& state = *(State*) data; + State4& state = *(State4*) data; int ax = state.a & 0x03; int ay = state.a >> 2; int bx = state.b & 0x03; @@ -283,7 +252,7 @@ static void* testSimplify4x4DegeneratesMain(void* data) { } void SimplifyDegenerate4x4TrianglesThreaded_Test() { - State threadState[maxThreads]; + State4 threadState[maxThreads]; int threadIndex = 0; for (int a = 0; a < 16; ++a) { int ax = a & 0x03; @@ -294,7 +263,7 @@ void SimplifyDegenerate4x4TrianglesThreaded_Test() { for (int c = a ; c < 16; ++c) { int cx = c & 0x03; int cy = c >> 2; - State* statePtr = &threadState[threadIndex]; + State4* statePtr = &threadState[threadIndex]; statePtr->abcIsATriangle = (bx - ax) * (cy - ay) != (by - ay) * (cx - ax); statePtr->a = a; diff --git a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp index 31a5a23778..96ba2f3c5f 100644 --- a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp +++ b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp @@ -49,7 +49,188 @@ static void testSimplifyQuadratic4() { drawAsciiPaths(path, out, true); } +static void testSimplifyQuadratic5() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(0, 0, 0, 1); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic6() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(1, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(1, 0, 0, 1); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic7() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 1); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(1, 0, 0, 2); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic8() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(1, 0, 0, 2); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic9() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(1, 1); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(1, 0, 2, 2); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic10() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 1); + path.quadTo(1, 1, 1, 2); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic11() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 2); + path.close(); + path.moveTo(0, 0); + path.lineTo(2, 1); + path.quadTo(2, 2, 3, 3); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic12() { + SkPath path, out; + path.moveTo(0, 0); + path.lineTo(0, 2); + path.lineTo(0, 0); + path.close(); + path.moveTo(3, 0); + path.quadTo(1, 1, 0, 2); + path.lineTo(3, 0); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic13() { + SkPath path, out; +path.moveTo(0, 0); +path.quadTo(0, 0, 1, 0); +path.lineTo(1, 1); +path.lineTo(0, 0); +path.close(); +path.moveTo(0, 0); +path.quadTo(3, 0, 1, 1); +path.lineTo(0, 0); +path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic14() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(1, 1); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(0, 1, 2, 1); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic15() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 1, 3); + path.lineTo(3, 3); + path.close(); + path.moveTo(0, 1); + path.lineTo(1, 1); + path.quadTo(0, 3, 3, 3); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + +static void testSimplifyQuadratic16() { + SkPath path, out; + path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 1); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(1, 0, 0, 1); + path.close(); + testSimplify(path, true, out, bitmap); + drawAsciiPaths(path, out, true); +} + static void (*simplifyTests[])() = { + testSimplifyQuadratic16, + testSimplifyQuadratic15, + testSimplifyQuadratic14, + testSimplifyQuadratic13, + testSimplifyQuadratic12, + testSimplifyQuadratic11, + testSimplifyQuadratic10, + testSimplifyQuadratic9, + testSimplifyQuadratic8, + testSimplifyQuadratic7, + testSimplifyQuadratic6, + testSimplifyQuadratic5, testSimplifyQuadratic4, testSimplifyQuadratic3, testSimplifyQuadratic2, @@ -58,7 +239,7 @@ static void (*simplifyTests[])() = { static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]); -static void (*firstTest)() = testSimplifyQuadratic3; +static void (*firstTest)() = 0; static bool skipAll = false; void SimplifyQuadraticPaths_Test() { diff --git a/experimental/Intersection/EdgeWalker_Test.h b/experimental/Intersection/EdgeWalker_Test.h index 1f644a7480..f30743b2b3 100644 --- a/experimental/Intersection/EdgeWalker_Test.h +++ b/experimental/Intersection/EdgeWalker_Test.h @@ -1,8 +1,9 @@ #include "ShapeOps.h" +#include "SkBitmap.h" +#include -class SkBitmap; class SkCanvas; //extern int comparePaths(const SkPath& one, const SkPath& two); @@ -12,3 +13,19 @@ extern bool drawAsciiPaths(const SkPath& one, const SkPath& two, extern void showPath(const SkPath& path, const char* str = NULL); extern bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap, SkCanvas* canvas = 0); + +struct State4 { + State4(); + + int a; + int b; + int c; + int d; + pthread_t threadID; + SkCanvas* canvas; + SkBitmap bitmap; + bool abcIsATriangle; +}; + +void createThread(State4* statePtr, void* (*test)(void* )); +void waitForCompletion(State4 threadState[], int& threadIndex); diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp index 0844458ba3..d0b82c1695 100644 --- a/experimental/Intersection/EdgeWalker_TestUtility.cpp +++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp @@ -5,6 +5,9 @@ #include "SkPaint.h" #include +#undef SkASSERT +#define SkASSERT(cond) while (!(cond)) { sk_throw(); } + static bool gShowPath = false; static bool gComparePaths = true; static bool gDrawLastAsciiPaths = true; @@ -170,6 +173,8 @@ static int scaledDrawTheSame(const SkPath& one, const SkPath& two, return errors; } +static int max = 0; + static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap, SkCanvas* canvas) { int errors = pathsDrawTheSame(one, two, bitmap, canvas); @@ -192,17 +197,22 @@ static int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap, 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) { + if (errors > 50) { scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas); } } - if (errors > 0) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); - if (errors > 31 && gComparePathsAssert) { + if (errors > max) { + SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); + max = errors; + } + const int MAX_ERRORS = 100; + if (errors > MAX_ERRORS) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); + if (errors > MAX_ERRORS && gComparePathsAssert) { showPath(one); showPath(two, "simplified:"); SkASSERT(0); } - return errors; + return errors > MAX_ERRORS ? errors : 0; } // doesn't work yet @@ -247,3 +257,23 @@ bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap, } return comparePaths(path, out, bitmap, canvas) == 0; } + +State4::State4() { + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100); + bitmap.allocPixels(); + canvas = new SkCanvas(bitmap); +} + +void createThread(State4* statePtr, void* (*test)(void* )) { + int threadError = pthread_create(&statePtr->threadID, NULL, test, + (void*) statePtr); + SkASSERT(!threadError); +} + +void waitForCompletion(State4 threadState[], int& threadIndex) { + for (int index = 0; index < threadIndex; ++index) { + pthread_join(threadState[index].threadID, NULL); + } + SkDebugf("."); + threadIndex = 0; +} diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp index 5209eee22f..0179cb7450 100644 --- a/experimental/Intersection/Intersection_Tests.cpp +++ b/experimental/Intersection/Intersection_Tests.cpp @@ -4,8 +4,12 @@ void cubecode_test(int test); void testSimplify(); +#define TEST_QUADS_FIRST 01 + void Intersection_Tests() { +#if !TEST_QUADS_FIRST ActiveEdge_Test(); +#endif cubecode_test(1); convert_testx(); @@ -24,10 +28,17 @@ void Intersection_Tests() { SimplifyPolygonPaths_Test(); SimplifyRectangularPaths_Test(); SimplifyQuadralateralPaths_Test(); - + +#if TEST_QUADS_FIRST + ActiveEdge_Test(); + Simplify4x4QuadraticsThreaded_Test(); +#endif SimplifyDegenerate4x4TrianglesThreaded_Test(); SimplifyNondegenerate4x4TrianglesThreaded_Test(); Simplify4x4QuadralateralsThreaded_Test(); +#if !TEST_QUADS_FIRST + Simplify4x4QuadraticsThreaded_Test(); +#endif QuadraticCoincidence_Test(); QuadraticReduceOrder_Test(); diff --git a/experimental/Intersection/Intersection_Tests.h b/experimental/Intersection/Intersection_Tests.h index 432f83fcf0..e0d892df8c 100644 --- a/experimental/Intersection/Intersection_Tests.h +++ b/experimental/Intersection/Intersection_Tests.h @@ -18,6 +18,7 @@ void SimplifyPolygonPaths_Test(); void SimplifyQuadralateralPaths_Test(); void SimplifyQuadraticPaths_Test(); void Simplify4x4QuadralateralsThreaded_Test(); +void Simplify4x4QuadraticsThreaded_Test(); void SimplifyRectangularPaths_Test(); void QuadraticBezierClip_Test(); void QuadraticCoincidence_Test(); diff --git a/experimental/Intersection/LineParameterization.cpp b/experimental/Intersection/LineParameterization.cpp index 4455cf21a2..1611a318bc 100644 --- a/experimental/Intersection/LineParameterization.cpp +++ b/experimental/Intersection/LineParameterization.cpp @@ -28,6 +28,29 @@ bool implicit_matches(const _Line& one, const _Line& two) { return true; } +bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps) { + _Point oneD, twoD; + tangent(one, oneD); + tangent(two, twoD); + /* See if the slopes match, i.e. + dx1 / dy1 == dx2 / dy2 + (dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2 + dy2 * dx1 == dy1 * dx2 + */ + int diff = UlpsDiff(oneD.x * twoD.y, twoD.x * oneD.y); + if (diff < 0 || diff > ulps) { + return false; + } + /* See if the axis intercepts match, i.e. + y0 - x0 * dy / dx == y1 - x1 * dy / dx + dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx) + dx * y0 - x0 * dy == dx * y1 - x1 * dy + */ + diff = UlpsDiff(oneD.x * one[0].y - oneD.y * one[0].x, + oneD.x * two[0].y - oneD.y * two[0].x); + return diff >= 0 && diff <= ulps; +} + void tangent(const _Line& line, _Point& result) { result.x = line[0].x - line[1].x; result.y = line[0].y - line[1].y; diff --git a/experimental/Intersection/QuadraticUtilities.cpp b/experimental/Intersection/QuadraticUtilities.cpp index 51a7569609..ea4cd851a7 100644 --- a/experimental/Intersection/QuadraticUtilities.cpp +++ b/experimental/Intersection/QuadraticUtilities.cpp @@ -9,12 +9,19 @@ int quadraticRoots(double A, double B, double C, double t[2]) { } double squareRt = sqrt(square); double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2; + double ratio; int foundRoots = 0; if ((Q <= A) ^ (Q < 0)) { - t[foundRoots++] = Q / A; + ratio = Q / A; + if (!isnan(ratio)) { + t[foundRoots++] = ratio; + } } if ((C <= Q) ^ (C < 0)) { - t[foundRoots++] = C / Q; + ratio = C / Q; + if (!isnan(ratio)) { + t[foundRoots++] = ratio; + } } return foundRoots; } diff --git a/experimental/Intersection/ShapeOps.h b/experimental/Intersection/ShapeOps.h index d532e872dc..389275d9e2 100644 --- a/experimental/Intersection/ShapeOps.h +++ b/experimental/Intersection/ShapeOps.h @@ -3,4 +3,4 @@ void contourBounds(const SkPath& path, SkTDArray& boundsArray); void simplify(const SkPath& path, bool asFill, SkPath& simple); -extern const bool gShowDebugf; // FIXME: remove once debugging is complete \ No newline at end of file +extern const bool gRunTestsInOneThread; // FIXME: remove once debugging is complete \ No newline at end of file diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm index 2078396bad..4f12c91e79 100644 --- a/experimental/Intersection/op.htm +++ b/experimental/Intersection/op.htm @@ -110,14 +110,150 @@ path.quadTo(37.0710678, 12.9289322, 40, 10); path.lineTo(0, 10); path.close(); +
+ path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 1); + path.quadTo(1, 1, 1, 2); + path.lineTo(0, 0); + path.close(); +
+ +
+original: +path.moveTo(0, 0); +path.lineTo(3, 1); +path.lineTo(0, 0); +path.close(); +path.moveTo(1, 0); +path.lineTo(0, 1); +path.quadTo(2, 1, 3, 3); +path.lineTo(1, 0); +path.close(); +simplified: +path.moveTo(1, 0); +path.lineTo(0, 1); +path.quadTo(1.42857146, 1, 2.34693885, 2.02040815); +path.lineTo(1.28571427, 0.428571433); +path.lineTo(1, 0); +path.close(); +path.moveTo(2.34693885, 2.02040815); +path.quadTo(2.71428561, 2.42857146, 3, 3); +path.lineTo(2.34693885, 2.02040815); +path.close(); +
+ +
+ path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(0, 2); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(2, 1); + path.quadTo(2, 2, 3, 3); + path.lineTo(0, 0); + path.close(); +
+ +
+original: +path.moveTo(0, 0); +path.lineTo(0, 2); +path.lineTo(0, 0); +path.close(); +path.moveTo(3, 0); +path.quadTo(1, 1, 0, 2); +path.lineTo(3, 0); +path.close(); +simplified: +path.moveTo(0, 0); +path.lineTo(0, 2); +path.quadTo(1, 1, 3, 0); +path.lineTo(0, 0); +path.close(); +
+ +
+original: +path.moveTo(0, 0); +path.quadTo(0, 0, 1, 0); +path.lineTo(1, 1); +path.lineTo(0, 0); +path.close(); +path.moveTo(0, 0); +path.quadTo(3, 0, 1, 1); +path.lineTo(0, 0); +path.close(); +simplified: +path.moveTo(0, 0); +path.lineTo(1, 1); +path.lineTo(1, 0); +path.lineTo(0, 0); +
+ +
+ path.moveTo(0, 0); + path.quadTo(0, 0, 0, 0); + path.lineTo(1, 1); + path.lineTo(0, 0); + path.close(); + path.moveTo(0, 0); + path.lineTo(0, 0); + path.quadTo(0, 1, 2, 1); + path.lineTo(0, 0); + path.close(); +
+ +
+original: +path.moveTo(0, 0); +path.quadTo(0, 0, 1, 3); +path.lineTo(3, 3); +path.lineTo(0, 0); +path.close(); +path.moveTo(0, 1); +path.lineTo(1, 1); +path.quadTo(0, 3, 3, 3); +path.lineTo(0, 1); +path.close(); +simplified: +path.moveTo(0, 0); +path.lineTo(0.333333343, 1); +path.lineTo(0, 1); +path.lineTo(0.428571433, 1.28571427); +path.lineTo(0.333333343, 1); +path.lineTo(1, 1); +path.lineTo(0, 0); +path.close(); +path.moveTo(1, 1); +path.quadTo(0.857142866, 1.28571427, 0.795918345, 1.53061223); +path.lineTo(0.428571433, 1.28571427); +path.lineTo(1, 3); +path.lineTo(3, 3); +path.lineTo(0.795918345, 1.53061223); +path.quadTo(0.428571433, 3, 3, 3); +path.lineTo(1, 1); +path.close(); +