diff options
author | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-23 15:27:41 +0000 |
---|---|---|
committer | caryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-23 15:27:41 +0000 |
commit | 4fdbb229649caf74e5c1b55a1823926df903af34 (patch) | |
tree | 5f822b5335b213ce7f9857cac288e79e4f3cc8f9 /src/pathops | |
parent | 672222e400ec10024106a394512ce864d5b839ea (diff) |
turn off debugging printfs
fix pathops issues 1417, 1418
be more rigorous about pulling intersections of lines to end points
rewrite cubic/line and quad/line intersections to share style
BUG=
Review URL: https://codereview.chromium.org/19543005
git-svn-id: http://skia.googlecode.com/svn/trunk@10270 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/pathops')
-rw-r--r-- | src/pathops/SkDCubicIntersection.cpp | 218 | ||||
-rw-r--r-- | src/pathops/SkDCubicLineIntersection.cpp | 372 | ||||
-rw-r--r-- | src/pathops/SkDLineIntersection.cpp | 17 | ||||
-rw-r--r-- | src/pathops/SkDQuadIntersection.cpp | 113 | ||||
-rw-r--r-- | src/pathops/SkDQuadLineIntersection.cpp | 126 | ||||
-rw-r--r-- | src/pathops/SkIntersections.cpp | 102 | ||||
-rw-r--r-- | src/pathops/SkIntersections.h | 2 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.cpp | 46 | ||||
-rw-r--r-- | src/pathops/SkOpSegment.h | 11 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCommon.cpp | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCubic.cpp | 10 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCubic.h | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsCurve.h | 8 | ||||
-rw-r--r-- | src/pathops/SkPathOpsDebug.h | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsLine.cpp | 11 | ||||
-rw-r--r-- | src/pathops/SkPathOpsLine.h | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsQuad.cpp | 12 | ||||
-rw-r--r-- | src/pathops/SkPathOpsQuad.h | 2 | ||||
-rw-r--r-- | src/pathops/SkPathOpsRect.cpp | 4 | ||||
-rw-r--r-- | src/pathops/SkPathOpsTypes.cpp | 37 | ||||
-rw-r--r-- | src/pathops/SkPathOpsTypes.h | 21 |
21 files changed, 598 insertions, 522 deletions
diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp index 6e049708ac..19e7340cdc 100644 --- a/src/pathops/SkDCubicIntersection.cpp +++ b/src/pathops/SkDCubicIntersection.cpp @@ -114,26 +114,16 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC #endif SkIntersections locals; intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals); - double coStart[2] = { -1 }; - SkDPoint coPoint; int tCount = locals.used(); for (int tIdx = 0; tIdx < tCount; ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx]; // if the computed t is not sufficiently precise, iterate - SkDPoint p1 = cubic1.xyAtT(to1); - SkDPoint p2 = cubic2.xyAtT(to2); + SkDPoint p1 = cubic1.ptAtT(to1); + SkDPoint p2 = cubic2.ptAtT(to2); if (p1.approximatelyEqual(p2)) { - if (locals.isCoincident(tIdx)) { - if (coStart[0] < 0) { - coStart[0] = to1; - coStart[1] = to2; - coPoint = p1; - } else { - i.insertCoincidentPair(coStart[0], to1, coStart[1], to2, coPoint, p1); - coStart[0] = -1; - } - } else if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) { + SkASSERT(!locals.isCoincident(tIdx)); + if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(to2, to1, p1); } else { @@ -250,7 +240,6 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC // for that. } } - SkASSERT(coStart[0] == -1); t2Start = t2; } t1Start = t1; @@ -263,11 +252,34 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC // intersect the end of the cubic with the other. Try lines from the end to control and opposite // end to determine range of t on opposite cubic. static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2, - const SkDRect& bounds2, SkIntersections& i) { + const SkDRect& bounds2, bool selfIntersect, SkIntersections& i) { SkDLine line; int t1Index = start ? 0 : 3; + bool swap = i.swapped(); + double testT = (double) !start; + // quad/quad at this point checks to see if exact matches have already been found + // cubic/cubic can't reject so easily since cubics can intersect same point more than once + if (!selfIntersect) { + SkDLine tmpLine; + tmpLine[0] = tmpLine[1] = cubic2[t1Index]; + tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY; + tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX; + SkIntersections impTs; + impTs.intersectRay(cubic1, tmpLine); + for (int index = 0; index < impTs.used(); ++index) { + SkDPoint realPt = impTs.pt(index); + if (!tmpLine[0].approximatelyEqualHalf(realPt)) { + continue; + } + if (swap) { + i.insert(testT, impTs[0][index], tmpLine[0]); + } else { + i.insert(impTs[0][index], testT, tmpLine[0]); + } + return; + } + } // don't bother if the two cubics are connnected -#if 1 static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this static const int kMaxLineCubicIntersections = 3; SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals; @@ -297,9 +309,9 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub } if (local.pt(idx2).approximatelyEqual(line[0])) { if (i.swapped()) { // FIXME: insert should respect swap - i.insert(foundT, start ? 0 : 1, line[0]); + i.insert(foundT, testT, line[0]); } else { - i.insert(start ? 0 : 1, foundT, line[0]); + i.insert(testT, foundT, line[0]); } } else { tVals.push_back(foundT); @@ -329,57 +341,6 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub } tIdx = tLast + 1; } while (tIdx < tVals.count()); -#else - const SkDPoint& endPt = cubic1[t1Index]; - if (!bounds2.contains(endPt)) { - return; - } - // this variant looks for intersections within an 'x' of the endpoint - double delta = SkTMax(bounds2.width(), bounds2.height()); - for (int index = 0; index < 2; ++index) { - if (index == 0) { - line[0].fY = line[1].fY = endPt.fY; - line[0].fX = endPt.fX - delta; - line[1].fX = endPt.fX + delta; - } else { - line[0].fX = line[1].fX = cubic1[t1Index].fX; - line[0].fY = endPt.fY - delta; - line[1].fY = endPt.fY + delta; - } - SkIntersections local; - local.intersectRay(cubic2, line); // OPTIMIZE: special for horizontal/vertical lines - int used = local.used(); - for (int index = 0; index < used; ++index) { - double foundT = local[0][index]; - if (approximately_less_than_zero(foundT) || approximately_greater_than_one(foundT)) { - continue; - } - if (!local.pt(index).approximatelyEqual(endPt)) { - continue; - } - if (i.swapped()) { // FIXME: insert should respect swap - i.insert(foundT, start ? 0 : 1, endPt); - } else { - i.insert(start ? 0 : 1, foundT, endPt); - } - return; - } - } -// the above doesn't catch when the end of the cubic missed the other cubic because the quad -// approximation moved too far away, so something like the below is still needed. The enabled -// code above tries to avoid this heavy lifting unless the convex hull intersected the cubic. - double tMin1 = start ? 0 : 1 - LINE_FRACTION; - double tMax1 = start ? LINE_FRACTION : 1; - double tMin2 = SkTMax(foundT - LINE_FRACTION, 0.0); - double tMax2 = SkTMin(foundT + LINE_FRACTION, 1.0); - int lastUsed = i.used(); - intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); - if (lastUsed == i.used()) { - tMin2 = SkTMax(foundT - (1.0 / SkDCubic::gPrecisionUnit), 0.0); - tMax2 = SkTMin(foundT + (1.0 / SkDCubic::gPrecisionUnit), 1.0); - intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); - } -#endif return; } @@ -389,7 +350,7 @@ static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) { return false; } - pt = cubic.xyAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2); + pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2); return true; } @@ -398,29 +359,120 @@ static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) { return false; } - pt = cubic.xyAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2); + pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2); return true; } +static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) { +// the idea here is to see at minimum do a quick reject by rotating all points +// to either side of the line formed by connecting the endpoints +// if the opposite curves points are on the line or on the other side, the +// curves at most intersect at the endpoints + for (int oddMan = 0; oddMan < 4; ++oddMan) { + const SkDPoint* endPt[3]; + for (int opp = 1; opp < 4; ++opp) { + int end = oddMan ^ opp; // choose a value not equal to oddMan + endPt[opp - 1] = &c1[end]; + } + for (int triTest = 0; triTest < 3; ++triTest) { + double origX = endPt[triTest]->fX; + double origY = endPt[triTest]->fY; + int oppTest = triTest + 1; + if (3 == oppTest) { + oppTest = 0; + } + double adj = endPt[oppTest]->fX - origX; + double opp = endPt[oppTest]->fY - origY; + double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp; + if (approximately_zero(sign)) { + goto tryNextHalfPlane; + } + for (int n = 0; n < 4; ++n) { + double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp; + if (test * sign > 0 && !precisely_zero(test)) { + goto tryNextHalfPlane; + } + } + } + return true; +tryNextHalfPlane: + ; + } + return false; +} + int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) { - ::intersect(c1, 0, 1, c2, 0, 1, 1, *this); - // FIXME: pass in cached bounds from caller + bool selfIntersect = &c1 == &c2; + if (selfIntersect) { + if (c1[0].approximatelyEqualHalf(c1[3])) { + insert(0, 1, c1[0]); + } + } else { + for (int i1 = 0; i1 < 4; i1 += 3) { + for (int i2 = 0; i2 < 4; i2 += 3) { + if (c1[i1].approximatelyEqualHalf(c2[i2])) { + insert(i1 >> 1, i2 >> 1, c1[i1]); + } + } + } + } + SkASSERT(fUsed < 4); + if (!selfIntersect) { + if (only_end_pts_in_common(c1, c2)) { + return fUsed; + } + if (only_end_pts_in_common(c2, c1)) { + return fUsed; + } + } + // quad/quad does linear test here -- cubic does not + // cubics which are really lines should have been detected in reduce step earlier SkDRect c1Bounds, c2Bounds; + // FIXME: pass in cached bounds from caller c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? c2Bounds.setBounds(c2); - intersectEnd(c1, false, c2, c2Bounds, *this); - intersectEnd(c1, true, c2, c2Bounds, *this); - bool selfIntersect = &c1 == &c2; - if (!selfIntersect) { + intersectEnd(c1, false, c2, c2Bounds, selfIntersect, *this); + intersectEnd(c1, true, c2, c2Bounds, selfIntersect, *this); + if (selfIntersect) { + if (fUsed) { + return fUsed; + } + } else { swap(); - intersectEnd(c2, false, c1, c1Bounds, *this); - intersectEnd(c2, true, c1, c1Bounds, *this); + intersectEnd(c2, false, c1, c1Bounds, false, *this); + intersectEnd(c2, true, c1, c1Bounds, false, *this); swap(); } + // if two ends intersect, check middle for coincidence + if (fUsed >= 2) { + SkASSERT(!selfIntersect); + int last = fUsed - 1; + double tRange1 = fT[0][last] - fT[0][0]; + double tRange2 = fT[1][last] - fT[1][0]; + for (int index = 1; index < 5; ++index) { + double testT1 = fT[0][0] + tRange1 * index / 5; + double testT2 = fT[1][0] + tRange2 * index / 5; + SkDPoint testPt1 = c1.ptAtT(testT1); + SkDPoint testPt2 = c2.ptAtT(testT2); + if (!testPt1.approximatelyEqual(testPt2)) { + goto skipCoincidence; + } + } + if (fUsed > 2) { + fPt[1] = fPt[last]; + fT[0][1] = fT[0][last]; + fT[1][1] = fT[1][last]; + fUsed = 2; + } + fIsCoincident[0] = fIsCoincident[1] = 0x03; + return fUsed; + } +skipCoincidence: + ::intersect(c1, 0, 1, c2, 0, 1, 1, *this); // If an end point and a second point very close to the end is returned, the second // point may have been detected because the approximate quads // intersected at the end and close to it. Verify that the second point is valid. - if (fUsed <= 1 || coincidentUsed()) { + if (fUsed <= 1) { return fUsed; } SkDPoint pt[2]; @@ -440,8 +492,8 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) { for (int index = 0; index < last; ++index) { double mid1 = (fT[0][index] + fT[0][index + 1]) / 2; double mid2 = (fT[1][index] + fT[1][index + 1]) / 2; - pt[0] = c1.xyAtT(mid1); - pt[1] = c2.xyAtT(mid2); + pt[0] = c1.ptAtT(mid1); + pt[1] = c2.ptAtT(mid2); if (pt[0].approximatelyEqual(pt[1])) { match |= 1 << index; } diff --git a/src/pathops/SkDCubicLineIntersection.cpp b/src/pathops/SkDCubicLineIntersection.cpp index dc80479f60..a891abec66 100644 --- a/src/pathops/SkDCubicLineIntersection.cpp +++ b/src/pathops/SkDCubicLineIntersection.cpp @@ -76,250 +76,252 @@ For horizontal lines: class LineCubicIntersections { public: + enum PinTPoint { + kPointUninitialized, + kPointInitialized + }; + + LineCubicIntersections(const SkDCubic& c, const SkDLine& l, SkIntersections* i) + : fCubic(c) + , fLine(l) + , fIntersections(i) + , fAllowNear(true) { + } -LineCubicIntersections(const SkDCubic& c, const SkDLine& l, SkIntersections& i) - : cubic(c) - , line(l) - , intersections(i) - , fAllowNear(true) { -} - -void allowNear(bool allow) { - fAllowNear = allow; -} - -// see parallel routine in line quadratic intersections -int intersectRay(double roots[3]) { - double adj = line[1].fX - line[0].fX; - double opp = line[1].fY - line[0].fY; - SkDCubic r; - for (int n = 0; n < 4; ++n) { - r[n].fX = (cubic[n].fY - line[0].fY) * adj - (cubic[n].fX - line[0].fX) * opp; + void allowNear(bool allow) { + fAllowNear = allow; } - double A, B, C, D; - SkDCubic::Coefficients(&r[0].fX, &A, &B, &C, &D); - return SkDCubic::RootsValidT(A, B, C, D, roots); -} -int intersect() { - addExactEndPoints(); - double rootVals[3]; - int roots = intersectRay(rootVals); - for (int index = 0; index < roots; ++index) { - double cubicT = rootVals[index]; - double lineT = findLineT(cubicT); - if (pinTs(&cubicT, &lineT)) { - SkDPoint pt = line.xyAtT(lineT); -#if ONE_OFF_DEBUG - SkDPoint cPt = cubic.xyAtT(cubicT); - SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY, - cPt.fX, cPt.fY); -#endif - intersections.insert(cubicT, lineT, pt); + // see parallel routine in line quadratic intersections + int intersectRay(double roots[3]) { + double adj = fLine[1].fX - fLine[0].fX; + double opp = fLine[1].fY - fLine[0].fY; + SkDCubic r; + for (int n = 0; n < 4; ++n) { + r[n].fX = (fCubic[n].fY - fLine[0].fY) * adj - (fCubic[n].fX - fLine[0].fX) * opp; } + double A, B, C, D; + SkDCubic::Coefficients(&r[0].fX, &A, &B, &C, &D); + return SkDCubic::RootsValidT(A, B, C, D, roots); } - if (fAllowNear) { - addNearEndPoints(); - } - return intersections.used(); -} -int horizontalIntersect(double axisIntercept, double roots[3]) { - double A, B, C, D; - SkDCubic::Coefficients(&cubic[0].fY, &A, &B, &C, &D); - D -= axisIntercept; - return SkDCubic::RootsValidT(A, B, C, D, roots); -} - -int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) { - addExactHorizontalEndPoints(left, right, axisIntercept); - double rootVals[3]; - int roots = horizontalIntersect(axisIntercept, rootVals); - for (int index = 0; index < roots; ++index) { - double cubicT = rootVals[index]; - SkDPoint pt = cubic.xyAtT(cubicT); - double lineT = (pt.fX - left) / (right - left); - if (pinTs(&cubicT, &lineT)) { - intersections.insert(cubicT, lineT, pt); + int intersect() { + addExactEndPoints(); + double rootVals[3]; + int roots = intersectRay(rootVals); + for (int index = 0; index < roots; ++index) { + double cubicT = rootVals[index]; + double lineT = findLineT(cubicT); + SkDPoint pt; + if (pinTs(&cubicT, &lineT, &pt, kPointUninitialized)) { + #if ONE_OFF_DEBUG + SkDPoint cPt = fCubic.ptAtT(cubicT); + SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY, + cPt.fX, cPt.fY); + #endif + fIntersections->insert(cubicT, lineT, pt); + } } + if (fAllowNear) { + addNearEndPoints(); + } + return fIntersections->used(); } - if (fAllowNear) { - addNearHorizontalEndPoints(left, right, axisIntercept); - } - if (flipped) { - intersections.flip(); - } - return intersections.used(); -} -int verticalIntersect(double axisIntercept, double roots[3]) { - double A, B, C, D; - SkDCubic::Coefficients(&cubic[0].fX, &A, &B, &C, &D); - D -= axisIntercept; - return SkDCubic::RootsValidT(A, B, C, D, roots); -} + int horizontalIntersect(double axisIntercept, double roots[3]) { + double A, B, C, D; + SkDCubic::Coefficients(&fCubic[0].fY, &A, &B, &C, &D); + D -= axisIntercept; + return SkDCubic::RootsValidT(A, B, C, D, roots); + } -int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) { - addExactVerticalEndPoints(top, bottom, axisIntercept); - double rootVals[3]; - int roots = verticalIntersect(axisIntercept, rootVals); - for (int index = 0; index < roots; ++index) { - double cubicT = rootVals[index]; - SkDPoint pt = cubic.xyAtT(cubicT); - double lineT = (pt.fY - top) / (bottom - top); - if (pinTs(&cubicT, &lineT)) { - intersections.insert(cubicT, lineT, pt); + int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) { + addExactHorizontalEndPoints(left, right, axisIntercept); + double rootVals[3]; + int roots = horizontalIntersect(axisIntercept, rootVals); + for (int index = 0; index < roots; ++index) { + double cubicT = rootVals[index]; + SkDPoint pt = fCubic.ptAtT(cubicT); + double lineT = (pt.fX - left) / (right - left); + if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) { + fIntersections->insert(cubicT, lineT, pt); + } + } + if (fAllowNear) { + addNearHorizontalEndPoints(left, right, axisIntercept); } + if (flipped) { + fIntersections->flip(); + } + return fIntersections->used(); } - if (fAllowNear) { - addNearVerticalEndPoints(top, bottom, axisIntercept); + + int verticalIntersect(double axisIntercept, double roots[3]) { + double A, B, C, D; + SkDCubic::Coefficients(&fCubic[0].fX, &A, &B, &C, &D); + D -= axisIntercept; + return SkDCubic::RootsValidT(A, B, C, D, roots); } - if (flipped) { - intersections.flip(); + + int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) { + addExactVerticalEndPoints(top, bottom, axisIntercept); + double rootVals[3]; + int roots = verticalIntersect(axisIntercept, rootVals); + for (int index = 0; index < roots; ++index) { + double cubicT = rootVals[index]; + SkDPoint pt = fCubic.ptAtT(cubicT); + double lineT = (pt.fY - top) / (bottom - top); + if (pinTs(&cubicT, &lineT, &pt, kPointInitialized)) { + fIntersections->insert(cubicT, lineT, pt); + } + } + if (fAllowNear) { + addNearVerticalEndPoints(top, bottom, axisIntercept); + } + if (flipped) { + fIntersections->flip(); + } + return fIntersections->used(); } - return intersections.used(); -} -protected: + protected: -void addExactEndPoints() { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double lineT = line.exactPoint(cubic[cIndex]); - if (lineT < 0) { - continue; + void addExactEndPoints() { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double lineT = fLine.exactPoint(fCubic[cIndex]); + if (lineT < 0) { + continue; + } + double cubicT = (double) (cIndex >> 1); + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double cubicT = (double) (cIndex >> 1); - intersections.insert(cubicT, lineT, cubic[cIndex]); } -} -void addNearEndPoints() { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double cubicT = (double) (cIndex >> 1); - if (intersections.hasT(cubicT)) { - continue; + void addNearEndPoints() { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double cubicT = (double) (cIndex >> 1); + if (fIntersections->hasT(cubicT)) { + continue; + } + double lineT = fLine.nearPoint(fCubic[cIndex]); + if (lineT < 0) { + continue; + } + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double lineT = line.nearPoint(cubic[cIndex]); - if (lineT < 0) { - continue; - } - intersections.insert(cubicT, lineT, cubic[cIndex]); } -} -void addExactHorizontalEndPoints(double left, double right, double y) { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double lineT = SkDLine::ExactPointH(cubic[cIndex], left, right, y); - if (lineT < 0) { - continue; + void addExactHorizontalEndPoints(double left, double right, double y) { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double lineT = SkDLine::ExactPointH(fCubic[cIndex], left, right, y); + if (lineT < 0) { + continue; + } + double cubicT = (double) (cIndex >> 1); + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double cubicT = (double) (cIndex >> 1); - intersections.insert(cubicT, lineT, cubic[cIndex]); } -} -void addNearHorizontalEndPoints(double left, double right, double y) { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double cubicT = (double) (cIndex >> 1); - if (intersections.hasT(cubicT)) { - continue; + void addNearHorizontalEndPoints(double left, double right, double y) { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double cubicT = (double) (cIndex >> 1); + if (fIntersections->hasT(cubicT)) { + continue; + } + double lineT = SkDLine::NearPointH(fCubic[cIndex], left, right, y); + if (lineT < 0) { + continue; + } + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double lineT = SkDLine::NearPointH(cubic[cIndex], left, right, y); - if (lineT < 0) { - continue; - } - intersections.insert(cubicT, lineT, cubic[cIndex]); + // FIXME: see if line end is nearly on cubic } - // FIXME: see if line end is nearly on cubic -} -void addExactVerticalEndPoints(double top, double bottom, double x) { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double lineT = SkDLine::ExactPointV(cubic[cIndex], top, bottom, x); - if (lineT < 0) { - continue; + void addExactVerticalEndPoints(double top, double bottom, double x) { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double lineT = SkDLine::ExactPointV(fCubic[cIndex], top, bottom, x); + if (lineT < 0) { + continue; + } + double cubicT = (double) (cIndex >> 1); + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double cubicT = (double) (cIndex >> 1); - intersections.insert(cubicT, lineT, cubic[cIndex]); } -} -void addNearVerticalEndPoints(double top, double bottom, double x) { - for (int cIndex = 0; cIndex < 4; cIndex += 3) { - double cubicT = (double) (cIndex >> 1); - if (intersections.hasT(cubicT)) { - continue; + void addNearVerticalEndPoints(double top, double bottom, double x) { + for (int cIndex = 0; cIndex < 4; cIndex += 3) { + double cubicT = (double) (cIndex >> 1); + if (fIntersections->hasT(cubicT)) { + continue; + } + double lineT = SkDLine::NearPointV(fCubic[cIndex], top, bottom, x); + if (lineT < 0) { + continue; + } + fIntersections->insert(cubicT, lineT, fCubic[cIndex]); } - double lineT = SkDLine::NearPointV(cubic[cIndex], top, bottom, x); - if (lineT < 0) { - continue; - } - intersections.insert(cubicT, lineT, cubic[cIndex]); + // FIXME: see if line end is nearly on cubic } - // FIXME: see if line end is nearly on cubic -} -double findLineT(double t) { - SkDPoint xy = cubic.xyAtT(t); - double dx = line[1].fX - line[0].fX; - double dy = line[1].fY - line[0].fY; - if (fabs(dx) > fabs(dy)) { - return (xy.fX - line[0].fX) / dx; + double findLineT(double t) { + SkDPoint xy = fCubic.ptAtT(t); + double dx = fLine[1].fX - fLine[0].fX; + double dy = fLine[1].fY - fLine[0].fY; + if (fabs(dx) > fabs(dy)) { + return (xy.fX - fLine[0].fX) / dx; + } + return (xy.fY - fLine[0].fY) / dy; } - return (xy.fY - line[0].fY) / dy; -} -static bool pinTs(double* cubicT, double* lineT) { - if (!approximately_one_or_less(*lineT)) { - return false; - } - if (!approximately_zero_or_more(*lineT)) { - return false; - } - if (precisely_less_than_zero(*cubicT)) { - *cubicT = 0; - } else if (precisely_greater_than_one(*cubicT)) { - *cubicT = 1; - } - if (precisely_less_than_zero(*lineT)) { - *lineT = 0; - } else if (precisely_greater_than_one(*lineT)) { - *lineT = 1; + bool pinTs(double* cubicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) { + if (!approximately_one_or_less(*lineT)) { + return false; + } + if (!approximately_zero_or_more(*lineT)) { + return false; + } + double cT = *cubicT = SkPinT(*cubicT); + double lT = *lineT = SkPinT(*lineT); + if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && cT != 0 && cT != 1)) { + *pt = fLine.ptAtT(lT); + } else if (ptSet == kPointUninitialized) { + *pt = fCubic.ptAtT(cT); + } + return true; } - return true; -} private: - -const SkDCubic& cubic; -const SkDLine& line; -SkIntersections& intersections; -bool fAllowNear; + const SkDCubic& fCubic; + const SkDLine& fLine; + SkIntersections* fIntersections; + bool fAllowNear; }; int SkIntersections::horizontal(const SkDCubic& cubic, double left, double right, double y, bool flipped) { - LineCubicIntersections c(cubic, *(static_cast<SkDLine*>(0)), *this); + SkDLine line = {{{ left, y }, { right, y }}}; + LineCubicIntersections c(cubic, line, this); return c.horizontalIntersect(y, left, right, flipped); } int SkIntersections::vertical(const SkDCubic& cubic, double top, double bottom, double x, bool flipped) { - LineCubicIntersections c(cubic, *(static_cast<SkDLine*>(0)), *this); + SkDLine line = {{{ x, top }, { x, bottom }}}; + LineCubicIntersections c(cubic, line, this); return c.verticalIntersect(x, top, bottom, flipped); } int SkIntersections::intersect(const SkDCubic& cubic, const SkDLine& line) { - LineCubicIntersections c(cubic, line, *this); + LineCubicIntersections c(cubic, line, this); c.allowNear(fAllowNear); return c.intersect(); } int SkIntersections::intersectRay(const SkDCubic& cubic, const SkDLine& line) { - LineCubicIntersections c(cubic, line, *this); + LineCubicIntersections c(cubic, line, this); fUsed = c.intersectRay(fT[0]); for (int index = 0; index < fUsed; ++index) { - fPt[index] = cubic.xyAtT(fT[0][index]); + fPt[index] = cubic.ptAtT(fT[0][index]); } return fUsed; } diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp index faa7c1d392..46118429cd 100644 --- a/src/pathops/SkDLineIntersection.cpp +++ b/src/pathops/SkDLineIntersection.cpp @@ -27,9 +27,9 @@ SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) { } int SkIntersections::computePoints(const SkDLine& line, int used) { - fPt[0] = line.xyAtT(fT[0][0]); + fPt[0] = line.ptAtT(fT[0][0]); if ((fUsed = used) == 2) { - fPt[1] = line.xyAtT(fT[0][1]); + fPt[1] = line.ptAtT(fT[0][1]); } return fUsed; } @@ -102,19 +102,24 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { byLen * axLen == ayLen * bxLen byLen * axLen - ayLen * bxLen == 0 ( == denom ) */ - double denom = byLen * axLen - ayLen * bxLen; - if (0 != denom) { + double axByLen = axLen * byLen; + double ayBxLen = ayLen * bxLen; + // detect parallel lines the same way here and in SkOpAngle operator < + // so that non-parallel means they are also sortable + bool parallel = AlmostEqualUlps(axByLen, ayBxLen); + if (!parallel) { double ab0y = a[0].fY - b[0].fY; double ab0x = a[0].fX - b[0].fX; double numerA = ab0y * bxLen - byLen * ab0x; double numerB = ab0y * axLen - ayLen * ab0x; + double denom = axByLen - ayBxLen; if (between(0, numerA, denom) && between(0, numerB, denom)) { fT[0][0] = numerA / denom; fT[1][0] = numerB / denom; - return computePoints(a, 1); + computePoints(a, 1); } } - if (fAllowNear || 0 == denom) { + if (fAllowNear || parallel) { for (int iA = 0; iA < 2; ++iA) { if ((t = b.nearPoint(a[iA])) >= 0) { insert(iA, t, a[iA]); diff --git a/src/pathops/SkDQuadIntersection.cpp b/src/pathops/SkDQuadIntersection.cpp index 124c7dab06..e10fb3f139 100644 --- a/src/pathops/SkDQuadIntersection.cpp +++ b/src/pathops/SkDQuadIntersection.cpp @@ -87,8 +87,8 @@ static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) { for (int oddMan = 0; oddMan < 3; ++oddMan) { const SkDPoint* endPt[2]; for (int opp = 1; opp < 3; ++opp) { - int end = oddMan ^ opp; - if (end == 3) { + int end = oddMan ^ opp; // choose a value not equal to oddMan + if (3 == end) { // and correct so that largest value is 1 or 2 end = opp; } endPt[opp - 1] = &q1[end]; @@ -120,7 +120,7 @@ tryNextHalfPlane: static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax, SkIntersections* i, bool* subDivide) { double tMid = (tMin + tMax) / 2; - SkDPoint mid = q2.xyAtT(tMid); + SkDPoint mid = q2.ptAtT(tMid); SkDLine line; line[0] = line[1] = mid; SkDVector dxdy = q2.dxdyAtT(tMid); @@ -138,7 +138,7 @@ static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, dou if (roots == 2) { return false; } - SkDPoint pt2 = q1.xyAtT(rootTs[0][0]); + SkDPoint pt2 = q1.ptAtT(rootTs[0][0]); if (!pt2.approximatelyEqualHalf(mid)) { return false; } @@ -160,8 +160,8 @@ static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkD for (int idx2 = 0; idx2 < roots; ++idx2) { double t = rootTs[0][idx2]; #ifdef SK_DEBUG - SkDPoint qPt = q2.xyAtT(t); - SkDPoint lPt = testLines[index]->xyAtT(rootTs[1][idx2]); + SkDPoint qPt = q2.ptAtT(t); + SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]); SkASSERT(qPt.approximatelyEqual(lPt)); #endif if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) { @@ -183,12 +183,12 @@ static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkD tMin = tsFound[0]; tMax = tsFound[tsFound.count() - 1]; } - SkDPoint end = q2.xyAtT(t2s); + SkDPoint end = q2.ptAtT(t2s); bool startInTriangle = hull.pointInHull(end); if (startInTriangle) { tMin = t2s; } - end = q2.xyAtT(t2e); + end = q2.ptAtT(t2e); bool endInTriangle = hull.pointInHull(end); if (endInTriangle) { tMax = t2e; @@ -290,8 +290,8 @@ static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1 SkDPoint t1[3], t2[3]; int calcMask = ~0; do { - if (calcMask & (1 << 1)) t1[1] = quad1.xyAtT(*t1Seed); - if (calcMask & (1 << 4)) t2[1] = quad2.xyAtT(*t2Seed); + if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed); + if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed); if (t1[1].approximatelyEqual(t2[1])) { *pt = t1[1]; #if ONE_OFF_DEBUG @@ -300,10 +300,10 @@ static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1 #endif return true; } - if (calcMask & (1 << 0)) t1[0] = quad1.xyAtT(*t1Seed - tStep); - if (calcMask & (1 << 2)) t1[2] = quad1.xyAtT(*t1Seed + tStep); - if (calcMask & (1 << 3)) t2[0] = quad2.xyAtT(*t2Seed - tStep); - if (calcMask & (1 << 5)) t2[2] = quad2.xyAtT(*t2Seed + tStep); + if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(*t1Seed - tStep); + if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(*t1Seed + tStep); + if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(*t2Seed - tStep); + if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(*t2Seed + tStep); double dist[3][3]; // OPTIMIZE: using calcMask value permits skipping some distance calcuations // if prior loop's results are moved to correct slot for reuse @@ -361,6 +361,34 @@ static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1 return false; } +static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT, + const SkIntersections& orig, bool swap, SkIntersections* i) { + if (orig.used() == 1 && orig[!swap][0] == testT) { + return; + } + if (orig.used() == 2 && orig[!swap][1] == testT) { + return; + } + SkDLine tmpLine; + int testTIndex = testT << 1; + tmpLine[0] = tmpLine[1] = q2[testTIndex]; + tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY; + tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX; + SkIntersections impTs; + impTs.intersectRay(q1, tmpLine); + for (int index = 0; index < impTs.used(); ++index) { + SkDPoint realPt = impTs.pt(index); + if (!tmpLine[0].approximatelyEqualHalf(realPt)) { + continue; + } + if (swap) { + i->insert(testT, impTs[0][index], tmpLine[0]); + } else { + i->insert(impTs[0][index], testT, tmpLine[0]); + } + } +} + int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { // if the quads share an end point, check to see if they overlap @@ -379,6 +407,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { return fUsed; } // see if either quad is really a line + // FIXME: figure out why reduce step didn't find this earlier if (is_linear(q1, q2, this)) { return fUsed; } @@ -388,30 +417,42 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { set(swapped); return fUsed; } - SkDQuadImplicit i1(q1); - SkDQuadImplicit i2(q2); - if (i1.match(i2)) { - // FIXME: compute T values - // compute the intersections of the ends to find the coincident span - reset(); - bool useVertical = fabs(q1[0].fX - q1[2].fX) < fabs(q1[0].fY - q1[2].fY); - double t; - if ((t = SkIntersections::Axial(q1, q2[0], useVertical)) >= 0) { - insertCoincident(t, 0, q2[0]); - } - if ((t = SkIntersections::Axial(q1, q2[2], useVertical)) >= 0) { - insertCoincident(t, 1, q2[2]); - } - useVertical = fabs(q2[0].fX - q2[2].fX) < fabs(q2[0].fY - q2[2].fY); - if ((t = SkIntersections::Axial(q2, q1[0], useVertical)) >= 0) { - insertCoincident(0, t, q1[0]); + SkIntersections copyI(*this); + lookNearEnd(q1, q2, 0, *this, false, ©I); + lookNearEnd(q1, q2, 1, *this, false, ©I); + lookNearEnd(q2, q1, 0, *this, true, ©I); + lookNearEnd(q2, q1, 1, *this, true, ©I); + int innerEqual = 0; + if (copyI.fUsed >= 2) { + SkASSERT(copyI.fUsed <= 4); + double width = copyI[0][1] - copyI[0][0]; + int midEnd = 1; + for (int index = 2; index < copyI.fUsed; ++index) { + double testWidth = copyI[0][index] - copyI[0][index - 1]; + if (testWidth <= width) { + continue; + } + midEnd = index; } - if ((t = SkIntersections::Axial(q2, q1[2], useVertical)) >= 0) { - insertCoincident(1, t, q1[2]); + for (int index = 0; index < 2; ++index) { + double testT = (copyI[0][midEnd] * (index + 1) + + copyI[0][midEnd - 1] * (2 - index)) / 3; + SkDPoint testPt1 = q1.ptAtT(testT); + testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3; + SkDPoint testPt2 = q2.ptAtT(testT); + innerEqual += testPt1.approximatelyEqual(testPt2); } - SkASSERT(coincidentUsed() <= 2); + } + bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2; + if (expectCoincident) { + reset(); + insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]); + int last = copyI.fUsed - 1; + insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]); return fUsed; } + SkDQuadImplicit i1(q1); + SkDQuadImplicit i2(q2); int index; bool flip1 = q1[2] == q2[0]; bool flip2 = q1[0] == q2[2]; @@ -423,7 +464,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { int r1Count = addValidRoots(roots1, rootCount, roots1Copy); SkDPoint pts1[4]; for (index = 0; index < r1Count; ++index) { - pts1[index] = q1.xyAtT(roots1Copy[index]); + pts1[index] = q1.ptAtT(roots1Copy[index]); } double roots2[4]; int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0); @@ -431,7 +472,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) { int r2Count = addValidRoots(roots2, rootCount2, roots2Copy); SkDPoint pts2[4]; for (index = 0; index < r2Count; ++index) { - pts2[index] = q2.xyAtT(roots2Copy[index]); + pts2[index] = q2.ptAtT(roots2Copy[index]); } if (r1Count == r2Count && r1Count <= 1) { if (r1Count == 1) { diff --git a/src/pathops/SkDQuadLineIntersection.cpp b/src/pathops/SkDQuadLineIntersection.cpp index 9df2dc248a..58b3060ab3 100644 --- a/src/pathops/SkDQuadLineIntersection.cpp +++ b/src/pathops/SkDQuadLineIntersection.cpp @@ -89,10 +89,15 @@ Thus, if the slope of the line tends towards vertical, we use: class LineQuadraticIntersections { public: + enum PinTPoint { + kPointUninitialized, + kPointInitialized + }; + LineQuadraticIntersections(const SkDQuad& q, const SkDLine& l, SkIntersections* i) - : quad(q) - , line(l) - , intersections(i) + : fQuad(q) + , fLine(l) + , fIntersections(i) , fAllowNear(true) { } @@ -116,11 +121,11 @@ public: for each of the three points (e.g. n = 0 to 2) quad[n].fY' = (quad[n].fY - line[0].fY) * A - (quad[n].fX - line[0].fX) * O */ - double adj = line[1].fX - line[0].fX; - double opp = line[1].fY - line[0].fY; + double adj = fLine[1].fX - fLine[0].fX; + double opp = fLine[1].fY - fLine[0].fY; double r[3]; for (int n = 0; n < 3; ++n) { - r[n] = (quad[n].fY - line[0].fY) * adj - (quad[n].fX - line[0].fX) * opp; + r[n] = (fQuad[n].fY - fLine[0].fY) * adj - (fQuad[n].fX - fLine[0].fX) * opp; } double A = r[2]; double B = r[1]; @@ -137,21 +142,21 @@ public: for (int index = 0; index < roots; ++index) { double quadT = rootVals[index]; double lineT = findLineT(quadT); - if (PinTs(&quadT, &lineT)) { - SkDPoint pt = line.xyAtT(lineT); - intersections->insert(quadT, lineT, pt); + SkDPoint pt; + if (pinTs(&quadT, &lineT, &pt, kPointUninitialized)) { + fIntersections->insert(quadT, lineT, pt); } } if (fAllowNear) { addNearEndPoints(); } - return intersections->used(); + return fIntersections->used(); } int horizontalIntersect(double axisIntercept, double roots[2]) { - double D = quad[2].fY; // f - double E = quad[1].fY; // e - double F = quad[0].fY; // d + double D = fQuad[2].fY; // f + double E = fQuad[1].fY; // e + double F = fQuad[0].fY; // d D += F - 2 * E; // D = d - 2*e + f E -= F; // E = -(d - e) F -= axisIntercept; @@ -164,25 +169,25 @@ public: int roots = horizontalIntersect(axisIntercept, rootVals); for (int index = 0; index < roots; ++index) { double quadT = rootVals[index]; - SkDPoint pt = quad.xyAtT(quadT); + SkDPoint pt = fQuad.ptAtT(quadT); double lineT = (pt.fX - left) / (right - left); - if (PinTs(&quadT, &lineT)) { - intersections->insert(quadT, lineT, pt); + if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) { + fIntersections->insert(quadT, lineT, pt); } } if (fAllowNear) { addNearHorizontalEndPoints(left, right, axisIntercept); } if (flipped) { - intersections->flip(); + fIntersections->flip(); } - return intersections->used(); + return fIntersections->used(); } int verticalIntersect(double axisIntercept, double roots[2]) { - double D = quad[2].fX; // f - double E = quad[1].fX; // e - double F = quad[0].fX; // d + double D = fQuad[2].fX; // f + double E = fQuad[1].fX; // e + double F = fQuad[0].fX; // d D += F - 2 * E; // D = d - 2*e + f E -= F; // E = -(d - e) F -= axisIntercept; @@ -195,107 +200,107 @@ public: int roots = verticalIntersect(axisIntercept, rootVals); for (int index = 0; index < roots; ++index) { double quadT = rootVals[index]; - SkDPoint pt = quad.xyAtT(quadT); + SkDPoint pt = fQuad.ptAtT(quadT); double lineT = (pt.fY - top) / (bottom - top); - if (PinTs(&quadT, &lineT)) { - intersections->insert(quadT, lineT, pt); + if (pinTs(&quadT, &lineT, &pt, kPointInitialized)) { + fIntersections->insert(quadT, lineT, pt); } } if (fAllowNear) { addNearVerticalEndPoints(top, bottom, axisIntercept); } if (flipped) { - intersections->flip(); + fIntersections->flip(); } - return intersections->used(); + return fIntersections->used(); } protected: // add endpoints first to get zero and one t values exactly void addExactEndPoints() { for (int qIndex = 0; qIndex < 3; qIndex += 2) { - double lineT = line.exactPoint(quad[qIndex]); + double lineT = fLine.exactPoint(fQuad[qIndex]); if (lineT < 0) { continue; } double quadT = (double) (qIndex >> 1); - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } } void addNearEndPoints() { for (int qIndex = 0; qIndex < 3; qIndex += 2) { double quadT = (double) (qIndex >> 1); - if (intersections->hasT(quadT)) { + if (fIntersections->hasT(quadT)) { continue; } - double lineT = line.nearPoint(quad[qIndex]); + double lineT = fLine.nearPoint(fQuad[qIndex]); if (lineT < 0) { continue; } - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } // FIXME: see if line end is nearly on quad } void addExactHorizontalEndPoints(double left, double right, double y) { for (int qIndex = 0; qIndex < 3; qIndex += 2) { - double lineT = SkDLine::ExactPointH(quad[qIndex], left, right, y); + double lineT = SkDLine::ExactPointH(fQuad[qIndex], left, right, y); if (lineT < 0) { continue; } double quadT = (double) (qIndex >> 1); - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } } void addNearHorizontalEndPoints(double left, double right, double y) { for (int qIndex = 0; qIndex < 3; qIndex += 2) { double quadT = (double) (qIndex >> 1); - if (intersections->hasT(quadT)) { + if (fIntersections->hasT(quadT)) { continue; } - double lineT = SkDLine::NearPointH(quad[qIndex], left, right, y); + double lineT = SkDLine::NearPointH(fQuad[qIndex], left, right, y); if (lineT < 0) { continue; } - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } // FIXME: see if line end is nearly on quad } void addExactVerticalEndPoints(double top, double bottom, double x) { for (int qIndex = 0; qIndex < 3; qIndex += 2) { - double lineT = SkDLine::ExactPointV(quad[qIndex], top, bottom, x); + double lineT = SkDLine::ExactPointV(fQuad[qIndex], top, bottom, x); if (lineT < 0) { continue; } double quadT = (double) (qIndex >> 1); - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } } void addNearVerticalEndPoints(double top, double bottom, double x) { for (int qIndex = 0; qIndex < 3; qIndex += 2) { double quadT = (double) (qIndex >> 1); - if (intersections->hasT(quadT)) { + if (fIntersections->hasT(quadT)) { continue; } - double lineT = SkDLine::NearPointV(quad[qIndex], top, bottom, x); + double lineT = SkDLine::NearPointV(fQuad[qIndex], top, bottom, x); if (lineT < 0) { continue; } - intersections->insert(quadT, lineT, quad[qIndex]); + fIntersections->insert(quadT, lineT, fQuad[qIndex]); } // FIXME: see if line end is nearly on quad } double findLineT(double t) { - SkDPoint xy = quad.xyAtT(t); - double dx = line[1].fX - line[0].fX; - double dy = line[1].fY - line[0].fY; - double dxT = (xy.fX - line[0].fX) / dx; - double dyT = (xy.fY - line[0].fY) / dy; + SkDPoint xy = fQuad.ptAtT(t); + double dx = fLine[1].fX - fLine[0].fX; + double dy = fLine[1].fY - fLine[0].fY; + double dxT = (xy.fX - fLine[0].fX) / dx; + double dyT = (xy.fY - fLine[0].fY) / dy; if (!between(FLT_EPSILON, dxT, 1 - FLT_EPSILON) && between(0, dyT, 1)) { return dyT; } @@ -305,22 +310,27 @@ protected: return fabs(dx) > fabs(dy) ? dxT : dyT; } - static bool PinTs(double* quadT, double* lineT) { + bool pinTs(double* quadT, double* lineT, SkDPoint* pt, PinTPoint ptSet) { if (!approximately_one_or_less(*lineT)) { return false; } if (!approximately_zero_or_more(*lineT)) { return false; } - *quadT = SkPinT(*quadT); - *lineT = SkPinT(*lineT); + double qT = *quadT = SkPinT(*quadT); + double lT = *lineT = SkPinT(*lineT); + if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) { + *pt = fLine.ptAtT(lT); + } else if (ptSet == kPointUninitialized) { + *pt = fQuad.ptAtT(qT); + } return true; } private: - const SkDQuad& quad; - const SkDLine& line; - SkIntersections* intersections; + const SkDQuad& fQuad; + const SkDLine& fLine; + SkIntersections* fIntersections; bool fAllowNear; }; @@ -332,7 +342,7 @@ static double horizontalIntersect(const SkDQuad& quad, const SkDPoint& pt) { int roots = q.horizontalIntersect(pt.fY, rootVals); for (int index = 0; index < roots; ++index) { double t = rootVals[index]; - SkDPoint qPt = quad.xyAtT(t); + SkDPoint qPt = quad.ptAtT(t); if (AlmostEqualUlps(qPt.fX, pt.fX)) { return t; } @@ -347,7 +357,7 @@ static double verticalIntersect(const SkDQuad& quad, const SkDPoint& pt) { int roots = q.verticalIntersect(pt.fX, rootVals); for (int index = 0; index < roots; ++index) { double t = rootVals[index]; - SkDPoint qPt = quad.xyAtT(t); + SkDPoint qPt = quad.ptAtT(t); if (AlmostEqualUlps(qPt.fY, pt.fY)) { return t; } @@ -364,13 +374,15 @@ double SkIntersections::Axial(const SkDQuad& q1, const SkDPoint& p, bool vertica int SkIntersections::horizontal(const SkDQuad& quad, double left, double right, double y, bool flipped) { - LineQuadraticIntersections q(quad, *(static_cast<SkDLine*>(0)), this); + SkDLine line = {{{ left, y }, { right, y }}}; + LineQuadraticIntersections q(quad, line, this); return q.horizontalIntersect(y, left, right, flipped); } int SkIntersections::vertical(const SkDQuad& quad, double top, double bottom, double x, bool flipped) { - LineQuadraticIntersections q(quad, *(static_cast<SkDLine*>(0)), this); + SkDLine line = {{{ x, top }, { x, bottom }}}; + LineQuadraticIntersections q(quad, line, this); return q.verticalIntersect(x, top, bottom, flipped); } @@ -384,7 +396,7 @@ int SkIntersections::intersectRay(const SkDQuad& quad, const SkDLine& line) { LineQuadraticIntersections q(quad, line, this); fUsed = q.intersectRay(fT[0]); for (int index = 0; index < fUsed; ++index) { - fPt[index] = quad.xyAtT(fT[0][index]); + fPt[index] = quad.ptAtT(fT[0][index]); } return fUsed; } diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp index fe23316683..242c67b926 100644 --- a/src/pathops/SkIntersections.cpp +++ b/src/pathops/SkIntersections.cpp @@ -54,108 +54,6 @@ void SkIntersections::flip() { } } -void SkIntersections::insertCoincidentPair(double s1, double e1, double s2, double e2, - const SkDPoint& startPt, const SkDPoint& endPt) { - SkASSERT(s1 < e1); - SkASSERT(s2 != e2); - if (coincidentUsed() != fUsed) { // if the curve is partially coincident, treat it as fully so - for (int index = fUsed - 1; index >= 0; --index) { - if (fIsCoincident[0] & (1 << index)) { - continue; - } - double nonCoinT = fT[0][index]; - if (!between(s1, nonCoinT, e1)) { - if (s1 > nonCoinT) { - s1 = nonCoinT; - } else { - e1 = nonCoinT; - } - } - nonCoinT = fT[1][index]; - if (!between(s2, nonCoinT, e2)) { - if ((s2 > nonCoinT) ^ (s2 > e2)) { - s2 = nonCoinT; - } else { - e2 = nonCoinT; - } - } - removeOne(index); - } - } - SkASSERT(coincidentUsed() == fUsed); - SkASSERT((coincidentUsed() & 1) != 1); - int i1 = 0; - int i2 = 0; - do { - while (i1 < fUsed && !(fIsCoincident[fSwap] & (1 << i1))) { - ++i1; - } - if (i1 == fUsed) { - break; - } - SkASSERT(i1 < fUsed); - int iEnd1 = i1 + 1; - while (!(fIsCoincident[fSwap] & (1 << iEnd1))) { - ++iEnd1; - } - SkASSERT(iEnd1 < fUsed); - double cs1 = fT[fSwap][i1]; - double ce1 = fT[fSwap][iEnd1]; - bool s1in = between(cs1, s1, ce1) || startPt.approximatelyEqual(fPt[i1]) - || startPt.approximatelyEqual(fPt[iEnd1]); - bool e1in = between(cs1, e1, ce1) || endPt.approximatelyEqual(fPt[i1]) - || endPt.approximatelyEqual(fPt[iEnd1]); - while (i2 < fUsed && !(fIsCoincident[fSwap ^ 1] & (1 << i2))) { - ++i2; - } - int iEnd2 = i2 + 1; - while (!(fIsCoincident[fSwap ^ 1] & (1 << iEnd2))) { - ++iEnd2; - } - SkASSERT(iEnd2 < fUsed); - double cs2 = fT[fSwap ^ 1][i2]; - double ce2 = fT[fSwap ^ 1][iEnd2]; - bool s2in = between(cs2, s2, ce2) || startPt.approximatelyEqual(fPt[i2]) - || startPt.approximatelyEqual(fPt[iEnd2]); - bool e2in = between(cs2, e2, ce2) || endPt.approximatelyEqual(fPt[i2]) - || endPt.approximatelyEqual(fPt[iEnd2]); - if ((s1in | e1in) & (s2in | e2in)) { - if (s1 < cs1) { - fT[fSwap][i1] = s1; - fPt[i1] = startPt; - } else if (e1 < cs1) { - fT[fSwap][i1] = e1; - fPt[i1] = endPt; - } - if (s1 > ce1) { - fT[fSwap][iEnd1] = s1; - fPt[iEnd1] = startPt; - } else if (e1 > ce1) { - fT[fSwap][iEnd1] = e1; - fPt[iEnd1] = endPt; - } - if (s2 > e2) { - SkTSwap(cs2, ce2); - SkTSwap(i2, iEnd2); - } - if (s2 < cs2) { - fT[fSwap ^ 1][i2] = s2; - } else if (e2 < cs2) { - fT[fSwap ^ 1][i2] = e2; - } - if (s2 > ce2) { - fT[fSwap ^ 1][iEnd2] = s2; - } else if (e2 > ce2) { - fT[fSwap ^ 1][iEnd2] = e2; - } - return; - } - } while (true); - SkASSERT(fUsed < 9); - insertCoincident(s1, s2, startPt); - insertCoincident(e1, e2, endPt); -} - int SkIntersections::insert(double one, double two, const SkDPoint& pt) { if (fIsCoincident[0] == 3 && between(fT[0][0], one, fT[0][1])) { // For now, don't allow a mix of coincident and non-coincident intersections diff --git a/src/pathops/SkIntersections.h b/src/pathops/SkIntersections.h index de3d44cd70..26a1d1a559 100644 --- a/src/pathops/SkIntersections.h +++ b/src/pathops/SkIntersections.h @@ -208,8 +208,6 @@ public: int insert(double one, double two, const SkDPoint& pt); // start if index == 0 : end if index == 1 void insertCoincident(double one, double two, const SkDPoint& pt); - void insertCoincidentPair(double s1, double e1, double s2, double e2, - const SkDPoint& startPt, const SkDPoint& endPt); int intersect(const SkDLine&, const SkDLine&); int intersect(const SkDQuad&, const SkDLine&); int intersect(const SkDQuad&, const SkDQuad&); diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index 5b20749957..7e69bb835b 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -1082,9 +1082,7 @@ bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) { // look to see if the curve end intersects an intermediary that intersects the other void SkOpSegment::checkEnds() { -#if 1 - return; // FIXME: suspect we will need the code below to make intersections consistent -#else + debugValidate(); SkTDArray<SkOpSpan> missingSpans; int count = fTs.count(); for (int index = 0; index < count; ++index) { @@ -1150,14 +1148,20 @@ nextPeeker: ; } int missingCount = missingSpans.count(); + if (missingCount == 0) { + return; + } + debugValidate(); for (int index = 0; index < missingCount; ++index) { const SkOpSpan& missing = missingSpans[index]; addTPair(missing.fT, missing.fOther, missing.fOtherT, false, missing.fPt); } - if (missingCount > 0) { - fixOtherTIndex(); + fixOtherTIndex(); + for (int index = 0; index < missingCount; ++index) { + const SkOpSpan& missing = missingSpans[index]; + missing.fOther->fixOtherTIndex(); } -#endif + debugValidate(); } bool SkOpSegment::equalPoints(int greaterTIndex, int lesserTIndex) { @@ -1792,13 +1796,16 @@ void SkOpSegment::fixOtherTIndex() { double oT = iSpan.fOtherT; SkOpSegment* other = iSpan.fOther; int oCount = other->fTs.count(); + SkDEBUGCODE(iSpan.fOtherIndex = -1); for (int o = 0; o < oCount; ++o) { SkOpSpan& oSpan = other->fTs[o]; if (oT == oSpan.fT && this == oSpan.fOther && oSpan.fOtherT == iSpan.fT) { iSpan.fOtherIndex = o; + oSpan.fOtherIndex = i; break; } } + SkASSERT(iSpan.fOtherIndex >= 0); } } @@ -2755,6 +2762,7 @@ void SkOpSegment::debugShowTs() const { #if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY void SkOpSegment::debugShowActiveSpans() const { + debugValidate(); if (done()) { return; } @@ -2763,8 +2771,6 @@ void SkOpSegment::debugShowActiveSpans() const { double lastT = -1; #endif for (int i = 0; i < fTs.count(); ++i) { - SkASSERT(&fTs[i] == &fTs[i].fOther->fTs[fTs[i].fOtherIndex].fOther-> - fTs[fTs[i].fOther->fTs[fTs[i].fOtherIndex].fOtherIndex]); if (fTs[i].fDone) { continue; } @@ -2995,3 +3001,27 @@ int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const { return sum; } #endif + +void SkOpSegment::debugValidate() const { +#if DEBUG_VALIDATE + int count = fTs.count(); + SkASSERT(count >= 2); + SkASSERT(fTs[0].fT == 0); + SkASSERT(fTs[count - 1].fT == 1); + int done = 0; + double t = -1; + for (int i = 0; i < count; ++i) { + const SkOpSpan& span = fTs[i]; + SkASSERT(t <= span.fT); + t = span.fT; + int otherIndex = span.fOtherIndex; + const SkOpSegment* other = span.fOther; + const SkOpSpan& otherSpan = other->fTs[otherIndex]; + SkASSERT(otherSpan.fPt == span.fPt); + SkASSERT(otherSpan.fOtherT == t); + SkASSERT(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); + done += span.fDone; + } + SkASSERT(done == fDoneSpans); +#endif +} diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h index 7e5e644f1d..bfaf4ed9de 100644 --- a/src/pathops/SkOpSegment.h +++ b/src/pathops/SkOpSegment.h @@ -130,6 +130,11 @@ public: return fTs[index].fOther; } + // was used only by right angle winding finding + SkPoint ptAtT(double mid) const { + return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid); + } + const SkPoint* pts() const { return fPts; } @@ -214,11 +219,6 @@ public: return span->fPt; } - // used only by right angle winding finding - SkPoint xyAtT(double mid) const { - return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid); - } - const SkPoint& xyAtT(int index) const { return xyAtT(&fTs[index]); } @@ -394,6 +394,7 @@ private: return value < 0 ? '?' : value <= 9 ? '0' + value : '+'; } #endif + void debugValidate() const; const SkPoint* fPts; SkPathOpsBounds fBounds; diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index 9a92b00acd..28cf59cdd3 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -17,7 +17,7 @@ static int contourRangeCheckY(const SkTArray<SkOpContour*, true>& contourList, S const double mid = *midPtr; const SkOpSegment* current = *currentPtr; double tAtMid = current->tAtMid(index, endIndex, mid); - SkPoint basePt = current->xyAtT(tAtMid); + SkPoint basePt = current->ptAtT(tAtMid); int contourCount = contourList.count(); SkScalar bestY = SK_ScalarMin; SkOpSegment* bestSeg = NULL; diff --git a/src/pathops/SkPathOpsCubic.cpp b/src/pathops/SkPathOpsCubic.cpp index 60dca44a27..5fbfeba50d 100644 --- a/src/pathops/SkPathOpsCubic.cpp +++ b/src/pathops/SkPathOpsCubic.cpp @@ -304,7 +304,7 @@ SkDPoint SkDCubic::top(double startT, double endT) const { int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extremeTs); for (int index = 0; index < roots; ++index) { double t = startT + (endT - startT) * extremeTs[index]; - SkDPoint mid = xyAtT(t); + SkDPoint mid = ptAtT(t); if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX)) { topPt = mid; } @@ -313,7 +313,13 @@ SkDPoint SkDCubic::top(double startT, double endT) const { return topPt; } -SkDPoint SkDCubic::xyAtT(double t) const { +SkDPoint SkDCubic::ptAtT(double t) const { + if (0 == t) { + return fPts[0]; + } + if (1 == t) { + return fPts[3]; + } double one_t = 1 - t; double one_t2 = one_t * one_t; double a = one_t2 * one_t; diff --git a/src/pathops/SkPathOpsCubic.h b/src/pathops/SkPathOpsCubic.h index f07af80dcd..1ba35be22b 100644 --- a/src/pathops/SkPathOpsCubic.h +++ b/src/pathops/SkPathOpsCubic.h @@ -53,6 +53,7 @@ struct SkDCubic { int findMaxCurvature(double tValues[]) const; bool isLinear(int startIndex, int endIndex) const; bool monotonicInY() const; + SkDPoint ptAtT(double t) const; static int RootsReal(double A, double B, double C, double D, double t[3]); static int RootsValidT(const double A, const double B, const double C, double D, double s[3]); bool serpentine() const; @@ -76,7 +77,6 @@ struct SkDCubic { SkDPoint top(double startT, double endT) const; void toQuadraticTs(double precision, SkTArray<double, true>* ts) const; SkDQuad toQuad() const; - SkDPoint xyAtT(double t) const; }; #endif diff --git a/src/pathops/SkPathOpsCurve.h b/src/pathops/SkPathOpsCurve.h index ca68a664e6..5d12cb811a 100644 --- a/src/pathops/SkPathOpsCurve.h +++ b/src/pathops/SkPathOpsCurve.h @@ -14,19 +14,19 @@ static SkDPoint dline_xy_at_t(const SkPoint a[2], double t) { SkDLine line; line.set(a); - return line.xyAtT(t); + return line.ptAtT(t); } static SkDPoint dquad_xy_at_t(const SkPoint a[3], double t) { SkDQuad quad; quad.set(a); - return quad.xyAtT(t); + return quad.ptAtT(t); } static SkDPoint dcubic_xy_at_t(const SkPoint a[4], double t) { SkDCubic cubic; cubic.set(a); - return cubic.xyAtT(t); + return cubic.ptAtT(t); } static SkDPoint (* const CurveDPointAtT[])(const SkPoint[], double ) = { @@ -123,7 +123,7 @@ static SkPoint (* const CurveTop[])(const SkPoint[], double , double ) = { static bool line_is_vertical(const SkPoint a[2], double startT, double endT) { SkDLine line; line.set(a); - SkDPoint dst[2] = { line.xyAtT(startT), line.xyAtT(endT) }; + SkDPoint dst[2] = { line.ptAtT(startT), line.ptAtT(endT) }; return AlmostEqualUlps(dst[0].fX, dst[1].fX); } diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h index 05fe241e0f..e4ef072b9e 100644 --- a/src/pathops/SkPathOpsDebug.h +++ b/src/pathops/SkPathOpsDebug.h @@ -64,6 +64,7 @@ extern int gDebugMaxWindValue; #define DEBUG_SORT_SINGLE 0 #define DEBUG_SWAP_TOP 0 #define DEBUG_UNSORTABLE 0 +#define DEBUG_VALIDATE 0 #define DEBUG_WIND_BUMP 0 #define DEBUG_WINDING 0 #define DEBUG_WINDING_AT_T 0 @@ -93,6 +94,7 @@ extern int gDebugMaxWindValue; #define DEBUG_SORT_SINGLE 0 #define DEBUG_SWAP_TOP 1 #define DEBUG_UNSORTABLE 1 +#define DEBUG_VALIDATE 1 #define DEBUG_WIND_BUMP 0 #define DEBUG_WINDING 1 #define DEBUG_WINDING_AT_T 1 diff --git a/src/pathops/SkPathOpsLine.cpp b/src/pathops/SkPathOpsLine.cpp index 47c0565b69..48c042a7fa 100644 --- a/src/pathops/SkPathOpsLine.cpp +++ b/src/pathops/SkPathOpsLine.cpp @@ -41,8 +41,13 @@ double SkDLine::isLeft(const SkDPoint& pt) const { return p0.cross(p2); } -// OPTIMIZE: assert if t is 0 or 1 (caller shouldn't pass only 0/1) -SkDPoint SkDLine::xyAtT(double t) const { +SkDPoint SkDLine::ptAtT(double t) const { + if (0 == t) { + return fPts[0]; + } + if (1 == t) { + return fPts[1]; + } double one_t = 1 - t; SkDPoint result = { one_t * fPts[0].fX + t * fPts[1].fX, one_t * fPts[0].fY + t * fPts[1].fY }; return result; @@ -72,7 +77,7 @@ double SkDLine::nearPoint(const SkDPoint& xy) const { return -1; } double t = numer / denom; - SkDPoint realPt = xyAtT(t); + SkDPoint realPt = ptAtT(t); SkDVector distU = xy - realPt; double distSq = distU.fX * distU.fX + distU.fY * distU.fY; double dist = sqrt(distSq); // OPTIMIZATION: can we compare against distSq instead ? diff --git a/src/pathops/SkPathOpsLine.h b/src/pathops/SkPathOpsLine.h index c5ac7fdb09..75f3bd1058 100644 --- a/src/pathops/SkPathOpsLine.h +++ b/src/pathops/SkPathOpsLine.h @@ -33,8 +33,8 @@ struct SkDLine { double nearPoint(const SkDPoint& xy) const; static double NearPointH(const SkDPoint& xy, double left, double right, double y); static double NearPointV(const SkDPoint& xy, double top, double bottom, double x); + SkDPoint ptAtT(double t) const; SkDLine subDivide(double t1, double t2) const; - SkDPoint xyAtT(double t) const; private: SkDVector tangent() const { return fPts[0] - fPts[1]; } }; diff --git a/src/pathops/SkPathOpsQuad.cpp b/src/pathops/SkPathOpsQuad.cpp index 636e3854fd..7d9ff52aa2 100644 --- a/src/pathops/SkPathOpsQuad.cpp +++ b/src/pathops/SkPathOpsQuad.cpp @@ -30,7 +30,7 @@ double SkDQuad::nearestT(const SkDPoint& pt) const { double distMin = SkTMin(d0, d2); int bestIndex = -1; for (int index = 0; index < roots; ++index) { - SkDPoint onQuad = xyAtT(ts[index]); + SkDPoint onQuad = ptAtT(ts[index]); double dist = pt.distanceSquared(onQuad); if (distMin > dist) { distMin = dist; @@ -57,7 +57,7 @@ SkDPoint SkDQuad::top(double startT, double endT) const { double extremeT; if (FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, &extremeT)) { extremeT = startT + (endT - startT) * extremeT; - SkDPoint test = xyAtT(extremeT); + SkDPoint test = ptAtT(extremeT); if (topPt.fY > test.fY || (topPt.fY == test.fY && topPt.fX > test.fX)) { topPt = test; } @@ -165,7 +165,13 @@ SkDVector SkDQuad::dxdyAtT(double t) const { } // OPTIMIZE: assert if caller passes in t == 0 / t == 1 ? -SkDPoint SkDQuad::xyAtT(double t) const { +SkDPoint SkDQuad::ptAtT(double t) const { + if (0 == t) { + return fPts[0]; + } + if (1 == t) { + return fPts[2]; + } double one_t = 1 - t; double a = one_t * one_t; double b = 2 * one_t * t; diff --git a/src/pathops/SkPathOpsQuad.h b/src/pathops/SkPathOpsQuad.h index d06f3449bf..c8650515bb 100644 --- a/src/pathops/SkPathOpsQuad.h +++ b/src/pathops/SkPathOpsQuad.h @@ -42,6 +42,7 @@ struct SkDQuad { bool monotonicInY() const; double nearestT(const SkDPoint&) const; bool pointInHull(const SkDPoint&) const; + SkDPoint ptAtT(double t) const; static int RootsReal(double A, double B, double C, double t[2]); static int RootsValidT(const double A, const double B, const double C, double s[2]); static void SetABC(const double* quad, double* a, double* b, double* c); @@ -60,7 +61,6 @@ struct SkDQuad { } SkDCubic toCubic() const; SkDPoint top(double startT, double endT) const; - SkDPoint xyAtT(double t) const; private: // static double Tangent(const double* quadratic, double t); // uncalled }; diff --git a/src/pathops/SkPathOpsRect.cpp b/src/pathops/SkPathOpsRect.cpp index 9d7f876cac..2ceed32900 100644 --- a/src/pathops/SkPathOpsRect.cpp +++ b/src/pathops/SkPathOpsRect.cpp @@ -26,7 +26,7 @@ void SkDRect::setBounds(const SkDQuad& quad) { roots += SkDQuad::FindExtrema(quad[0].fY, quad[1].fY, quad[2].fY, &tValues[roots]); } for (int x = 0; x < roots; ++x) { - add(quad.xyAtT(tValues[x])); + add(quad.ptAtT(tValues[x])); } } @@ -53,7 +53,7 @@ void SkDRect::setBounds(const SkDCubic& c) { roots += SkDCubic::FindExtrema(c[0].fY, c[1].fY, c[2].fY, c[3].fY, &tValues[roots]); } for (int x = 0; x < roots; ++x) { - add(c.xyAtT(tValues[x])); + add(c.ptAtT(tValues[x])); } } diff --git a/src/pathops/SkPathOpsTypes.cpp b/src/pathops/SkPathOpsTypes.cpp index a076d155f2..8135c57025 100644 --- a/src/pathops/SkPathOpsTypes.cpp +++ b/src/pathops/SkPathOpsTypes.cpp @@ -11,40 +11,40 @@ // from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ // FIXME: move to SkFloatBits.h -static bool equal_ulps(float A, float B, int epsilon) { +static bool equal_ulps(float a, float b, int epsilon) { SkFloatIntUnion floatIntA, floatIntB; - floatIntA.fFloat = A; - floatIntB.fFloat = B; + floatIntA.fFloat = a; + floatIntB.fFloat = b; // Different signs means they do not match. if ((floatIntA.fSignBitInt < 0) != (floatIntB.fSignBitInt < 0)) { // Check for equality to make sure +0 == -0 - return A == B; + return a == b; } // Find the difference in ULPs. int ulpsDiff = abs(floatIntA.fSignBitInt - floatIntB.fSignBitInt); return ulpsDiff <= epsilon; } -static bool less_ulps(float A, float B, int epsilon) { +static bool less_ulps(float a, float b, int epsilon) { SkFloatIntUnion floatIntA, floatIntB; - floatIntA.fFloat = A; - floatIntB.fFloat = B; + floatIntA.fFloat = a; + floatIntB.fFloat = b; // Check different signs with float epsilon since we only care if they're both close to 0. if ((floatIntA.fSignBitInt < 0) != (floatIntB.fSignBitInt < 0)) { - return A <= B + FLT_EPSILON * epsilon; + return a <= b + FLT_EPSILON * epsilon; } // Find the difference in ULPs. return floatIntA.fSignBitInt <= floatIntB.fSignBitInt + epsilon; } -bool AlmostEqualUlps(float A, float B) { +bool AlmostEqualUlps(float a, float b) { const int UlpsEpsilon = 16; - return equal_ulps(A, B, UlpsEpsilon); + return equal_ulps(a, b, UlpsEpsilon); } -bool RoughlyEqualUlps(float A, float B) { +bool RoughlyEqualUlps(float a, float b) { const int UlpsEpsilon = 256; - return equal_ulps(A, B, UlpsEpsilon); + return equal_ulps(a, b, UlpsEpsilon); } bool AlmostBetweenUlps(float a, float b, float c) { @@ -53,6 +53,19 @@ bool AlmostBetweenUlps(float a, float b, float c) { : less_ulps(b, a, UlpsEpsilon) && less_ulps(c, b, UlpsEpsilon); } +int UlpsDistance(float a, float b) { + SkFloatIntUnion floatIntA, floatIntB; + floatIntA.fFloat = a; + floatIntB.fFloat = b; + // Different signs means they do not match. + if ((floatIntA.fSignBitInt < 0) != (floatIntB.fSignBitInt < 0)) { + // Check for equality to make sure +0 == -0 + return a == b ? 0 : SK_MaxS32; + } + // Find the difference in ULPs. + return abs(floatIntA.fSignBitInt - floatIntB.fSignBitInt); +} + // cube root approximation using bit hack for 64-bit float // adapted from Kahan's cbrt static double cbrt_5d(double d) { diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h index c988691e26..6158f13576 100644 --- a/src/pathops/SkPathOpsTypes.h +++ b/src/pathops/SkPathOpsTypes.h @@ -23,19 +23,24 @@ enum SkPathOpsMask { }; // Use Almost Equal when comparing coordinates. Use epsilon to compare T values. -bool AlmostEqualUlps(float A, float B); -inline bool AlmostEqualUlps(double A, double B) { - return AlmostEqualUlps(SkDoubleToScalar(A), SkDoubleToScalar(B)); +bool AlmostEqualUlps(float a, float b); +inline bool AlmostEqualUlps(double a, double b) { + return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } -bool RoughlyEqualUlps(float A, float B); -inline bool RoughlyEqualUlps(double A, double B) { - return RoughlyEqualUlps(SkDoubleToScalar(A), SkDoubleToScalar(B)); +bool RoughlyEqualUlps(float a, float b); +inline bool RoughlyEqualUlps(double a, double b) { + return RoughlyEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b)); } bool AlmostBetweenUlps(float a, float b, float c); -inline bool AlmostBetweenUlps(double A, double B, double C) { - return AlmostBetweenUlps(SkDoubleToScalar(A), SkDoubleToScalar(B), SkDoubleToScalar(C)); +inline bool AlmostBetweenUlps(double a, double b, double c) { + return AlmostBetweenUlps(SkDoubleToScalar(a), SkDoubleToScalar(b), SkDoubleToScalar(c)); +} + +int UlpsDistance(float a, float b); +inline int UlpsDistance(double a, double b) { + return UlpsDistance(SkDoubleToScalar(a), SkDoubleToScalar(b)); } // FLT_EPSILON == 1.19209290E-07 == 1 / (2 ^ 23) |