Index: src/pathops/SkOpAngle.cpp |
=================================================================== |
--- src/pathops/SkOpAngle.cpp (revision 9425) |
+++ src/pathops/SkOpAngle.cpp (working copy) |
@@ -6,95 +6,169 @@ |
*/ |
#include "SkIntersections.h" |
#include "SkOpAngle.h" |
+#include "SkOpSegment.h" |
#include "SkPathOpsCurve.h" |
#include "SkTSort.h" |
-#if DEBUG_SORT || DEBUG_SORT_SINGLE |
-#include "SkOpSegment.h" |
+#if DEBUG_ANGLE |
+#include "SkString.h" |
+ |
+static const char funcName[] = "SkOpSegment::operator<"; |
+static const int bugChar = strlen(funcName) + 1; |
#endif |
-// FIXME: this is bogus for quads and cubics |
-// if the quads and cubics' line from end pt to ctrl pt are coincident, |
-// there's no obvious way to determine the curve ordering from the |
-// derivatives alone. In particular, if one quadratic's coincident tangent |
-// is longer than the other curve, the final control point can place the |
-// longer curve on either side of the shorter one. |
-// Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf |
-// may provide some help, but nothing has been figured out yet. |
+/* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest |
+ positive y. The largest angle has a positive x and a zero y. */ |
-/*( |
+#if DEBUG_ANGLE |
+ static bool CompareResult(SkString* bugOut, const char* append, bool compare) { |
+ bugOut->appendf(append); |
+ bugOut->writable_str()[bugChar] = "><"[compare]; |
+ SkDebugf("%s\n", bugOut->c_str()); |
+ return compare; |
+ } |
+ |
+ #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare) |
+#else |
+ #define COMPARE_RESULT(append, compare) compare |
+#endif |
+ |
+bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const{ |
+ double absX = fabs(x); |
+ double absY = fabs(y); |
+ double length = absX < absY ? absX / 2 + absY : absX + absY / 2; |
+ int exponent; |
+ (void) frexp(length, &exponent); |
+ double epsilon = ldexp(FLT_EPSILON, exponent); |
+ SkPath::Verb verb = fSegment->verb(); |
+ SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb); |
+ // FIXME: the quad and cubic factors are made up ; determine actual values |
+ double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon; |
+ double xSlop = slop; |
+ double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ? |
+ double x1 = x - xSlop; |
+ double y1 = y + ySlop; |
+ double x_ry1 = x1 * ry; |
+ double rx_y1 = rx * y1; |
+ *result = x_ry1 < rx_y1; |
+ double x2 = x + xSlop; |
+ double y2 = y - ySlop; |
+ double x_ry2 = x2 * ry; |
+ double rx_y2 = rx * y2; |
+ bool less2 = x_ry2 < rx_y2; |
+ return *result == less2; |
+} |
+ |
+/* |
for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
for points [0] to [1]. See if point [2] is on that line, or on one side |
or the other. If it both quads' end points are on the same side, choose |
the shorter tangent. If the tangents are equal, choose the better second |
tangent angle |
-maybe I could set up LineParameters lazily |
+FIXME: maybe I could set up LineParameters lazily |
*/ |
-static int simple_compare(double x, double y, double rx, double ry) { |
- if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? |
- return y < 0; |
+bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; rh: right-hand |
+ double y = dy(); |
+ double ry = rh.dy(); |
+#if DEBUG_ANGLE |
+ SkString bugOut; |
+ bugOut.printf("%s _ id=%d segId=%d tStart=%1.9g tEnd=%1.9g" |
+ " | id=%d segId=%d tStart=%1.9g tEnd=%1.9g ", funcName, |
+ fID, fSegment->debugID(), fSegment->t(fStart), fSegment->t(fEnd), |
+ rh.fID, rh.fSegment->debugID(), rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd)); |
+#endif |
+ double y_ry = y * ry; |
+ if (y_ry < 0) { // if y's are opposite signs, we can do a quick return |
+ return COMPARE_RESULT("1 y * ry < 0", y < 0); |
} |
- if (y == 0 && ry == 0 && x * rx < 0) { |
- return x < rx; |
- } |
- double x_ry = x * ry; |
- double rx_y = rx * y; |
- double cmp = x_ry - rx_y; |
- if (!approximately_zero(cmp)) { |
- return cmp < 0; |
- } |
- if (approximately_zero(x_ry) && approximately_zero(rx_y) |
- && !approximately_zero_squared(cmp)) { |
- return cmp < 0; |
- } |
- return -1; |
-} |
- |
-bool SkOpAngle::operator<(const SkOpAngle& rh) const { |
+ // at this point, both y's must be the same sign, or one (or both) is zero |
double x = dx(); |
- double y = dy(); |
double rx = rh.dx(); |
- double ry = rh.dy(); |
- int simple = simple_compare(x, y, rx, ry); |
- if (simple >= 0) { |
- return simple; |
+ if (x * rx < 0) { // if x's are opposite signs, use y to determine first or second half |
+ if (y < 0 && ry < 0) { // if y's are negative, lh x is smaller if positive |
+ return COMPARE_RESULT("2 x_rx < 0 && y < 0 ...", x > 0); |
+ } |
+ if (y >= 0 && ry >= 0) { // if y's are zero or positive, lh x is smaller if negative |
+ return COMPARE_RESULT("3 x_rx < 0 && y >= 0 ...", x < 0); |
+ } |
+ SkASSERT((y == 0) ^ (ry == 0)); // if one y is zero and one is negative, neg y is smaller |
+ return COMPARE_RESULT("4 x_rx < 0 && y == 0 ...", y < 0); |
} |
- // at this point, the initial tangent line is coincident |
- // see if edges curl away from each other |
- if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) |
- || !approximately_zero(rh.fSide))) { |
- // FIXME: running demo will trigger this assertion |
- // (don't know if commenting out will trigger further assertion or not) |
- // commenting it out allows demo to run in release, though |
- return fSide < rh.fSide; |
- } |
- // see if either curve can be lengthened and try the tangent compare again |
- if (/* cmp && */ (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical |
- && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting |
+ // at this point, both x's must be the same sign, or one (or both) is zero |
+ if (y_ry == 0) { // if either y is zero |
+ if (y + ry < 0) { // if the other y is less than zero, it must be smaller |
+ return COMPARE_RESULT("5 y_ry == 0 && y + ry < 0", y < 0); |
+ } |
+ if (y + ry > 0) { // if a y is greater than zero and an x is positive, non zero is smaller |
+ return COMPARE_RESULT("6 y_ry == 0 && y + ry > 0", (x + rx > 0) ^ (y == 0)); |
+ } |
+ // at this point, both y's are zero, so lines are coincident or one is degenerate |
+ SkASSERT(x * rx != 0); // and a degenerate line should haven't gotten this far |
+ } |
+ // see if either curve can be lengthened before trying the tangent |
+ if (fSegment->other(fEnd) != rh.fSegment // tangents not absolutely identical |
+ && rh.fSegment->other(rh.fEnd) != fSegment) { // and not intersecting |
SkOpAngle longer = *this; |
SkOpAngle rhLonger = rh; |
- if (longer.lengthen() | rhLonger.lengthen()) { |
- return longer < rhLonger; |
+ if ((longer.lengthen(rh) | rhLonger.lengthen(*this)) // lengthen both |
+ && (fUnorderable || !longer.fUnorderable) |
+ && (rh.fUnorderable || !rhLonger.fUnorderable)) { |
+#if DEBUG_ANGLE |
+ bugOut.prepend(" "); |
+#endif |
+ return COMPARE_RESULT("10 longer.lengthen(rh) ...", longer < rhLonger); |
} |
} |
- if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y)) |
- || (rh.fVerb == SkPath::kLine_Verb |
- && approximately_zero(rx) && approximately_zero(ry))) { |
+ if (y_ry != 0) { // if they aren't coincident, look for a stable cross product |
+ // at this point, y's are the same sign, neither is zero |
+ // and x's are the same sign, or one (or both) is zero |
+ double x_ry = x * ry; |
+ double rx_y = rx * y; |
+ if (!fComputed && !rh.fComputed) { |
+ if (!AlmostEqualUlps(x_ry, rx_y)) { |
+ return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx_y); |
+ } |
+ } else { |
+ // if the vector was a result of subdividing a curve, see if it is stable |
+ bool sloppy1 = x_ry < rx_y; |
+ bool sloppy2 = !sloppy1; |
+ if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1)) |
+ && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2)) |
+ && sloppy1 != sloppy2) { |
+ return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1); |
+ } |
+ } |
+ } |
+ if (fSide * rh.fSide == 0) { |
+ SkASSERT(fSide + rh.fSide != 0); |
+ return COMPARE_RESULT("9 fSide * rh.fSide == 0 ...", fSide < rh.fSide); |
+ } |
+ // at this point, the initial tangent line is nearly coincident |
+ // see if edges curl away from each other |
+ if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) { |
+ return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide); |
+ } |
+ if (fUnsortable || rh.fUnsortable) { |
+ // even with no solution, return a stable sort |
+ return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh); |
+ } |
+ SkPath::Verb verb = fSegment->verb(); |
+ SkPath::Verb rVerb = rh.fSegment->verb(); |
+ if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_zero(x)) |
+ || (rVerb == SkPath::kLine_Verb |
+ && approximately_zero(ry) && approximately_zero(rx))) { |
// See general unsortable comment below. This case can happen when |
// one line has a non-zero change in t but no change in x and y. |
fUnsortable = true; |
- rh.fUnsortable = true; |
- return this < &rh; // even with no solution, return a stable sort |
+ return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh); |
} |
- if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny |
- || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { |
+ if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) { |
fUnsortable = true; |
- rh.fUnsortable = true; |
- return this < &rh; // even with no solution, return a stable sort |
+ return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &rh); |
} |
- SkASSERT(fVerb >= SkPath::kQuad_Verb); |
- SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); |
+ SkASSERT(verb >= SkPath::kQuad_Verb); |
+ SkASSERT(rVerb >= SkPath::kQuad_Verb); |
// FIXME: until I can think of something better, project a ray from the |
// end of the shorter tangent to midway between the end points |
// through both curves and use the resulting angle to sort |
@@ -110,22 +184,20 @@ |
do { |
useThis = (len < rlen) ^ flip; |
const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; |
- SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; |
+ SkPath::Verb partVerb = useThis ? verb : rVerb; |
ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ? |
part[2] : part[1]; |
- ray[1].fX = (part[0].fX + part[SkPathOpsVerbToPoints(partVerb)].fX) / 2; |
- ray[1].fY = (part[0].fY + part[SkPathOpsVerbToPoints(partVerb)].fY) / 2; |
+ ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]); |
SkASSERT(ray[0] != ray[1]); |
- roots = (i.*CurveRay[SkPathOpsVerbToPoints(fVerb)])(fPts, ray); |
- rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rh.fVerb)])(rh.fPts, ray); |
+ roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray); |
+ rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts(), ray); |
} while ((roots == 0 || rroots == 0) && (flip ^= true)); |
if (roots == 0 || rroots == 0) { |
// FIXME: we don't have a solution in this case. The interim solution |
// is to mark the edges as unsortable, exclude them from this and |
// future computations, and allow the returned path to be fragmented |
fUnsortable = true; |
- rh.fUnsortable = true; |
- return this < &rh; // even with no solution, return a stable sort |
+ return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh); |
} |
SkASSERT(fSide != 0 && rh.fSide != 0); |
SkASSERT(fSide * rh.fSide > 0); // both are the same sign |
@@ -164,105 +236,96 @@ |
break; |
} |
} |
- #if 0 |
- SkDVector lRay = lLoc - fCurvePart[0]; |
- SkDVector rRay = rLoc - fCurvePart[0]; |
- int rayDir = simple_compare(lRay.fX, lRay.fY, rRay.fX, rRay.fY); |
- SkASSERT(rayDir >= 0); |
- if (rayDir < 0) { |
- fUnsortable = true; |
- rh.fUnsortable = true; |
- return this < &rh; // even with no solution, return a stable sort |
- } |
-#endif |
- if (flip) { |
+ if (flip) { |
leftLessThanRight = !leftLessThanRight; |
- // rayDir = !rayDir; |
} |
-#if 0 && (DEBUG_SORT || DEBUG_SORT_SINGLE) |
- SkDebugf("%d %c %d (fSide %c 0) loc={{%1.9g,%1.9g}, {%1.9g,%1.9g}} flip=%d rayDir=%d\n", |
- fSegment->debugID(), "><"[leftLessThanRight], rh.fSegment->debugID(), |
- "<>"[fSide > 0], lLoc.fX, lLoc.fY, rLoc.fX, rLoc.fY, flip, rayDir); |
-#endif |
-// SkASSERT(leftLessThanRight == (bool) rayDir); |
- return leftLessThanRight; |
+ return COMPARE_RESULT("14 leftLessThanRight", leftLessThanRight); |
} |
-bool SkOpAngle::lengthen() { |
- int newEnd = fEnd; |
- if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
- fEnd = newEnd; |
- setSpans(); |
- return true; |
- } |
- return false; |
+bool SkOpAngle::isHorizontal() const { |
+ return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb; |
} |
-bool SkOpAngle::reverseLengthen() { |
- if (fReversed) { |
+// lengthen cannot cross opposite angle |
+bool SkOpAngle::lengthen(const SkOpAngle& opp) { |
+ if (fSegment->other(fEnd) == opp.fSegment) { |
return false; |
} |
- int newEnd = fStart; |
- if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
+ // FIXME: make this a while loop instead and make it as large as possible? |
+ int newEnd = fEnd; |
+ if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) { |
fEnd = newEnd; |
- fReversed = true; |
setSpans(); |
return true; |
} |
return false; |
} |
-void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* segment, |
- int start, int end, const SkTDArray<SkOpSpan>& spans) { |
+void SkOpAngle::set(const SkOpSegment* segment, int start, int end) { |
fSegment = segment; |
fStart = start; |
fEnd = end; |
- fPts = orig; |
- fVerb = verb; |
- fSpans = &spans; |
- fReversed = false; |
- fUnsortable = false; |
setSpans(); |
} |
- |
void SkOpAngle::setSpans() { |
- double startT = (*fSpans)[fStart].fT; |
- double endT = (*fSpans)[fEnd].fT; |
- switch (fVerb) { |
+ fUnorderable = false; |
+ if (fSegment->verb() == SkPath::kLine_Verb) { |
+ fUnsortable = false; |
+ } else { |
+ // if start-1 exists and is tiny, then start pt may have moved |
+ int smaller = SkMin32(fStart, fEnd); |
+ int tinyCheck = smaller; |
+ while (tinyCheck > 0 && fSegment->isTiny(tinyCheck - 1)) { |
+ --tinyCheck; |
+ } |
+ if ((fUnsortable = smaller > 0 && tinyCheck == 0)) { |
+ return; |
+ } |
+ int larger = SkMax32(fStart, fEnd); |
+ tinyCheck = larger; |
+ int max = fSegment->count() - 1; |
+ while (tinyCheck < max && fSegment->isTiny(tinyCheck + 1)) { |
+ ++tinyCheck; |
+ } |
+ if ((fUnsortable = larger < max && tinyCheck == max)) { |
+ return; |
+ } |
+ } |
+ fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart); |
+ // FIXME: slight errors in subdivision cause sort trouble later on. As an experiment, try |
+ // rounding the curve part to float precision here |
+ // fCurvePart.round(fSegment->verb()); |
+ switch (fSegment->verb()) { |
case SkPath::kLine_Verb: { |
- SkDLine l = SkDLine::SubDivide(fPts, startT, endT); |
// OPTIMIZATION: for pure line compares, we never need fTangent1.c |
- fTangent1.lineEndPoints(l); |
+ fTangent1.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart)); |
fSide = 0; |
} break; |
case SkPath::kQuad_Verb: { |
SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); |
- quad = SkDQuad::SubDivide(fPts, startT, endT); |
- fTangent1.quadEndPoints(quad, 0, 1); |
- if (dx() == 0 && dy() == 0) { |
- fTangent1.quadEndPoints(quad); |
+ fTangent1.quadEndPoints(quad); |
+ fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only |
+ if (fComputed && dx() > 0 && approximately_zero(dy())) { |
+ SkDCubic origCurve; // can't use segment's curve in place since it may be flipped |
+ int last = fSegment->count() - 1; |
+ fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve); |
+ SkLineParameters origTan; |
+ origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve)); |
+ if ((fUnorderable = origTan.dx() <= 0 |
+ || (dy() != origTan.dy() && dy() * origTan.dy() <= 0))) { // signs match? |
+ return; |
+ } |
} |
- fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- compare sign only |
} break; |
case SkPath::kCubic_Verb: { |
- // int nextC = 2; |
- fCurvePart = SkDCubic::SubDivide(fPts, startT, endT); |
- fTangent1.cubicEndPoints(fCurvePart, 0, 1); |
- if (dx() == 0 && dy() == 0) { |
- fTangent1.cubicEndPoints(fCurvePart, 0, 2); |
- // nextC = 3; |
- if (dx() == 0 && dy() == 0) { |
- fTangent1.cubicEndPoints(fCurvePart, 0, 3); |
- } |
- } |
- // fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign only |
- // if (nextC == 2 && approximately_zero(fSide)) { |
- // fSide = -fTangent1.pointDistance(fCurvePart[3]); |
- // } |
+ fTangent1.cubicEndPoints(fCurvePart); |
double testTs[4]; |
// OPTIMIZATION: keep inflections precomputed with cubic segment? |
- int testCount = SkDCubic::FindInflections(fPts, testTs); |
+ const SkPoint* pts = fSegment->pts(); |
+ int testCount = SkDCubic::FindInflections(pts, testTs); |
+ double startT = fSegment->t(fStart); |
+ double endT = fSegment->t(fEnd); |
double limitT = endT; |
int index; |
for (index = 0; index < testCount; ++index) { |
@@ -287,35 +350,62 @@ |
testT = (testT + testTs[testIndex + 1]) / 2; |
} |
// OPTIMIZE: could avoid call for t == startT, endT |
- SkDPoint pt = dcubic_xy_at_t(fPts, testT); |
+ SkDPoint pt = dcubic_xy_at_t(pts, testT); |
double testSide = fTangent1.pointDistance(pt); |
if (fabs(bestSide) < fabs(testSide)) { |
bestSide = testSide; |
} |
} |
fSide = -bestSide; // compare sign only |
+ if (fComputed && dx() > 0 && approximately_zero(dy())) { |
+ SkDCubic origCurve; // can't use segment's curve in place since it may be flipped |
+ int last = fSegment->count() - 1; |
+ fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last : 0, &origCurve); |
+ SkLineParameters origTan; |
+ origTan.cubicEndPoints(origCurve); |
+ if ((fUnorderable = origTan.dx() <= 0)) { |
+ fUnsortable = fSegment->isTiny(this); |
+ return; |
+ } |
+ // if one is < 0 and the other is >= 0 |
+ if ((fUnorderable = (dy() < 0) ^ (origTan.dy() < 0))) { |
+ fUnsortable = fSegment->isTiny(this); |
+ return; |
+ } |
+ SkDCubicPair split = origCurve.chopAt(startT); |
+ SkLineParameters splitTan; |
+ splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first()); |
+ if ((fUnorderable = splitTan.dx() <= 0)) { |
+ fUnsortable = fSegment->isTiny(this); |
+ return; |
+ } |
+ // if one is < 0 and the other is >= 0 |
+ if ((fUnorderable = (dy() < 0) ^ (splitTan.dy() < 0))) { |
+ fUnsortable = fSegment->isTiny(this); |
+ return; |
+ } |
+ } |
} break; |
default: |
SkASSERT(0); |
} |
- fUnsortable = dx() == 0 && dy() == 0; |
- if (fUnsortable) { |
+ if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) { |
return; |
} |
SkASSERT(fStart != fEnd); |
int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 type macro? |
for (int index = fStart; index != fEnd; index += step) { |
#if 1 |
- const SkOpSpan& thisSpan = (*fSpans)[index]; |
- const SkOpSpan& nextSpan = (*fSpans)[index + step]; |
+ const SkOpSpan& thisSpan = fSegment->span(index); |
+ const SkOpSpan& nextSpan = fSegment->span(index + step); |
if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { |
continue; |
} |
fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd; |
#if DEBUG_UNSORTABLE |
if (fUnsortable) { |
- SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, thisSpan.fT); |
- SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, nextSpan.fT); |
+ SkPoint iPt = fSegment->xyAtT(index); |
+ SkPoint ePt = fSegment->xyAtT(index + step); |
SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, |
index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
} |
@@ -330,8 +420,8 @@ |
} |
#if 1 |
#if DEBUG_UNSORTABLE |
- SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, startT); |
- SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, endT); |
+ SkPoint iPt = fSegment->xyAtT(fStart); |
+ SkPoint ePt = fSegment->xyAtT(fEnd); |
SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__, |
fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
#endif |