aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pathops
diff options
context:
space:
mode:
authorGravatar caryclark <caryclark@google.com>2015-04-23 09:13:37 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-04-23 09:13:37 -0700
commit03b03cad01628146bbb8d4f33c073bd0c77ee558 (patch)
tree3daa35fc7a85abd54f6d48e23d3f8f665b677dc5 /src/pathops
parent4b17fa353e777de309ca8b0706f1d3e326b59822 (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.h2
-rw-r--r--src/pathops/SkOpContour.cpp10
-rw-r--r--src/pathops/SkOpContour.h2
-rw-r--r--src/pathops/SkOpEdgeBuilder.cpp7
-rw-r--r--src/pathops/SkOpSegment.cpp109
-rw-r--r--src/pathops/SkOpSegment.h18
-rw-r--r--src/pathops/SkPathOpsCommon.cpp3
-rw-r--r--src/pathops/SkPathOpsConic.cpp41
-rw-r--r--src/pathops/SkPathOpsConic.h2
-rw-r--r--src/pathops/SkPathOpsCubic.cpp90
-rw-r--r--src/pathops/SkPathOpsCubic.h14
-rw-r--r--src/pathops/SkPathOpsCurve.h25
-rw-r--r--src/pathops/SkPathOpsDebug.cpp6
-rw-r--r--src/pathops/SkPathOpsDebug.h19
-rw-r--r--src/pathops/SkPathOpsOp.cpp2
-rw-r--r--src/pathops/SkPathOpsPoint.h14
-rw-r--r--src/pathops/SkPathOpsQuad.cpp53
-rw-r--r--src/pathops/SkPathOpsQuad.h5
-rw-r--r--src/pathops/SkPathOpsSimplify.cpp2
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;