Home | History | Annotate | Download | only in Intersection
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 #include "CurveIntersection.h"
      8 #include "Intersections.h"
      9 #include "IntersectionUtilities.h"
     10 #include "LineIntersection.h"
     11 #include "LineUtilities.h"
     12 #include "QuadraticLineSegments.h"
     13 #include "QuadraticUtilities.h"
     14 #include <algorithm> // for swap
     15 
     16 static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
     17 
     18 class QuadraticIntersections {
     19 public:
     20 
     21 QuadraticIntersections(const Quadratic& q1, const Quadratic& q2, Intersections& i)
     22     : quad1(q1)
     23     , quad2(q2)
     24     , intersections(i)
     25     , depth(0)
     26     , splits(0)
     27     , coinMinT1(-1) {
     28 }
     29 
     30 bool intersect() {
     31     double minT1, minT2, maxT1, maxT2;
     32     if (!bezier_clip(quad2, quad1, minT1, maxT1)) {
     33         return false;
     34     }
     35     if (!bezier_clip(quad1, quad2, minT2, maxT2)) {
     36         return false;
     37     }
     38     quad1Divisions = 1 / subDivisions(quad1);
     39     quad2Divisions = 1 / subDivisions(quad2);
     40     int split;
     41     if (maxT1 - minT1 < maxT2 - minT2) {
     42         intersections.swap();
     43         minT2 = 0;
     44         maxT2 = 1;
     45         split = maxT1 - minT1 > tClipLimit;
     46     } else {
     47         minT1 = 0;
     48         maxT1 = 1;
     49         split = (maxT2 - minT2 > tClipLimit) << 1;
     50     }
     51     return chop(minT1, maxT1, minT2, maxT2, split);
     52 }
     53 
     54 protected:
     55 
     56 bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
     57     bool t1IsLine = maxT1 - minT1 <= quad1Divisions;
     58     bool t2IsLine = maxT2 - minT2 <= quad2Divisions;
     59     if (t1IsLine | t2IsLine) {
     60         return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine);
     61     }
     62     Quadratic smaller, larger;
     63     // FIXME: carry last subdivide and reduceOrder result with quad
     64     sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller);
     65     sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger);
     66     double minT, maxT;
     67     if (!bezier_clip(smaller, larger, minT, maxT)) {
     68         if (approximately_equal(minT, maxT)) {
     69             double smallT, largeT;
     70             _Point q2pt, q1pt;
     71             if (intersections.swapped()) {
     72                 largeT = interp(minT2, maxT2, minT);
     73                 xy_at_t(quad2, largeT, q2pt.x, q2pt.y);
     74                 xy_at_t(quad1, minT1, q1pt.x, q1pt.y);
     75                 if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
     76                     smallT = minT1;
     77                 } else {
     78                     xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code
     79                     SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
     80                     smallT = maxT1;
     81                 }
     82             } else {
     83                 smallT = interp(minT1, maxT1, minT);
     84                 xy_at_t(quad1, smallT, q1pt.x, q1pt.y);
     85                 xy_at_t(quad2, minT2, q2pt.x, q2pt.y);
     86                 if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) {
     87                     largeT = minT2;
     88                 } else {
     89                     xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code
     90                     SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y));
     91                     largeT = maxT2;
     92                 }
     93             }
     94             intersections.add(smallT, largeT);
     95             return true;
     96         }
     97         return false;
     98     }
     99     int split;
    100     if (intersections.swapped()) {
    101         double newMinT1 = interp(minT1, maxT1, minT);
    102         double newMaxT1 = interp(minT1, maxT1, maxT);
    103         split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
    104 #define VERBOSE 0
    105 #if VERBOSE
    106         printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth,
    107             splits, newMinT1, newMaxT1, minT1, maxT1, split);
    108 #endif
    109         minT1 = newMinT1;
    110         maxT1 = newMaxT1;
    111     } else {
    112         double newMinT2 = interp(minT2, maxT2, minT);
    113         double newMaxT2 = interp(minT2, maxT2, maxT);
    114         split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
    115 #if VERBOSE
    116         printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth,
    117             splits, newMinT2, newMaxT2, minT2, maxT2, split);
    118 #endif
    119         minT2 = newMinT2;
    120         maxT2 = newMaxT2;
    121     }
    122     return chop(minT1, maxT1, minT2, maxT2, split);
    123 }
    124 
    125 bool intersectAsLine(double minT1, double maxT1, double minT2, double maxT2,
    126        bool treat1AsLine, bool treat2AsLine)
    127 {
    128     _Line line1, line2;
    129     if (intersections.swapped()) {
    130         SkTSwap(treat1AsLine, treat2AsLine);
    131         SkTSwap(minT1, minT2);
    132         SkTSwap(maxT1, maxT2);
    133     }
    134     if (coinMinT1 >= 0) {
    135         bool earlyExit;
    136         if ((earlyExit = coinMaxT1 == minT1)) {
    137             coinMaxT1 = maxT1;
    138         }
    139         if (coinMaxT2 == minT2) {
    140             coinMaxT2 = maxT2;
    141             return true;
    142         }
    143         if (earlyExit) {
    144             return true;
    145         }
    146         coinMinT1 = -1;
    147     }
    148     // do line/quadratic or even line/line intersection instead
    149     if (treat1AsLine) {
    150         xy_at_t(quad1, minT1, line1[0].x, line1[0].y);
    151         xy_at_t(quad1, maxT1, line1[1].x, line1[1].y);
    152     }
    153     if (treat2AsLine) {
    154         xy_at_t(quad2, minT2, line2[0].x, line2[0].y);
    155         xy_at_t(quad2, maxT2, line2[1].x, line2[1].y);
    156     }
    157     int pts;
    158     double smallT1, largeT1, smallT2, largeT2;
    159     if (treat1AsLine & treat2AsLine) {
    160         double t1[2], t2[2];
    161         pts = ::intersect(line1, line2, t1, t2);
    162         if (pts == 2) {
    163             smallT1 = interp(minT1, maxT1, t1[0]);
    164             largeT1 = interp(minT2, maxT2, t2[0]);
    165             smallT2 = interp(minT1, maxT1, t1[1]);
    166             largeT2 = interp(minT2, maxT2, t2[1]);
    167             intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
    168         } else {
    169             smallT1 = interp(minT1, maxT1, t1[0]);
    170             largeT1 = interp(minT2, maxT2, t2[0]);
    171             intersections.add(smallT1, largeT1);
    172         }
    173     } else {
    174         Intersections lq;
    175         pts = ::intersect(treat1AsLine ? quad2 : quad1,
    176                 treat1AsLine ? line1 : line2, lq);
    177         if (pts == 2) { // if the line and edge are coincident treat differently
    178             _Point midQuad, midLine;
    179             double midQuadT = (lq.fT[0][0] + lq.fT[0][1]) / 2;
    180             xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y);
    181             double lineT = t_at(treat1AsLine ? line1 : line2, midQuad);
    182             xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y);
    183             if (AlmostEqualUlps(midQuad.x, midLine.x)
    184                     && AlmostEqualUlps(midQuad.y, midLine.y)) {
    185                 smallT1 = lq.fT[0][0];
    186                 largeT1 = lq.fT[1][0];
    187                 smallT2 = lq.fT[0][1];
    188                 largeT2 = lq.fT[1][1];
    189                 if (treat2AsLine) {
    190                     smallT1 = interp(minT1, maxT1, smallT1);
    191                     smallT2 = interp(minT1, maxT1, smallT2);
    192                 } else {
    193                     largeT1 = interp(minT2, maxT2, largeT1);
    194                     largeT2 = interp(minT2, maxT2, largeT2);
    195                 }
    196                 intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
    197                 goto setCoinMinMax;
    198             }
    199         }
    200         for (int index = 0; index < pts; ++index) {
    201             smallT1 = lq.fT[0][index];
    202             largeT1 = lq.fT[1][index];
    203             if (treat2AsLine) {
    204                 smallT1 = interp(minT1, maxT1, smallT1);
    205             } else {
    206                 largeT1 = interp(minT2, maxT2, largeT1);
    207             }
    208             intersections.add(smallT1, largeT1);
    209         }
    210     }
    211     if (pts > 0) {
    212 setCoinMinMax:
    213         coinMinT1 = minT1;
    214         coinMaxT1 = maxT1;
    215         coinMinT2 = minT2;
    216         coinMaxT2 = maxT2;
    217     }
    218     return pts > 0;
    219 }
    220 
    221 bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
    222     ++depth;
    223     intersections.swap();
    224     if (split) {
    225         ++splits;
    226         if (split & 2) {
    227             double middle1 = (maxT1 + minT1) / 2;
    228             intersect(minT1, middle1, minT2, maxT2);
    229             intersect(middle1, maxT1, minT2, maxT2);
    230         } else {
    231             double middle2 = (maxT2 + minT2) / 2;
    232             intersect(minT1, maxT1, minT2, middle2);
    233             intersect(minT1, maxT1, middle2, maxT2);
    234         }
    235         --splits;
    236         intersections.swap();
    237         --depth;
    238         return intersections.intersected();
    239     }
    240     bool result = intersect(minT1, maxT1, minT2, maxT2);
    241     intersections.swap();
    242     --depth;
    243     return result;
    244 }
    245 
    246 private:
    247 
    248 const Quadratic& quad1;
    249 const Quadratic& quad2;
    250 Intersections& intersections;
    251 int depth;
    252 int splits;
    253 double quad1Divisions; // line segments to approximate original within error
    254 double quad2Divisions;
    255 double coinMinT1; // range of Ts where approximate line intersected curve
    256 double coinMaxT1;
    257 double coinMinT2;
    258 double coinMaxT2;
    259 };
    260 
    261 #include "LineParameters.h"
    262 
    263 static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
    264     // look to see if non-coincident data basically has unsortable tangents
    265 
    266     // look to see if a point between non-coincident data is on the curve
    267     int cIndex;
    268     for (int uIndex = 0; uIndex < i.fUsed; ) {
    269         double bestDist1 = 1;
    270         double bestDist2 = 1;
    271         int closest1 = -1;
    272         int closest2 = -1;
    273         for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) {
    274             double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]);
    275             if (bestDist1 > dist) {
    276                 bestDist1 = dist;
    277                 closest1 = cIndex;
    278             }
    279             dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]);
    280             if (bestDist2 > dist) {
    281                 bestDist2 = dist;
    282                 closest2 = cIndex;
    283             }
    284         }
    285         _Line ends;
    286         _Point mid;
    287         double t1 = i.fT[0][uIndex];
    288         xy_at_t(q1, t1, ends[0].x, ends[0].y);
    289         xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y);
    290         double midT = (t1 + i.fCoincidentT[0][closest1]) / 2;
    291         xy_at_t(q1, midT, mid.x, mid.y);
    292         LineParameters params;
    293         params.lineEndPoints(ends);
    294         double midDist = params.pointDistance(mid);
    295         // Note that we prefer to always measure t error, which does not scale,
    296         // instead of point error, which is scale dependent. FIXME
    297         if (!approximately_zero(midDist)) {
    298             ++uIndex;
    299             continue;
    300         }
    301         double t2 = i.fT[1][uIndex];
    302         xy_at_t(q2, t2, ends[0].x, ends[0].y);
    303         xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y);
    304         midT = (t2 + i.fCoincidentT[1][closest2]) / 2;
    305         xy_at_t(q2, midT, mid.x, mid.y);
    306         params.lineEndPoints(ends);
    307         midDist = params.pointDistance(mid);
    308         if (!approximately_zero(midDist)) {
    309             ++uIndex;
    310             continue;
    311         }
    312         // if both midpoints are close to the line, lengthen coincident span
    313         int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs
    314         if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1])) {
    315             i.fCoincidentT[0][closest1] = t1;
    316         }
    317         cEnd = closest2 ^ 1;
    318         if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2])) {
    319             i.fCoincidentT[0][closest2] = t2;
    320         }
    321         int remaining = --i.fUsed - uIndex;
    322         if (remaining > 0) {
    323             memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) * remaining);
    324             memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) * remaining);
    325         }
    326     }
    327     // if coincident data is subjectively a tiny span, replace it with a single point
    328     for (cIndex = 0; cIndex < i.fCoincidentUsed; ) {
    329         double start1 = i.fCoincidentT[0][cIndex];
    330         double end1 = i.fCoincidentT[0][cIndex + 1];
    331         _Line ends1;
    332         xy_at_t(q1, start1, ends1[0].x, ends1[0].y);
    333         xy_at_t(q1, end1, ends1[1].x, ends1[1].y);
    334         if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0].y, ends1[1].y)) {
    335             cIndex += 2;
    336             continue;
    337         }
    338         double start2 = i.fCoincidentT[1][cIndex];
    339         double end2 = i.fCoincidentT[1][cIndex + 1];
    340         _Line ends2;
    341         xy_at_t(q2, start2, ends2[0].x, ends2[0].y);
    342         xy_at_t(q2, end2, ends2[1].x, ends2[1].y);
    343         // again, approximately should be used with T values, not points FIXME
    344         if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0].y, ends2[1].y)) {
    345             cIndex += 2;
    346             continue;
    347         }
    348         if (approximately_less_than_zero(start1) || approximately_less_than_zero(end1)) {
    349             start1 = 0;
    350         } else if (approximately_greater_than_one(start1) || approximately_greater_than_one(end1)) {
    351             start1 = 1;
    352         } else {
    353             start1 = (start1 + end1) / 2;
    354         }
    355         if (approximately_less_than_zero(start2) || approximately_less_than_zero(end2)) {
    356             start2 = 0;
    357         } else if (approximately_greater_than_one(start2) || approximately_greater_than_one(end2)) {
    358             start2 = 1;
    359         } else {
    360             start2 = (start2 + end2) / 2;
    361         }
    362         i.insert(start1, start2);
    363         i.fCoincidentUsed -= 2;
    364         int remaining = i.fCoincidentUsed - cIndex;
    365         if (remaining > 0) {
    366             memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2], sizeof(i.fCoincidentT[0][0]) * remaining);
    367             memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2], sizeof(i.fCoincidentT[1][0]) * remaining);
    368         }
    369     }
    370 }
    371 
    372 bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
    373     if (implicit_matches(q1, q2)) {
    374         // FIXME: compute T values
    375         // compute the intersections of the ends to find the coincident span
    376         bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y);
    377         double t;
    378         if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) {
    379             i.addCoincident(t, 0);
    380         }
    381         if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
    382             i.addCoincident(t, 1);
    383         }
    384         useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
    385         if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
    386             i.addCoincident(0, t);
    387         }
    388         if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
    389             i.addCoincident(1, t);
    390         }
    391         SkASSERT(i.fCoincidentUsed <= 2);
    392         return i.fCoincidentUsed > 0;
    393     }
    394     QuadraticIntersections q(q1, q2, i);
    395     bool result = q.intersect();
    396     // FIXME: partial coincidence detection is currently poor. For now, try
    397     // to fix up the data after the fact. In the future, revisit the error
    398     // term to try to avoid this kind of result in the first place.
    399     if (i.fUsed && i.fCoincidentUsed) {
    400         hackToFixPartialCoincidence(q1, q2, i);
    401     }
    402     return result;
    403 }
    404