| Index: src/pathops/SkDCubicIntersection.cpp
|
| diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
|
| index 6e049708ac1a046e32bc2a02864447b57e0d11af..19e7340cdc0d7778509ff59e1f61b56d552b8a70 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;
|
| }
|
|
|