gfx/skia/trunk/src/pathops/SkDQuadIntersection.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/pathops/SkDQuadIntersection.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,553 @@
     1.4 +// Another approach is to start with the implicit form of one curve and solve
     1.5 +// (seek implicit coefficients in QuadraticParameter.cpp
     1.6 +// by substituting in the parametric form of the other.
     1.7 +// The downside of this approach is that early rejects are difficult to come by.
     1.8 +// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
     1.9 +
    1.10 +
    1.11 +#include "SkDQuadImplicit.h"
    1.12 +#include "SkIntersections.h"
    1.13 +#include "SkPathOpsLine.h"
    1.14 +#include "SkQuarticRoot.h"
    1.15 +#include "SkTArray.h"
    1.16 +#include "SkTSort.h"
    1.17 +
    1.18 +/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
    1.19 + * and given x = at^2 + bt + c  (the parameterized form)
    1.20 + *           y = dt^2 + et + f
    1.21 + * then
    1.22 + * 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
    1.23 + */
    1.24 +
    1.25 +static int findRoots(const SkDQuadImplicit& i, const SkDQuad& quad, double roots[4],
    1.26 +        bool oneHint, bool flip, int firstCubicRoot) {
    1.27 +    SkDQuad flipped;
    1.28 +    const SkDQuad& q = flip ? (flipped = quad.flip()) : quad;
    1.29 +    double a, b, c;
    1.30 +    SkDQuad::SetABC(&q[0].fX, &a, &b, &c);
    1.31 +    double d, e, f;
    1.32 +    SkDQuad::SetABC(&q[0].fY, &d, &e, &f);
    1.33 +    const double t4 =     i.x2() *  a * a
    1.34 +                    +     i.xy() *  a * d
    1.35 +                    +     i.y2() *  d * d;
    1.36 +    const double t3 = 2 * i.x2() *  a * b
    1.37 +                    +     i.xy() * (a * e +     b * d)
    1.38 +                    + 2 * i.y2() *  d * e;
    1.39 +    const double t2 =     i.x2() * (b * b + 2 * a * c)
    1.40 +                    +     i.xy() * (c * d +     b * e + a * f)
    1.41 +                    +     i.y2() * (e * e + 2 * d * f)
    1.42 +                    +     i.x()  *  a
    1.43 +                    +     i.y()  *  d;
    1.44 +    const double t1 = 2 * i.x2() *  b * c
    1.45 +                    +     i.xy() * (c * e + b * f)
    1.46 +                    + 2 * i.y2() *  e * f
    1.47 +                    +     i.x()  *  b
    1.48 +                    +     i.y()  *  e;
    1.49 +    const double t0 =     i.x2() *  c * c
    1.50 +                    +     i.xy() *  c * f
    1.51 +                    +     i.y2() *  f * f
    1.52 +                    +     i.x()  *  c
    1.53 +                    +     i.y()  *  f
    1.54 +                    +     i.c();
    1.55 +    int rootCount = SkReducedQuarticRoots(t4, t3, t2, t1, t0, oneHint, roots);
    1.56 +    if (rootCount < 0) {
    1.57 +        rootCount = SkQuarticRootsReal(firstCubicRoot, t4, t3, t2, t1, t0, roots);
    1.58 +    }
    1.59 +    if (flip) {
    1.60 +        for (int index = 0; index < rootCount; ++index) {
    1.61 +            roots[index] = 1 - roots[index];
    1.62 +        }
    1.63 +    }
    1.64 +    return rootCount;
    1.65 +}
    1.66 +
    1.67 +static int addValidRoots(const double roots[4], const int count, double valid[4]) {
    1.68 +    int result = 0;
    1.69 +    int index;
    1.70 +    for (index = 0; index < count; ++index) {
    1.71 +        if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
    1.72 +            continue;
    1.73 +        }
    1.74 +        double t = 1 - roots[index];
    1.75 +        if (approximately_less_than_zero(t)) {
    1.76 +            t = 0;
    1.77 +        } else if (approximately_greater_than_one(t)) {
    1.78 +            t = 1;
    1.79 +        }
    1.80 +        valid[result++] = t;
    1.81 +    }
    1.82 +    return result;
    1.83 +}
    1.84 +
    1.85 +static bool only_end_pts_in_common(const SkDQuad& q1, const SkDQuad& q2) {
    1.86 +// the idea here is to see at minimum do a quick reject by rotating all points
    1.87 +// to either side of the line formed by connecting the endpoints
    1.88 +// if the opposite curves points are on the line or on the other side, the
    1.89 +// curves at most intersect at the endpoints
    1.90 +    for (int oddMan = 0; oddMan < 3; ++oddMan) {
    1.91 +        const SkDPoint* endPt[2];
    1.92 +        for (int opp = 1; opp < 3; ++opp) {
    1.93 +            int end = oddMan ^ opp;  // choose a value not equal to oddMan
    1.94 +            if (3 == end) {  // and correct so that largest value is 1 or 2
    1.95 +                end = opp;
    1.96 +            }
    1.97 +            endPt[opp - 1] = &q1[end];
    1.98 +        }
    1.99 +        double origX = endPt[0]->fX;
   1.100 +        double origY = endPt[0]->fY;
   1.101 +        double adj = endPt[1]->fX - origX;
   1.102 +        double opp = endPt[1]->fY - origY;
   1.103 +        double sign = (q1[oddMan].fY - origY) * adj - (q1[oddMan].fX - origX) * opp;
   1.104 +        if (approximately_zero(sign)) {
   1.105 +            goto tryNextHalfPlane;
   1.106 +        }
   1.107 +        for (int n = 0; n < 3; ++n) {
   1.108 +            double test = (q2[n].fY - origY) * adj - (q2[n].fX - origX) * opp;
   1.109 +            if (test * sign > 0 && !precisely_zero(test)) {
   1.110 +                goto tryNextHalfPlane;
   1.111 +            }
   1.112 +        }
   1.113 +        return true;
   1.114 +tryNextHalfPlane:
   1.115 +        ;
   1.116 +    }
   1.117 +    return false;
   1.118 +}
   1.119 +
   1.120 +// returns false if there's more than one intercept or the intercept doesn't match the point
   1.121 +// returns true if the intercept was successfully added or if the
   1.122 +// original quads need to be subdivided
   1.123 +static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax,
   1.124 +                          SkIntersections* i, bool* subDivide) {
   1.125 +    double tMid = (tMin + tMax) / 2;
   1.126 +    SkDPoint mid = q2.ptAtT(tMid);
   1.127 +    SkDLine line;
   1.128 +    line[0] = line[1] = mid;
   1.129 +    SkDVector dxdy = q2.dxdyAtT(tMid);
   1.130 +    line[0] -= dxdy;
   1.131 +    line[1] += dxdy;
   1.132 +    SkIntersections rootTs;
   1.133 +    rootTs.allowNear(false);
   1.134 +    int roots = rootTs.intersect(q1, line);
   1.135 +    if (roots == 0) {
   1.136 +        if (subDivide) {
   1.137 +            *subDivide = true;
   1.138 +        }
   1.139 +        return true;
   1.140 +    }
   1.141 +    if (roots == 2) {
   1.142 +        return false;
   1.143 +    }
   1.144 +    SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
   1.145 +    if (!pt2.approximatelyEqual(mid)) {
   1.146 +        return false;
   1.147 +    }
   1.148 +    i->insertSwap(rootTs[0][0], tMid, pt2);
   1.149 +    return true;
   1.150 +}
   1.151 +
   1.152 +static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2,
   1.153 +                            double t2s, double t2e, SkIntersections* i, bool* subDivide) {
   1.154 +    SkDQuad hull = q1.subDivide(t1s, t1e);
   1.155 +    SkDLine line = {{hull[2], hull[0]}};
   1.156 +    const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] };
   1.157 +    const size_t kTestCount = SK_ARRAY_COUNT(testLines);
   1.158 +    SkSTArray<kTestCount * 2, double, true> tsFound;
   1.159 +    for (size_t index = 0; index < kTestCount; ++index) {
   1.160 +        SkIntersections rootTs;
   1.161 +        rootTs.allowNear(false);
   1.162 +        int roots = rootTs.intersect(q2, *testLines[index]);
   1.163 +        for (int idx2 = 0; idx2 < roots; ++idx2) {
   1.164 +            double t = rootTs[0][idx2];
   1.165 +#ifdef SK_DEBUG
   1.166 +            SkDPoint qPt = q2.ptAtT(t);
   1.167 +            SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]);
   1.168 +            SkASSERT(qPt.approximatelyPEqual(lPt));
   1.169 +#endif
   1.170 +            if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) {
   1.171 +                continue;
   1.172 +            }
   1.173 +            tsFound.push_back(rootTs[0][idx2]);
   1.174 +        }
   1.175 +    }
   1.176 +    int tCount = tsFound.count();
   1.177 +    if (tCount <= 0) {
   1.178 +        return true;
   1.179 +    }
   1.180 +    double tMin, tMax;
   1.181 +    if (tCount == 1) {
   1.182 +        tMin = tMax = tsFound[0];
   1.183 +    } else {
   1.184 +        SkASSERT(tCount > 1);
   1.185 +        SkTQSort<double>(tsFound.begin(), tsFound.end() - 1);
   1.186 +        tMin = tsFound[0];
   1.187 +        tMax = tsFound[tsFound.count() - 1];
   1.188 +    }
   1.189 +    SkDPoint end = q2.ptAtT(t2s);
   1.190 +    bool startInTriangle = hull.pointInHull(end);
   1.191 +    if (startInTriangle) {
   1.192 +        tMin = t2s;
   1.193 +    }
   1.194 +    end = q2.ptAtT(t2e);
   1.195 +    bool endInTriangle = hull.pointInHull(end);
   1.196 +    if (endInTriangle) {
   1.197 +        tMax = t2e;
   1.198 +    }
   1.199 +    int split = 0;
   1.200 +    SkDVector dxy1, dxy2;
   1.201 +    if (tMin != tMax || tCount > 2) {
   1.202 +        dxy2 = q2.dxdyAtT(tMin);
   1.203 +        for (int index = 1; index < tCount; ++index) {
   1.204 +            dxy1 = dxy2;
   1.205 +            dxy2 = q2.dxdyAtT(tsFound[index]);
   1.206 +            double dot = dxy1.dot(dxy2);
   1.207 +            if (dot < 0) {
   1.208 +                split = index - 1;
   1.209 +                break;
   1.210 +            }
   1.211 +        }
   1.212 +    }
   1.213 +    if (split == 0) {  // there's one point
   1.214 +        if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) {
   1.215 +            return true;
   1.216 +        }
   1.217 +        i->swap();
   1.218 +        return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide);
   1.219 +    }
   1.220 +    // At this point, we have two ranges of t values -- treat each separately at the split
   1.221 +    bool result;
   1.222 +    if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) {
   1.223 +        result = true;
   1.224 +    } else {
   1.225 +        i->swap();
   1.226 +        result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide);
   1.227 +    }
   1.228 +    if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) {
   1.229 +        result = true;
   1.230 +    } else {
   1.231 +        i->swap();
   1.232 +        result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide);
   1.233 +    }
   1.234 +    return result;
   1.235 +}
   1.236 +
   1.237 +static double flat_measure(const SkDQuad& q) {
   1.238 +    SkDVector mid = q[1] - q[0];
   1.239 +    SkDVector dxy = q[2] - q[0];
   1.240 +    double length = dxy.length();  // OPTIMIZE: get rid of sqrt
   1.241 +    return fabs(mid.cross(dxy) / length);
   1.242 +}
   1.243 +
   1.244 +// FIXME ? should this measure both and then use the quad that is the flattest as the line?
   1.245 +static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
   1.246 +    double measure = flat_measure(q1);
   1.247 +    // OPTIMIZE: (get rid of sqrt) use approximately_zero
   1.248 +    if (!approximately_zero_sqrt(measure)) {
   1.249 +        return false;
   1.250 +    }
   1.251 +    return is_linear_inner(q1, 0, 1, q2, 0, 1, i, NULL);
   1.252 +}
   1.253 +
   1.254 +// FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
   1.255 +// avoid imprecision incurred with chopAt
   1.256 +static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2,
   1.257 +        double s2, double e2, SkIntersections* i) {
   1.258 +    double m1 = flat_measure(*q1);
   1.259 +    double m2 = flat_measure(*q2);
   1.260 +    i->reset();
   1.261 +    const SkDQuad* rounder, *flatter;
   1.262 +    double sf, midf, ef, sr, er;
   1.263 +    if (m2 < m1) {
   1.264 +        rounder = q1;
   1.265 +        sr = s1;
   1.266 +        er = e1;
   1.267 +        flatter = q2;
   1.268 +        sf = s2;
   1.269 +        midf = (s2 + e2) / 2;
   1.270 +        ef = e2;
   1.271 +    } else {
   1.272 +        rounder = q2;
   1.273 +        sr = s2;
   1.274 +        er = e2;
   1.275 +        flatter = q1;
   1.276 +        sf = s1;
   1.277 +        midf = (s1 + e1) / 2;
   1.278 +        ef = e1;
   1.279 +    }
   1.280 +    bool subDivide = false;
   1.281 +    is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
   1.282 +    if (subDivide) {
   1.283 +        relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
   1.284 +        relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
   1.285 +    }
   1.286 +    if (m2 < m1) {
   1.287 +        i->swapPts();
   1.288 +    }
   1.289 +}
   1.290 +
   1.291 +// each time through the loop, this computes values it had from the last loop
   1.292 +// if i == j == 1, the center values are still good
   1.293 +// otherwise, for i != 1 or j != 1, four of the values are still good
   1.294 +// and if i == 1 ^ j == 1, an additional value is good
   1.295 +static bool binary_search(const SkDQuad& quad1, const SkDQuad& quad2, double* t1Seed,
   1.296 +                          double* t2Seed, SkDPoint* pt) {
   1.297 +    double tStep = ROUGH_EPSILON;
   1.298 +    SkDPoint t1[3], t2[3];
   1.299 +    int calcMask = ~0;
   1.300 +    do {
   1.301 +        if (calcMask & (1 << 1)) t1[1] = quad1.ptAtT(*t1Seed);
   1.302 +        if (calcMask & (1 << 4)) t2[1] = quad2.ptAtT(*t2Seed);
   1.303 +        if (t1[1].approximatelyEqual(t2[1])) {
   1.304 +            *pt = t1[1];
   1.305 +    #if ONE_OFF_DEBUG
   1.306 +            SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) == (%1.9g,%1.9g)\n", __FUNCTION__,
   1.307 +                    t1Seed, t2Seed, t1[1].fX, t1[1].fY, t2[1].fX, t2[1].fY);
   1.308 +    #endif
   1.309 +            return true;
   1.310 +        }
   1.311 +        if (calcMask & (1 << 0)) t1[0] = quad1.ptAtT(*t1Seed - tStep);
   1.312 +        if (calcMask & (1 << 2)) t1[2] = quad1.ptAtT(*t1Seed + tStep);
   1.313 +        if (calcMask & (1 << 3)) t2[0] = quad2.ptAtT(*t2Seed - tStep);
   1.314 +        if (calcMask & (1 << 5)) t2[2] = quad2.ptAtT(*t2Seed + tStep);
   1.315 +        double dist[3][3];
   1.316 +        // OPTIMIZE: using calcMask value permits skipping some distance calcuations
   1.317 +        //   if prior loop's results are moved to correct slot for reuse
   1.318 +        dist[1][1] = t1[1].distanceSquared(t2[1]);
   1.319 +        int best_i = 1, best_j = 1;
   1.320 +        for (int i = 0; i < 3; ++i) {
   1.321 +            for (int j = 0; j < 3; ++j) {
   1.322 +                if (i == 1 && j == 1) {
   1.323 +                    continue;
   1.324 +                }
   1.325 +                dist[i][j] = t1[i].distanceSquared(t2[j]);
   1.326 +                if (dist[best_i][best_j] > dist[i][j]) {
   1.327 +                    best_i = i;
   1.328 +                    best_j = j;
   1.329 +                }
   1.330 +            }
   1.331 +        }
   1.332 +        if (best_i == 1 && best_j == 1) {
   1.333 +            tStep /= 2;
   1.334 +            if (tStep < FLT_EPSILON_HALF) {
   1.335 +                break;
   1.336 +            }
   1.337 +            calcMask = (1 << 0) | (1 << 2) | (1 << 3) | (1 << 5);
   1.338 +            continue;
   1.339 +        }
   1.340 +        if (best_i == 0) {
   1.341 +            *t1Seed -= tStep;
   1.342 +            t1[2] = t1[1];
   1.343 +            t1[1] = t1[0];
   1.344 +            calcMask = 1 << 0;
   1.345 +        } else if (best_i == 2) {
   1.346 +            *t1Seed += tStep;
   1.347 +            t1[0] = t1[1];
   1.348 +            t1[1] = t1[2];
   1.349 +            calcMask = 1 << 2;
   1.350 +        } else {
   1.351 +            calcMask = 0;
   1.352 +        }
   1.353 +        if (best_j == 0) {
   1.354 +            *t2Seed -= tStep;
   1.355 +            t2[2] = t2[1];
   1.356 +            t2[1] = t2[0];
   1.357 +            calcMask |= 1 << 3;
   1.358 +        } else if (best_j == 2) {
   1.359 +            *t2Seed += tStep;
   1.360 +            t2[0] = t2[1];
   1.361 +            t2[1] = t2[2];
   1.362 +            calcMask |= 1 << 5;
   1.363 +        }
   1.364 +    } while (true);
   1.365 +#if ONE_OFF_DEBUG
   1.366 +    SkDebugf("%s t1=%1.9g t2=%1.9g (%1.9g,%1.9g) != (%1.9g,%1.9g) %s\n", __FUNCTION__,
   1.367 +        t1Seed, t2Seed, t1[1].fX, t1[1].fY, t1[2].fX, t1[2].fY);
   1.368 +#endif
   1.369 +    return false;
   1.370 +}
   1.371 +
   1.372 +static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
   1.373 +        const SkIntersections& orig, bool swap, SkIntersections* i) {
   1.374 +    if (orig.used() == 1 && orig[!swap][0] == testT) {
   1.375 +        return;
   1.376 +    }
   1.377 +    if (orig.used() == 2 && orig[!swap][1] == testT) {
   1.378 +        return;
   1.379 +    }
   1.380 +    SkDLine tmpLine;
   1.381 +    int testTIndex = testT << 1;
   1.382 +    tmpLine[0] = tmpLine[1] = q2[testTIndex];
   1.383 +    tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY;
   1.384 +    tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX;
   1.385 +    SkIntersections impTs;
   1.386 +    impTs.intersectRay(q1, tmpLine);
   1.387 +    for (int index = 0; index < impTs.used(); ++index) {
   1.388 +        SkDPoint realPt = impTs.pt(index);
   1.389 +        if (!tmpLine[0].approximatelyEqual(realPt)) {
   1.390 +            continue;
   1.391 +        }
   1.392 +        if (swap) {
   1.393 +            i->insert(testT, impTs[0][index], tmpLine[0]);
   1.394 +        } else {
   1.395 +            i->insert(impTs[0][index], testT, tmpLine[0]);
   1.396 +        }
   1.397 +    }
   1.398 +}
   1.399 +
   1.400 +int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
   1.401 +    fMax = 4;
   1.402 +    // if the quads share an end point, check to see if they overlap
   1.403 +    for (int i1 = 0; i1 < 3; i1 += 2) {
   1.404 +        for (int i2 = 0; i2 < 3; i2 += 2) {
   1.405 +            if (q1[i1].asSkPoint() == q2[i2].asSkPoint()) {
   1.406 +                insert(i1 >> 1, i2 >> 1, q1[i1]);
   1.407 +            }
   1.408 +        }
   1.409 +    }
   1.410 +    SkASSERT(fUsed < 3);
   1.411 +    if (only_end_pts_in_common(q1, q2)) {
   1.412 +        return fUsed;
   1.413 +    }
   1.414 +    if (only_end_pts_in_common(q2, q1)) {
   1.415 +        return fUsed;
   1.416 +    }
   1.417 +    // see if either quad is really a line
   1.418 +    // FIXME: figure out why reduce step didn't find this earlier
   1.419 +    if (is_linear(q1, q2, this)) {
   1.420 +        return fUsed;
   1.421 +    }
   1.422 +    SkIntersections swapped;
   1.423 +    swapped.setMax(fMax);
   1.424 +    if (is_linear(q2, q1, &swapped)) {
   1.425 +        swapped.swapPts();
   1.426 +        set(swapped);
   1.427 +        return fUsed;
   1.428 +    }
   1.429 +    SkIntersections copyI(*this);
   1.430 +    lookNearEnd(q1, q2, 0, *this, false, &copyI);
   1.431 +    lookNearEnd(q1, q2, 1, *this, false, &copyI);
   1.432 +    lookNearEnd(q2, q1, 0, *this, true, &copyI);
   1.433 +    lookNearEnd(q2, q1, 1, *this, true, &copyI);
   1.434 +    int innerEqual = 0;
   1.435 +    if (copyI.fUsed >= 2) {
   1.436 +        SkASSERT(copyI.fUsed <= 4);
   1.437 +        double width = copyI[0][1] - copyI[0][0];
   1.438 +        int midEnd = 1;
   1.439 +        for (int index = 2; index < copyI.fUsed; ++index) {
   1.440 +            double testWidth = copyI[0][index] - copyI[0][index - 1];
   1.441 +            if (testWidth <= width) {
   1.442 +                continue;
   1.443 +            }
   1.444 +            midEnd = index;
   1.445 +        }
   1.446 +        for (int index = 0; index < 2; ++index) {
   1.447 +            double testT = (copyI[0][midEnd] * (index + 1)
   1.448 +                    + copyI[0][midEnd - 1] * (2 - index)) / 3;
   1.449 +            SkDPoint testPt1 = q1.ptAtT(testT);
   1.450 +            testT = (copyI[1][midEnd] * (index + 1) + copyI[1][midEnd - 1] * (2 - index)) / 3;
   1.451 +            SkDPoint testPt2 = q2.ptAtT(testT);
   1.452 +            innerEqual += testPt1.approximatelyEqual(testPt2);
   1.453 +        }
   1.454 +    }
   1.455 +    bool expectCoincident = copyI.fUsed >= 2 && innerEqual == 2;
   1.456 +    if (expectCoincident) {
   1.457 +        reset();
   1.458 +        insertCoincident(copyI[0][0], copyI[1][0], copyI.fPt[0]);
   1.459 +        int last = copyI.fUsed - 1;
   1.460 +        insertCoincident(copyI[0][last], copyI[1][last], copyI.fPt[last]);
   1.461 +        return fUsed;
   1.462 +    }
   1.463 +    SkDQuadImplicit i1(q1);
   1.464 +    SkDQuadImplicit i2(q2);
   1.465 +    int index;
   1.466 +    bool flip1 = q1[2] == q2[0];
   1.467 +    bool flip2 = q1[0] == q2[2];
   1.468 +    bool useCubic = q1[0] == q2[0];
   1.469 +    double roots1[4];
   1.470 +    int rootCount = findRoots(i2, q1, roots1, useCubic, flip1, 0);
   1.471 +    // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
   1.472 +    double roots1Copy[4];
   1.473 +    int r1Count = addValidRoots(roots1, rootCount, roots1Copy);
   1.474 +    SkDPoint pts1[4];
   1.475 +    for (index = 0; index < r1Count; ++index) {
   1.476 +        pts1[index] = q1.ptAtT(roots1Copy[index]);
   1.477 +    }
   1.478 +    double roots2[4];
   1.479 +    int rootCount2 = findRoots(i1, q2, roots2, useCubic, flip2, 0);
   1.480 +    double roots2Copy[4];
   1.481 +    int r2Count = addValidRoots(roots2, rootCount2, roots2Copy);
   1.482 +    SkDPoint pts2[4];
   1.483 +    for (index = 0; index < r2Count; ++index) {
   1.484 +        pts2[index] = q2.ptAtT(roots2Copy[index]);
   1.485 +    }
   1.486 +    if (r1Count == r2Count && r1Count <= 1) {
   1.487 +        if (r1Count == 1 && used() == 0) {
   1.488 +            if (pts1[0].approximatelyEqual(pts2[0])) {
   1.489 +                insert(roots1Copy[0], roots2Copy[0], pts1[0]);
   1.490 +            } else if (pts1[0].moreRoughlyEqual(pts2[0])) {
   1.491 +                // experiment: try to find intersection by chasing t
   1.492 +                if (binary_search(q1, q2, roots1Copy, roots2Copy, pts1)) {
   1.493 +                    insert(roots1Copy[0], roots2Copy[0], pts1[0]);
   1.494 +                }
   1.495 +            }
   1.496 +        }
   1.497 +        return fUsed;
   1.498 +    }
   1.499 +    int closest[4];
   1.500 +    double dist[4];
   1.501 +    bool foundSomething = false;
   1.502 +    for (index = 0; index < r1Count; ++index) {
   1.503 +        dist[index] = DBL_MAX;
   1.504 +        closest[index] = -1;
   1.505 +        for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
   1.506 +            if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
   1.507 +                continue;
   1.508 +            }
   1.509 +            double dx = pts2[ndex2].fX - pts1[index].fX;
   1.510 +            double dy = pts2[ndex2].fY - pts1[index].fY;
   1.511 +            double distance = dx * dx + dy * dy;
   1.512 +            if (dist[index] <= distance) {
   1.513 +                continue;
   1.514 +            }
   1.515 +            for (int outer = 0; outer < index; ++outer) {
   1.516 +                if (closest[outer] != ndex2) {
   1.517 +                    continue;
   1.518 +                }
   1.519 +                if (dist[outer] < distance) {
   1.520 +                    goto next;
   1.521 +                }
   1.522 +                closest[outer] = -1;
   1.523 +            }
   1.524 +            dist[index] = distance;
   1.525 +            closest[index] = ndex2;
   1.526 +            foundSomething = true;
   1.527 +        next:
   1.528 +            ;
   1.529 +        }
   1.530 +    }
   1.531 +    if (r1Count && r2Count && !foundSomething) {
   1.532 +        relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
   1.533 +        return fUsed;
   1.534 +    }
   1.535 +    int used = 0;
   1.536 +    do {
   1.537 +        double lowest = DBL_MAX;
   1.538 +        int lowestIndex = -1;
   1.539 +        for (index = 0; index < r1Count; ++index) {
   1.540 +            if (closest[index] < 0) {
   1.541 +                continue;
   1.542 +            }
   1.543 +            if (roots1Copy[index] < lowest) {
   1.544 +                lowestIndex = index;
   1.545 +                lowest = roots1Copy[index];
   1.546 +            }
   1.547 +        }
   1.548 +        if (lowestIndex < 0) {
   1.549 +            break;
   1.550 +        }
   1.551 +        insert(roots1Copy[lowestIndex], roots2Copy[closest[lowestIndex]],
   1.552 +                pts1[lowestIndex]);
   1.553 +        closest[lowestIndex] = -1;
   1.554 +    } while (++used < r1Count);
   1.555 +    return fUsed;
   1.556 +}

mercurial