diff options
author | caryclark <caryclark@google.com> | 2015-04-23 09:13:37 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-23 09:13:37 -0700 |
commit | 03b03cad01628146bbb8d4f33c073bd0c77ee558 (patch) | |
tree | 3daa35fc7a85abd54f6d48e23d3f8f665b677dc5 /src/pathops | |
parent | 4b17fa353e777de309ca8b0706f1d3e326b59822 (diff) |
working on initial winding for cubics
Path ops works well for all tests except for cubics.
Isolate failures caused by cubics, and do a better job of computing
the initial winding for cubics.
TBR=reed@google.com
BUG=skia:3588
Review URL: https://codereview.chromium.org/1096923003
Diffstat (limited to 'src/pathops')
-rw-r--r-- | src/pathops/SkOpAngle.h | 2 | ||||
-rw-r--r-- | src/pathops/SkOpContour.cpp | 10 | ||||
-rw-r--r-- | src/pathops/SkOpContour.h | 2 | ||||
-rw-r--r-- | src/pathops/SkOpEdgeBuilder.cpp | 7 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.cpp | 109 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.h | 18 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCommon.cpp | 3 | ||||
-rw-r--r-- | src/pathops/SkPathOpsConic.cpp | 41 | ||||
-rw-r--r-- | src/pathops/SkPathOpsConic.h | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCubic.cpp | 90 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCubic.h | 14 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCurve.h | 25 | ||||
-rw-r--r-- | src/pathops/SkPathOpsDebug.cpp | 6 | ||||
-rw-r--r-- | src/pathops/SkPathOpsDebug.h | 19 | ||||
-rw-r--r-- | src/pathops/SkPathOpsOp.cpp | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsPoint.h | 14 | ||||
-rw-r--r-- | src/pathops/SkPathOpsQuad.cpp | 53 | ||||
-rw-r--r-- | src/pathops/SkPathOpsQuad.h | 5 | ||||
-rw-r--r-- | src/pathops/SkPathOpsSimplify.cpp | 2 |
19 files changed, 277 insertions, 147 deletions
diff --git a/src/pathops/SkOpAngle.h b/src/pathops/SkOpAngle.h index 9947b43471..7088dd716f 100644 --- a/src/pathops/SkOpAngle.h +++ b/src/pathops/SkOpAngle.h @@ -42,7 +42,7 @@ struct SkOpAngle { return SkDEBUGRELEASE(fID, -1); } -#if DEBUG_SORT +#if DEBUG_SORT || DEBUG_SWAP_TOP void debugLoop() const; #endif diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp index ab1a37b09a..ce9439ac26 100644 --- a/src/pathops/SkOpContour.cpp +++ b/src/pathops/SkOpContour.cpp @@ -10,17 +10,18 @@ #include "SkReduceOrder.h" #include "SkTSort.h" -void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator) { +SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], + SkChunkAlloc* allocator) { switch (verb) { case SkPath::kLine_Verb: { SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 2); memcpy(ptStorage, pts, sizeof(SkPoint) * 2); - appendSegment(allocator).addLine(ptStorage, this); + return appendSegment(allocator).addLine(ptStorage, this); } break; case SkPath::kQuad_Verb: { SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 3); memcpy(ptStorage, pts, sizeof(SkPoint) * 3); - appendSegment(allocator).addQuad(ptStorage, this); + return appendSegment(allocator).addQuad(ptStorage, this); } break; case SkPath::kConic_Verb: { SkASSERT(0); // the original curve is a cubic, which will never reduce to a conic @@ -28,11 +29,12 @@ void SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc case SkPath::kCubic_Verb: { SkPoint* ptStorage = SkOpTAllocator<SkPoint>::AllocateArray(allocator, 4); memcpy(ptStorage, pts, sizeof(SkPoint) * 4); - appendSegment(allocator).addCubic(ptStorage, this); + return appendSegment(allocator).addCubic(ptStorage, this); } break; default: SkASSERT(0); } + return NULL; } SkOpSegment* SkOpContour::nonVerticalSegment(SkOpSpanBase** start, SkOpSpanBase** end) { diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h index 184ee92ddf..c86f27b22e 100644 --- a/src/pathops/SkOpContour.h +++ b/src/pathops/SkOpContour.h @@ -40,7 +40,7 @@ public: appendSegment(allocator).addCubic(pts, this); } - void addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator); + SkOpSegment* addCurve(SkPath::Verb verb, const SkPoint pts[4], SkChunkAlloc* allocator); void addLine(SkPoint pts[2], SkChunkAlloc* allocator) { appendSegment(allocator).addLine(pts, this); diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp index 24ca9b1f56..7216830993 100644 --- a/src/pathops/SkOpEdgeBuilder.cpp +++ b/src/pathops/SkOpEdgeBuilder.cpp @@ -202,7 +202,8 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) { // split self-intersecting cubics in two before proceeding // if the cubic is convex, it doesn't self intersect. SkScalar loopT; - if (SkDCubic::ComplexBreak(pointsPtr, &loopT)) { + SkDCubic::CubicType cubicType; + if (SkDCubic::ComplexBreak(pointsPtr, &loopT, &cubicType)) { SkPoint cubicPair[7]; SkChopCubicAt(pointsPtr, cubicPair, loopT); if (!SkScalarsAreFinite(&cubicPair[0].fX, SK_ARRAY_COUNT(cubicPair) * 2)) { @@ -220,8 +221,8 @@ bool SkOpEdgeBuilder::walk(SkChunkAlloc* allocator) { for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) { force_small_to_zero(&curve2[index]); } - fCurrentContour->addCurve(v1, curve1, fAllocator); - fCurrentContour->addCurve(v2, curve2, fAllocator); + fCurrentContour->addCurve(v1, curve1, fAllocator)->setCubicType(cubicType); + fCurrentContour->addCurve(v2, curve2, fAllocator)->setCubicType(cubicType); } else { fCurrentContour->addCubic(pointsPtr, fAllocator); } diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index 161eb33765..b14be78d06 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -107,13 +107,10 @@ SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) { SkPoint topPt = {SK_ScalarMax, SK_ScalarMax}; // see if either end is not done since we want smaller Y of the pair bool lastDone = true; - double lastT = -1; SkOpSpanBase* span = &fHead; + SkOpSpanBase* lastSpan = NULL; do { - if (lastDone && (span->final() || span->upCast()->done())) { - goto next; - } - { + if (!lastDone || (!span->final() && !span->upCast()->done())) { const SkPoint& xy = span->pt(); if (topPt.fY > xy.fY || (topPt.fY == xy.fY && topPt.fX > xy.fX)) { topPt = xy; @@ -121,19 +118,22 @@ SkPoint SkOpSegment::activeLeftTop(SkOpSpanBase** firstSpan) { *firstSpan = span; } } - if (fVerb != SkPath::kLine_Verb && !lastDone) { - SkPoint curveTop = (*CurveTop[fVerb])(fPts, fWeight, lastT, span->t()); - if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY - && topPt.fX > curveTop.fX)) { + if (fVerb != SkPath::kLine_Verb && !lastDone + && fCubicType != SkDCubic::kSplitAtMaxCurvature_SkDCubicType) { + double curveTopT; + SkPoint curveTop = (*CurveTop[fVerb])(fPts, fWeight, lastSpan->t(), span->t(), + &curveTopT); + if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY && topPt.fX > curveTop.fX)) { topPt = curveTop; if (firstSpan) { - *firstSpan = span; + const SkPoint& lastXY = lastSpan->pt(); + *firstSpan = lastXY.fY > xy.fY || (lastXY.fY == xy.fY && lastXY.fX > xy.fX) + ? span : lastSpan; } } } - lastT = span->t(); + lastSpan = span; } -next: if (span->final()) { break; } @@ -490,52 +490,12 @@ void SkOpSegment::checkAngleCoin(SkOpCoincidence* coincidences, SkChunkAlloc* al // from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order bool SkOpSegment::clockwise(const SkOpSpanBase* start, const SkOpSpanBase* end, bool* swap) const { SkASSERT(fVerb != SkPath::kLine_Verb); - SkOpCurve edge; - if (fVerb == SkPath::kCubic_Verb) { - double startT = start->t(); - double endT = end->t(); - bool flip = startT > endT; - SkDCubic cubic; - cubic.set(fPts); - double inflectionTs[2]; - int inflections = cubic.findInflections(inflectionTs); - for (int index = 0; index < inflections; ++index) { - double inflectionT = inflectionTs[index]; - if (between(startT, inflectionT, endT)) { - if (flip) { - if (!roughly_equal(inflectionT, endT)) { - startT = inflectionT; - } - } else { - if (!roughly_equal(inflectionT, startT)) { - endT = inflectionT; - } - } - } - } - SkDCubic part = cubic.subDivide(startT, endT); - edge.set(part); - } else { - subDivide(start, end, &edge); + if (fVerb != SkPath::kCubic_Verb) { + SkOpCurve edge; + this->subDivide(start, end, &edge); + return SkDQuad::Clockwise(edge, swap); } - bool sumSet = false; - int points = SkPathOpsVerbToPoints(fVerb); - double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY); - if (!sumSet) { - for (int idx = 0; idx < points; ++idx){ - sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY); - } - } - if (fVerb == SkPath::kCubic_Verb) { - SkDCubic cubic; - cubic.set(edge.fPts); - *swap = sum > 0 && !cubic.monotonicInY(); - } else { - SkDQuad quad; - quad.set(edge.fPts); - *swap = sum > 0 && !quad.monotonicInY(); - } - return sum <= 0; + return SkDCubic::Clockwise(fPts, start->t(), end->t(), swap); } void SkOpSegment::ComputeOneSum(const SkOpAngle* baseAngle, SkOpAngle* nextAngle, @@ -1116,6 +1076,10 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS SkScalar top = SK_ScalarMax; const SkOpAngle* firstAngle = NULL; const SkOpAngle* angle = baseAngle; +#if DEBUG_SWAP_TOP + SkDebugf("-%s- baseAngle\n", __FUNCTION__); + baseAngle->debugLoop(); +#endif do { if (!angle->unorderable()) { const SkOpSegment* next = angle->segment(); @@ -1134,8 +1098,8 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS if (!firstAngle) { return NULL; // if all are unorderable, give up } -#if DEBUG_SORT - SkDebugf("%s\n", __FUNCTION__); +#if DEBUG_SWAP_TOP + SkDebugf("-%s- firstAngle\n", __FUNCTION__); firstAngle->debugLoop(); #endif // skip edges that have already been processed @@ -1163,20 +1127,26 @@ SkOpSegment* SkOpSegment::findTop(bool firstPass, SkOpSpanBase** startPtr, SkOpS SkOpSpanBase* start = *startPtr; SkOpSpanBase* end = *endPtr; bool swap; - if (!leftSegment->clockwise(start, end, &swap)) { - #if DEBUG_SWAP_TOP - SkDebugf("%s swap=%d inflections=%d monotonic=%d\n", - __FUNCTION__, - swap, leftSegment->debugInflections(start, end), - leftSegment->monotonicInY(start, end)); - #endif - if (swap) { + bool cw = leftSegment->clockwise(start, end, &swap); +#if DEBUG_SWAP_TOP + SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n", + __FUNCTION__, leftSegment->debugID(), start->t(), end->t(), + start->t() < end->t() ? '-' : '+', cw, + swap, leftSegment->debugInflections(start, end), + leftSegment->monotonicInY(start, end)); +#endif + if (!cw && swap) { // FIXME: I doubt it makes sense to (necessarily) swap if the edge was not the first // sorted but merely the first not already processed (i.e., not done) - SkTSwap(*startPtr, *endPtr); - } + SkTSwap(*startPtr, *endPtr); } // FIXME: clockwise isn't reliable -- try computing swap from tangent ? + } else { +#if DEBUG_SWAP_TOP + SkDebugf("%s id=%d s=%1.9g e=%1.9g (%c) cw=%d swap=%d inflections=%d monotonic=%d\n", + __FUNCTION__, leftSegment->debugID(), (*startPtr)->t(), (*endPtr)->t(), + (*startPtr)->t() < (*endPtr)->t() ? '-' : '+', -1, -1, -1, 1); +#endif } return leftSegment; } @@ -1191,6 +1161,7 @@ void SkOpSegment::init(SkPoint pts[], SkScalar weight, SkOpContour* contour, SkP fPts = pts; fWeight = weight; fVerb = verb; + fCubicType = SkDCubic::kUnsplit_SkDCubicType; fCount = 0; fDoneCount = 0; fVisited = false; diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h index 65ed95c691..f878f5e64e 100644 --- a/src/pathops/SkOpSegment.h +++ b/src/pathops/SkOpSegment.h @@ -11,6 +11,7 @@ #include "SkOpSpan.h" #include "SkOpTAllocator.h" #include "SkPathOpsBounds.h" +#include "SkPathOpsCubic.h" #include "SkPathOpsCurve.h" struct SkDCurve; @@ -45,14 +46,16 @@ public: bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end); bool activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sumWinding); - void addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) { + SkOpSegment* addConic(SkPoint pts[3], SkScalar weight, SkOpContour* parent) { init(pts, weight, parent, SkPath::kConic_Verb); fBounds.setConicBounds(pts, weight); + return this; } - void addCubic(SkPoint pts[4], SkOpContour* parent) { + SkOpSegment* addCubic(SkPoint pts[4], SkOpContour* parent) { init(pts, 1, parent, SkPath::kCubic_Verb); fBounds.setCubicBounds(pts, 1); + return this; } void addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path, @@ -65,9 +68,10 @@ public: return angle; } - void addLine(SkPoint pts[2], SkOpContour* parent) { + SkOpSegment* addLine(SkPoint pts[2], SkOpContour* parent) { init(pts, 1, parent, SkPath::kLine_Verb); fBounds.set(pts, 2); + return this; } SkOpPtT* addMissing(double t, SkOpSegment* opp, SkChunkAlloc* ); @@ -82,9 +86,10 @@ public: return angle; } - void addQuad(SkPoint pts[3], SkOpContour* parent) { + SkOpSegment* addQuad(SkPoint pts[3], SkOpContour* parent) { init(pts, 1, parent, SkPath::kQuad_Verb); fBounds.setQuadBounds(pts, 1); + return this; } SkOpPtT* addT(double t, AllowAlias , SkChunkAlloc* ); @@ -297,6 +302,10 @@ public: fContour = contour; } + void setCubicType(SkDCubic::CubicType cubicType) { + fCubicType = cubicType; + } + void setNext(SkOpSegment* next) { fNext = next; } @@ -389,6 +398,7 @@ private: int fCount; // number of spans (one for a non-intersecting segment) int fDoneCount; // number of processed spans (zero initially) SkPath::Verb fVerb; + SkDCubic::CubicType fCubicType; bool fVisited; // used by missing coincidence check SkDEBUGCODE(int fID); }; diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index a16e811ea9..e766f5abe2 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -446,6 +446,9 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) { SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune SkOpContour contour; SkOpGlobalState globalState(NULL SkDEBUGPARAMS(&contour)); +#if DEBUG_SHOW_TEST_NAME + SkDebugf("</div>\n"); +#endif #if DEBUG_PATH_CONSTRUCTION SkDebugf("%s\n", __FUNCTION__); #endif diff --git a/src/pathops/SkPathOpsConic.cpp b/src/pathops/SkPathOpsConic.cpp index 1b544e405f..869a406dce 100644 --- a/src/pathops/SkPathOpsConic.cpp +++ b/src/pathops/SkPathOpsConic.cpp @@ -82,25 +82,6 @@ SkDPoint SkDConic::ptAtT(double t) const { return result; } -SkDPoint SkDConic::top(double startT, double endT) const { - SkDConic sub = subDivide(startT, endT); - SkDPoint topPt = sub[0]; - if (topPt.fY > sub[2].fY || (topPt.fY == sub[2].fY && topPt.fX > sub[2].fX)) { - topPt = sub[2]; - } - if (!between(sub[0].fY, sub[1].fY, sub[2].fY)) { - double extremeT; - if (FindExtrema(&sub[0].fY, sub.fWeight, &extremeT)) { - extremeT = startT + (endT - startT) * extremeT; - SkDPoint test = ptAtT(extremeT); - if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) { - topPt = test; - } - } - } - return topPt; -} - /* see quad subdivide for rationale */ SkDConic SkDConic::subDivide(double t1, double t2) const { double ax = conic_eval_numerator(&fPts[0].fX, fWeight, t1); @@ -130,3 +111,25 @@ SkDPoint SkDConic::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, do *weight = chopped.fWeight; return chopped[1]; } + +SkDPoint SkDConic::top(double startT, double endT, double* topT) const { + SkDConic sub = subDivide(startT, endT); + SkDPoint topPt = sub[0]; + *topT = startT; + if (topPt.fY > sub[2].fY || (topPt.fY == sub[2].fY && topPt.fX > sub[2].fX)) { + *topT = endT; + topPt = sub[2]; + } + if (!between(sub[0].fY, sub[1].fY, sub[2].fY)) { + double extremeT; + if (FindExtrema(&sub[0].fY, sub.fWeight, &extremeT)) { + extremeT = startT + (endT - startT) * extremeT; + SkDPoint test = ptAtT(extremeT); + if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) { + *topT = extremeT; + topPt = test; + } + } + } + return topPt; +} diff --git a/src/pathops/SkPathOpsConic.h b/src/pathops/SkPathOpsConic.h index dce7032ddc..8251901554 100644 --- a/src/pathops/SkPathOpsConic.h +++ b/src/pathops/SkPathOpsConic.h @@ -109,7 +109,7 @@ struct SkDConic { return conic.subDivide(a, c, t1, t2, newWeight); } - SkDPoint top(double startT, double endT) const; + SkDPoint top(double startT, double endT, double* topT) const; // utilities callable by the user from the debugger when the implementation code is linked in void dump() const; diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp index a44d29bb0f..63f828fb22 100644 --- a/src/pathops/SkPathOpsCubic.cpp +++ b/src/pathops/SkPathOpsCubic.cpp @@ -8,6 +8,7 @@ #include "SkLineParameters.h" #include "SkPathOpsConic.h" #include "SkPathOpsCubic.h" +#include "SkPathOpsCurve.h" #include "SkPathOpsLine.h" #include "SkPathOpsQuad.h" #include "SkPathOpsRect.h" @@ -74,14 +75,66 @@ double SkDCubic::calcPrecision() const { return (width > height ? width : height) / gPrecisionUnit; } -bool SkDCubic::clockwise() const { - double sum = (fPts[0].fX - fPts[3].fX) * (fPts[0].fY + fPts[3].fY); - for (int idx = 0; idx < 3; ++idx) { - sum += (fPts[idx + 1].fX - fPts[idx].fX) * (fPts[idx + 1].fY + fPts[idx].fY); +bool SkDCubic::clockwise(bool* swap) const { + SkDPoint lastPt = fPts[kPointLast]; + SkDPoint firstPt = fPts[0]; + double sum = 0; + // pick the control point furthest from the line connecting the end points + SkLineParameters lineParameters; + lineParameters.cubicEndPoints(*this, 0, 3); + lineParameters.normalize(); + double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY), + fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY); + double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY), + fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY); + largest = SkTMax(largest, -tiniest); + double pt1dist = lineParameters.controlPtDistance(*this, 1); + double pt2dist = lineParameters.controlPtDistance(*this, 2); +#if DEBUG_SWAP_TOP + SkDebugf("%s pt1dist=%1.9g pt2dist=%1.9g\n", __FUNCTION__, pt1dist, pt2dist); +#endif + int furthest; + if (!approximately_zero_when_compared_to(pt1dist, largest) + && !approximately_zero_when_compared_to(pt2dist, largest) && pt1dist * pt2dist < 0) { + furthest = 2; + } else { + furthest = fabs(pt1dist) < fabs(pt2dist) ? 2 : 1; + } + for (int idx = 1; idx <= 3; ++idx) { + sum += (firstPt.fX - lastPt.fX) * (firstPt.fY + lastPt.fY); + lastPt = firstPt; + firstPt = idx == 1 ? fPts[furthest] : fPts[kPointLast]; } + *swap = sum > 0 && !this->monotonicInY(); return sum <= 0; } +bool SkDCubic::Clockwise(const SkPoint* pts, double startT, double endT, bool* swap) { + SkDCubic cubic; + cubic.set(pts); +#if 0 + bool flip = startT > endT; + double inflectionTs[2]; + int inflections = cubic.findInflections(inflectionTs); + for (int index = 0; index < inflections; ++index) { + double inflectionT = inflectionTs[index]; + if (between(startT, inflectionT, endT)) { + if (flip) { + if (!roughly_equal(inflectionT, endT)) { + startT = inflectionT; + } + } else { + if (!roughly_equal(inflectionT, startT)) { + endT = inflectionT; + } + } + } + } +#endif + SkDCubic part = cubic.subDivide(startT, endT); + return part.clockwise(swap); +} + void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) { *A = src[6]; // d *B = src[4] * 3; // 3*c @@ -186,7 +239,7 @@ bool SkDCubic::isLinear(int startIndex, int endIndex) const { return approximately_zero_when_compared_to(distance, largest); } -bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { +bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t, CubicType* resultType) { SkScalar d[3]; SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); if (cubicType == kLoop_SkCubicType) { @@ -203,6 +256,7 @@ bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { SkScalar smaller = SkTMax(0.f, SkTMin(ls, ms)); SkScalar larger = SkTMin(1.f, SkTMax(ls, ms)); *t = (smaller + larger) / 2; + *resultType = kSplitAtLoop_SkDCubicType; return *t > 0 && *t < 1; } } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) { @@ -213,17 +267,36 @@ bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { if (infTCount == 2) { double maxCurvature[3]; int roots = cubic.findMaxCurvature(maxCurvature); +#if DEBUG_CUBIC_SPLIT + SkDebugf("%s\n", __FUNCTION__); + cubic.dump(); + for (int index = 0; index < infTCount; ++index) { + SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]); + SkDPoint pt = cubic.ptAtT(inflectionTs[index]); + SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]); + SkDLine perp = {{pt - dPt, pt + dPt}}; + perp.dump(); + } + for (int index = 0; index < roots; ++index) { + SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]); + SkDPoint pt = cubic.ptAtT(maxCurvature[index]); + SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]); + SkDLine perp = {{pt - dPt, pt + dPt}}; + perp.dump(); + } +#endif for (int index = 0; index < roots; ++index) { if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) { *t = maxCurvature[index]; + *resultType = kSplitAtMaxCurvature_SkDCubicType; return true; } } } else if (infTCount == 1) { *t = inflectionTs[0]; + *resultType = kSplitAtInflection_SkDCubicType; return *t > 0 && *t < 1; } - return false; } return false; } @@ -446,10 +519,12 @@ int SkDCubic::findMaxCurvature(double tValues[]) const { return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues); } -SkDPoint SkDCubic::top(double startT, double endT) const { +SkDPoint SkDCubic::top(double startT, double endT, double* topT) const { SkDCubic sub = subDivide(startT, endT); SkDPoint topPt = sub[0]; + *topT = startT; if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX)) { + *topT = endT; topPt = sub[3]; } double extremeTs[2]; @@ -459,6 +534,7 @@ SkDPoint SkDCubic::top(double startT, double endT) const { double t = startT + (endT - startT) * extremeTs[index]; SkDPoint mid = ptAtT(t); if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX)) { + *topT = t; topPt = mid; } } diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h index 1263ac80ee..269073ca69 100644 --- a/src/pathops/SkPathOpsCubic.h +++ b/src/pathops/SkPathOpsCubic.h @@ -27,6 +27,13 @@ struct SkDCubic { kYAxis }; + enum CubicType { + kUnsplit_SkDCubicType, + kSplitAtLoop_SkDCubicType, + kSplitAtInflection_SkDCubicType, + kSplitAtMaxCurvature_SkDCubicType, + }; + bool collapsed() const { return fPts[0].approximatelyEqual(fPts[1]) && fPts[0].approximatelyEqual(fPts[2]) && fPts[0].approximatelyEqual(fPts[3]); @@ -50,9 +57,10 @@ struct SkDCubic { double binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const; double calcPrecision() const; SkDCubicPair chopAt(double t) const; - bool clockwise() const; + bool clockwise(bool* swap) const; + static bool Clockwise(const SkPoint* pts, double startT, double endT, bool* swap); static void Coefficients(const double* cubic, double* A, double* B, double* C, double* D); - static bool ComplexBreak(const SkPoint pts[4], SkScalar* t); + static bool ComplexBreak(const SkPoint pts[4], SkScalar* t, CubicType* cubicType); int convexHull(char order[kPointCount]) const; void debugInit() { @@ -113,7 +121,7 @@ struct SkDCubic { cubic.subDivide(a, d, t1, t2, p); } - SkDPoint top(double startT, double endT) const; + SkDPoint top(double startT, double endT, double* topT) const; SkDQuad toQuad() const; static const int gPrecisionUnit; diff --git a/src/pathops/SkPathOpsCurve.h b/src/pathops/SkPathOpsCurve.h index 5a2eeec37d..69af91cf34 100644 --- a/src/pathops/SkPathOpsCurve.h +++ b/src/pathops/SkPathOpsCurve.h @@ -26,6 +26,16 @@ struct SkOpCurve { return fPts[n]; } + void dump() const; + + void set(const SkDQuad& quad) { + for (int index = 0; index < SkDQuad::kPointCount; ++index) { + fPts[index] = quad[index].asSkPoint(); + } + SkDEBUGCODE(fWeight = 1); + SkDEBUGCODE(fVerb = SkPath::kQuad_Verb); + } + void set(const SkDCubic& cubic) { for (int index = 0; index < SkDCubic::kPointCount; ++index) { fPts[index] = cubic[index].asSkPoint(); @@ -169,28 +179,29 @@ static SkVector (* const CurveSlopeAtT[])(const SkPoint[], SkScalar , double ) = fcubic_dxdy_at_t }; -static SkPoint quad_top(const SkPoint a[3], SkScalar , double startT, double endT) { +static SkPoint quad_top(const SkPoint a[3], SkScalar , double startT, double endT, double* topT) { SkDQuad quad; quad.set(a); - SkDPoint topPt = quad.top(startT, endT); + SkDPoint topPt = quad.top(startT, endT, topT); return topPt.asSkPoint(); } -static SkPoint conic_top(const SkPoint a[3], SkScalar weight, double startT, double endT) { +static SkPoint conic_top(const SkPoint a[3], SkScalar weight, double startT, double endT, + double* topT) { SkDConic conic; conic.set(a, weight); - SkDPoint topPt = conic.top(startT, endT); + SkDPoint topPt = conic.top(startT, endT, topT); return topPt.asSkPoint(); } -static SkPoint cubic_top(const SkPoint a[4], SkScalar , double startT, double endT) { +static SkPoint cubic_top(const SkPoint a[4], SkScalar , double startT, double endT, double* topT) { SkDCubic cubic; cubic.set(a); - SkDPoint topPt = cubic.top(startT, endT); + SkDPoint topPt = cubic.top(startT, endT, topT); return topPt.asSkPoint(); } -static SkPoint (* const CurveTop[])(const SkPoint[], SkScalar , double , double ) = { +static SkPoint (* const CurveTop[])(const SkPoint[], SkScalar , double , double , double* ) = { NULL, NULL, quad_top, diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp index 61bca42fcf..36f459935d 100644 --- a/src/pathops/SkPathOpsDebug.cpp +++ b/src/pathops/SkPathOpsDebug.cpp @@ -115,6 +115,10 @@ static const char* gOpStrs[] = { "kReverseDifference_SkPathOp", }; +const char* SkPathOpsDebug::OpStr(SkPathOp op) { + return gOpStrs[op]; +} + static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) { SkDebugf(" testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]); SkDebugf("}\n"); @@ -293,7 +297,7 @@ SkString SkOpAngle::debugPart() const { } #endif -#if DEBUG_SORT +#if DEBUG_SORT || DEBUG_SWAP_TOP void SkOpAngle::debugLoop() const { const SkOpAngle* first = this; const SkOpAngle* next = this; diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h index 78fc57ab3b..8473d66c70 100644 --- a/src/pathops/SkPathOpsDebug.h +++ b/src/pathops/SkPathOpsDebug.h @@ -37,6 +37,8 @@ #if FORCE_RELEASE +#define DEBUG_CUBIC_SWAP_TOP 0 + #define DEBUG_ACTIVE_OP 0 #define DEBUG_ACTIVE_SPANS 0 #define DEBUG_ADD_INTERSECTING_TS 0 @@ -44,20 +46,24 @@ #define DEBUG_ANGLE 0 #define DEBUG_ASSEMBLE 0 #define DEBUG_CUBIC_BINARY_SEARCH 0 +#define DEBUG_CUBIC_SPLIT 0 +#define DEBUG_DUMP_SEGMENTS DEBUG_CUBIC_SWAP_TOP #define DEBUG_FLOW 0 #define DEBUG_LIMIT_WIND_SUM 0 #define DEBUG_MARK_DONE 0 #define DEBUG_PATH_CONSTRUCTION 0 #define DEBUG_PERP 0 -#define DEBUG_SHOW_TEST_NAME 0 +#define DEBUG_SHOW_TEST_NAME DEBUG_CUBIC_SWAP_TOP #define DEBUG_SORT 0 -#define DEBUG_SWAP_TOP 0 +#define DEBUG_SWAP_TOP DEBUG_CUBIC_SWAP_TOP #define DEBUG_T_SECT 0 #define DEBUG_T_SECT_DUMP 0 #define DEBUG_VALIDATE 0 #define DEBUG_WINDING 0 #define DEBUG_WINDING_AT_T 0 +#undef DEBUG_CUBIC_SWAP_TOP + #else #define DEBUG_ACTIVE_OP 1 @@ -67,16 +73,18 @@ #define DEBUG_ANGLE 1 #define DEBUG_ASSEMBLE 1 #define DEBUG_CUBIC_BINARY_SEARCH 0 +#define DEBUG_CUBIC_SPLIT 1 +#define DEBUG_DUMP_SEGMENTS 1 #define DEBUG_FLOW 1 #define DEBUG_LIMIT_WIND_SUM 5 #define DEBUG_MARK_DONE 1 #define DEBUG_PATH_CONSTRUCTION 1 -#define DEBUG_PERP 0 +#define DEBUG_PERP 1 #define DEBUG_SHOW_TEST_NAME 1 #define DEBUG_SORT 1 #define DEBUG_SWAP_TOP 1 -#define DEBUG_T_SECT 1 -#define DEBUG_T_SECT_DUMP 02 +#define DEBUG_T_SECT 0 +#define DEBUG_T_SECT_DUMP 0 #define DEBUG_VALIDATE 1 #define DEBUG_WINDING 1 #define DEBUG_WINDING_AT_T 1 @@ -161,6 +169,7 @@ public: SkPathOpsDebug::DeleteNameStr))) static void BumpTestName(char* ); #endif + static const char* OpStr(SkPathOp ); static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration); static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name); diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index f7580ae7d9..64a6e6ee64 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -298,7 +298,7 @@ bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { if (!builder.finish(&allocator)) { return false; } -#if !FORCE_RELEASE +#if DEBUG_DUMP_SEGMENTS contour.dumpSegments(op); #endif diff --git a/src/pathops/SkPathOpsPoint.h b/src/pathops/SkPathOpsPoint.h index 35ad80ea4c..c596b91cc0 100644 --- a/src/pathops/SkPathOpsPoint.h +++ b/src/pathops/SkPathOpsPoint.h @@ -115,6 +115,20 @@ struct SkDPoint { fY -= v.fY; } + // only used by testing + SkDPoint operator+(const SkDVector& v) { + SkDPoint result = *this; + result += v; + return result; + } + + // only used by testing + SkDPoint operator-(const SkDVector& v) { + SkDPoint result = *this; + result -= v; + return result; + } + // note: this can not be implemented with // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX); // because that will not take the magnitude of the values into account diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp index 054509b3e2..397a3ce98a 100644 --- a/src/pathops/SkPathOpsQuad.cpp +++ b/src/pathops/SkPathOpsQuad.cpp @@ -7,6 +7,7 @@ #include "SkIntersections.h" #include "SkLineParameters.h" #include "SkPathOpsCubic.h" +#include "SkPathOpsCurve.h" #include "SkPathOpsQuad.h" /* started with at_most_end_pts_in_common from SkDQuadIntersection.cpp */ @@ -105,25 +106,6 @@ double SkDQuad::nearestT(const SkDPoint& pt) const { return d0 < d2 ? 0 : 1; } -SkDPoint SkDQuad::top(double startT, double endT) const { - SkDQuad sub = subDivide(startT, endT); - SkDPoint topPt = sub[0]; - if (topPt.fY > sub[2].fY || (topPt.fY == sub[2].fY && topPt.fX > sub[2].fX)) { - topPt = sub[2]; - } - if (!between(sub[0].fY, sub[1].fY, sub[2].fY)) { - double extremeT; - if (FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, &extremeT)) { - extremeT = startT + (endT - startT) * extremeT; - SkDPoint test = ptAtT(extremeT); - if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) { - topPt = test; - } - } - } - return topPt; -} - int SkDQuad::AddValidTs(double s[], int realRoots, double* t) { int foundRoots = 0; for (int index = 0; index < realRoots; ++index) { @@ -341,6 +323,28 @@ SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, dou return b; } +SkDPoint SkDQuad::top(double startT, double endT, double* topT) const { + SkDQuad sub = subDivide(startT, endT); + SkDPoint topPt = sub[0]; + *topT = startT; + if (topPt.fY > sub[2].fY || (topPt.fY == sub[2].fY && topPt.fX > sub[2].fX)) { + *topT = endT; + topPt = sub[2]; + } + if (!between(sub[0].fY, sub[1].fY, sub[2].fY)) { + double extremeT; + if (FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, &extremeT)) { + extremeT = startT + (endT - startT) * extremeT; + SkDPoint test = ptAtT(extremeT); + if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) { + *topT = extremeT; + topPt = test; + } + } + } + return topPt; +} + /* classic one t subdivision */ static void interp_quad_coords(const double* src, double* dst, double t) { double ab = SkDInterp(src[0], src[2], t); @@ -360,6 +364,17 @@ SkDQuadPair SkDQuad::chopAt(double t) const return dst; } +bool SkDQuad::Clockwise(const SkOpCurve& edge, bool* swap) { + SkDQuad temp; + double sum = (edge[0].fX - edge[kPointLast].fX) * (edge[0].fY + edge[kPointLast].fY); + for (int idx = 0; idx < kPointLast; ++idx){ + sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY); + } + temp.set(edge.fPts); + *swap = sum > 0 && !temp.monotonicInY(); + return sum <= 0; +} + static int valid_unit_divide(double numer, double denom, double* ratio) { if (numer < 0) { diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h index 847c69cedd..b201860f98 100644 --- a/src/pathops/SkPathOpsQuad.h +++ b/src/pathops/SkPathOpsQuad.h @@ -10,6 +10,8 @@ #include "SkPathOpsPoint.h" +struct SkOpCurve; + struct SkDQuadPair { const SkDQuad& first() const { return (const SkDQuad&) pts[0]; } const SkDQuad& second() const { return (const SkDQuad&) pts[2]; } @@ -58,6 +60,7 @@ struct SkDQuad { static int AddValidTs(double s[], int realRoots, double* t); void align(int endIndex, SkDPoint* dstPt) const; SkDQuadPair chopAt(double t) const; + static bool Clockwise(const SkOpCurve& edge, bool* swap); SkDVector dxdyAtT(double t) const; static int FindExtrema(double a, double b, double c, double tValue[1]); bool hullIntersects(const SkDQuad& , bool* isLinear) const; @@ -86,7 +89,7 @@ struct SkDQuad { } SkDConic toConic() const; SkDCubic toCubic() const; - SkDPoint top(double startT, double endT) const; + SkDPoint top(double startT, double endT, double* topT) const; // utilities callable by the user from the debugger when the implementation code is linked in void dump() const; diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp index 8d525fa5f7..90f75b9802 100644 --- a/src/pathops/SkPathOpsSimplify.cpp +++ b/src/pathops/SkPathOpsSimplify.cpp @@ -199,7 +199,7 @@ bool Simplify(const SkPath& path, SkPath* result) { if (!builder.finish(&allocator)) { return false; } -#if !FORCE_RELEASE +#if DEBUG_DUMP_SEGMENTS contour.dumpSegments((SkPathOp) -1); #endif SkTDArray<SkOpContour* > contourList; |