| 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
|
|
|