Home | History | Annotate | Download | only in pathops
      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 "SkIntersections.h"
      8 #include "SkPathOpsLine.h"
      9 
     10 /* Determine the intersection point of two lines. This assumes the lines are not parallel,
     11    and that that the lines are infinite.
     12    From http://en.wikipedia.org/wiki/Line-line_intersection
     13  */
     14 SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
     15     double axLen = a[1].fX - a[0].fX;
     16     double ayLen = a[1].fY - a[0].fY;
     17     double bxLen = b[1].fX - b[0].fX;
     18     double byLen = b[1].fY - b[0].fY;
     19     double denom = byLen * axLen - ayLen * bxLen;
     20     SkASSERT(denom);
     21     double term1 = a[1].fX * a[0].fY - a[1].fY * a[0].fX;
     22     double term2 = b[1].fX * b[0].fY - b[1].fY * b[0].fX;
     23     SkDPoint p;
     24     p.fX = (term1 * bxLen - axLen * term2) / denom;
     25     p.fY = (term1 * byLen - ayLen * term2) / denom;
     26     return p;
     27 }
     28 
     29 void SkIntersections::cleanUpCoincidence() {
     30     SkASSERT(fUsed == 2);
     31     // both t values are good
     32     bool startMatch = fT[0][0] == 0 && (fT[1][0] == 0 || fT[1][0] == 1);
     33     bool endMatch = fT[0][1] == 1 && (fT[1][1] == 0 || fT[1][1] == 1);
     34     if (startMatch || endMatch) {
     35         removeOne(startMatch);
     36         return;
     37     }
     38     // either t value is good
     39     bool pStartMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
     40     bool pEndMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
     41     removeOne(pStartMatch || !pEndMatch);
     42 }
     43 
     44 void SkIntersections::cleanUpParallelLines(bool parallel) {
     45     while (fUsed > 2) {
     46         removeOne(1);
     47     }
     48     if (fUsed == 2 && !parallel) {
     49         bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
     50         bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
     51         if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) {
     52             SkASSERT(startMatch || endMatch);
     53             removeOne(endMatch);
     54         }
     55     }
     56 }
     57 
     58 void SkIntersections::computePoints(const SkDLine& line, int used) {
     59     fPt[0] = line.ptAtT(fT[0][0]);
     60     if ((fUsed = used) == 2) {
     61         fPt[1] = line.ptAtT(fT[0][1]);
     62     }
     63 }
     64 
     65 int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
     66     fMax = 2;
     67     SkDVector aLen = a[1] - a[0];
     68     SkDVector bLen = b[1] - b[0];
     69     /* Slopes match when denom goes to zero:
     70                       axLen / ayLen ==                   bxLen / byLen
     71     (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
     72              byLen  * axLen         ==  ayLen          * bxLen
     73              byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
     74      */
     75     double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX;
     76     SkDVector ab0 = a[0] - b[0];
     77     double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
     78     double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
     79 #if 0
     80     if (!between(0, numerA, denom) || !between(0, numerB, denom)) {
     81         fUsed = 0;
     82         return 0;
     83     }
     84 #endif
     85     numerA /= denom;
     86     numerB /= denom;
     87     int used;
     88     if (!approximately_zero(denom)) {
     89         fT[0][0] = numerA;
     90         fT[1][0] = numerB;
     91         used = 1;
     92     } else {
     93        /* See if the axis intercepts match:
     94                   ay - ax * ayLen / axLen  ==          by - bx * ayLen / axLen
     95          axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
     96          axLen *  ay - ax * ayLen          == axLen *  by - bx * ayLen
     97         */
     98         if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX,
     99                 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) {
    100             return fUsed = 0;
    101         }
    102         // there's no great answer for intersection points for coincident rays, but return something
    103         fT[0][0] = fT[1][0] = 0;
    104         fT[1][0] = fT[1][1] = 1;
    105         used = 2;
    106     }
    107     computePoints(a, used);
    108     return fUsed;
    109 }
    110 
    111 // note that this only works if both lines are neither horizontal nor vertical
    112 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
    113     fMax = 3;  // note that we clean up so that there is no more than two in the end
    114     // see if end points intersect the opposite line
    115     double t;
    116     for (int iA = 0; iA < 2; ++iA) {
    117         if ((t = b.exactPoint(a[iA])) >= 0) {
    118             insert(iA, t, a[iA]);
    119         }
    120     }
    121     for (int iB = 0; iB < 2; ++iB) {
    122         if ((t = a.exactPoint(b[iB])) >= 0) {
    123             insert(t, iB, b[iB]);
    124         }
    125     }
    126     /* Determine the intersection point of two line segments
    127        Return FALSE if the lines don't intersect
    128        from: http://paulbourke.net/geometry/lineline2d/ */
    129     double axLen = a[1].fX - a[0].fX;
    130     double ayLen = a[1].fY - a[0].fY;
    131     double bxLen = b[1].fX - b[0].fX;
    132     double byLen = b[1].fY - b[0].fY;
    133     /* Slopes match when denom goes to zero:
    134                       axLen / ayLen ==                   bxLen / byLen
    135     (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
    136              byLen  * axLen         ==  ayLen          * bxLen
    137              byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
    138      */
    139     double axByLen = axLen * byLen;
    140     double ayBxLen = ayLen * bxLen;
    141     // detect parallel lines the same way here and in SkOpAngle operator <
    142     // so that non-parallel means they are also sortable
    143     bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen)
    144             : NotAlmostDequalUlps(axByLen, ayBxLen);
    145     if (unparallel && fUsed == 0) {
    146         double ab0y = a[0].fY - b[0].fY;
    147         double ab0x = a[0].fX - b[0].fX;
    148         double numerA = ab0y * bxLen - byLen * ab0x;
    149         double numerB = ab0y * axLen - ayLen * ab0x;
    150         double denom = axByLen - ayBxLen;
    151         if (between(0, numerA, denom) && between(0, numerB, denom)) {
    152             fT[0][0] = numerA / denom;
    153             fT[1][0] = numerB / denom;
    154             computePoints(a, 1);
    155         }
    156     }
    157 /* Allow tracking that both sets of end points are near each other -- the lines are entirely
    158    coincident -- even when the end points are not exactly the same.
    159    Mark this as a 'wild card' for the end points, so that either point is considered totally
    160    coincident. Then, avoid folding the lines over each other, but allow either end to mate
    161    to the next set of lines.
    162  */
    163     if (fAllowNear || !unparallel) {
    164         double aNearB[2];
    165         double bNearA[2];
    166         bool aNotB[2] = {false, false};
    167         bool bNotA[2] = {false, false};
    168         int nearCount = 0;
    169         for (int index = 0; index < 2; ++index) {
    170             aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]);
    171             nearCount += t >= 0;
    172             bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]);
    173             nearCount += t >= 0;
    174         }
    175         if (nearCount > 0) {
    176             for (int iA = 0; iA < 2; ++iA) {
    177                 if (!aNotB[iA]) {
    178                     continue;
    179                 }
    180                 int nearer = aNearB[iA] > 0.5;
    181                 if (!bNotA[nearer]) {
    182                     continue;
    183                 }
    184                 SkASSERT(a[iA] != b[nearer]);
    185                 SkASSERT(iA == (bNearA[nearer] > 0.5));
    186                 fNearlySame[iA] = true;
    187                 insertNear(iA, nearer, a[iA], b[nearer]);
    188                 aNearB[iA] = -1;
    189                 bNearA[nearer] = -1;
    190                 nearCount -= 2;
    191             }
    192             if (nearCount > 0) {
    193                 for (int iA = 0; iA < 2; ++iA) {
    194                     if (aNearB[iA] >= 0) {
    195                         insert(iA, aNearB[iA], a[iA]);
    196                     }
    197                 }
    198                 for (int iB = 0; iB < 2; ++iB) {
    199                     if (bNearA[iB] >= 0) {
    200                         insert(bNearA[iB], iB, b[iB]);
    201                     }
    202                 }
    203             }
    204         }
    205     }
    206     cleanUpParallelLines(!unparallel);
    207     SkASSERT(fUsed <= 2);
    208     return fUsed;
    209 }
    210 
    211 static int horizontal_coincident(const SkDLine& line, double y) {
    212     double min = line[0].fY;
    213     double max = line[1].fY;
    214     if (min > max) {
    215         SkTSwap(min, max);
    216     }
    217     if (min > y || max < y) {
    218         return 0;
    219     }
    220     if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) {
    221         return 2;
    222     }
    223     return 1;
    224 }
    225 
    226 static double horizontal_intercept(const SkDLine& line, double y) {
    227      return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY));
    228 }
    229 
    230 int SkIntersections::horizontal(const SkDLine& line, double y) {
    231     fMax = 2;
    232     int horizontalType = horizontal_coincident(line, y);
    233     if (horizontalType == 1) {
    234         fT[0][0] = horizontal_intercept(line, y);
    235     } else if (horizontalType == 2) {
    236         fT[0][0] = 0;
    237         fT[0][1] = 1;
    238     }
    239     return fUsed = horizontalType;
    240 }
    241 
    242 int SkIntersections::horizontal(const SkDLine& line, double left, double right,
    243                                 double y, bool flipped) {
    244     fMax = 3;  // clean up parallel at the end will limit the result to 2 at the most
    245     // see if end points intersect the opposite line
    246     double t;
    247     const SkDPoint leftPt = { left, y };
    248     if ((t = line.exactPoint(leftPt)) >= 0) {
    249         insert(t, (double) flipped, leftPt);
    250     }
    251     if (left != right) {
    252         const SkDPoint rightPt = { right, y };
    253         if ((t = line.exactPoint(rightPt)) >= 0) {
    254             insert(t, (double) !flipped, rightPt);
    255         }
    256         for (int index = 0; index < 2; ++index) {
    257             if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
    258                 insert((double) index, flipped ? 1 - t : t, line[index]);
    259             }
    260         }
    261     }
    262     int result = horizontal_coincident(line, y);
    263     if (result == 1 && fUsed == 0) {
    264         fT[0][0] = horizontal_intercept(line, y);
    265         double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
    266         if (between(left, xIntercept, right)) {
    267             fT[1][0] = (xIntercept - left) / (right - left);
    268             if (flipped) {
    269                 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
    270                 for (int index = 0; index < result; ++index) {
    271                     fT[1][index] = 1 - fT[1][index];
    272                 }
    273             }
    274             fPt[0].fX = xIntercept;
    275             fPt[0].fY = y;
    276             fUsed = 1;
    277         }
    278     }
    279     if (fAllowNear || result == 2) {
    280         if ((t = line.nearPoint(leftPt, NULL)) >= 0) {
    281             insert(t, (double) flipped, leftPt);
    282         }
    283         if (left != right) {
    284             const SkDPoint rightPt = { right, y };
    285             if ((t = line.nearPoint(rightPt, NULL)) >= 0) {
    286                 insert(t, (double) !flipped, rightPt);
    287             }
    288             for (int index = 0; index < 2; ++index) {
    289                 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
    290                     insert((double) index, flipped ? 1 - t : t, line[index]);
    291                 }
    292             }
    293         }
    294     }
    295     cleanUpParallelLines(result == 2);
    296     return fUsed;
    297 }
    298 
    299 static int vertical_coincident(const SkDLine& line, double x) {
    300     double min = line[0].fX;
    301     double max = line[1].fX;
    302     if (min > max) {
    303         SkTSwap(min, max);
    304     }
    305     if (!precisely_between(min, x, max)) {
    306         return 0;
    307     }
    308     if (AlmostEqualUlps(min, max)) {
    309         return 2;
    310     }
    311     return 1;
    312 }
    313 
    314 static double vertical_intercept(const SkDLine& line, double x) {
    315     return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX));
    316 }
    317 
    318 int SkIntersections::vertical(const SkDLine& line, double x) {
    319     fMax = 2;
    320     int verticalType = vertical_coincident(line, x);
    321     if (verticalType == 1) {
    322         fT[0][0] = vertical_intercept(line, x);
    323     } else if (verticalType == 2) {
    324         fT[0][0] = 0;
    325         fT[0][1] = 1;
    326     }
    327     return fUsed = verticalType;
    328 }
    329 
    330 int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
    331                               double x, bool flipped) {
    332     fMax = 3;  // cleanup parallel lines will bring this back line
    333     // see if end points intersect the opposite line
    334     double t;
    335     SkDPoint topPt = { x, top };
    336     if ((t = line.exactPoint(topPt)) >= 0) {
    337         insert(t, (double) flipped, topPt);
    338     }
    339     if (top != bottom) {
    340         SkDPoint bottomPt = { x, bottom };
    341         if ((t = line.exactPoint(bottomPt)) >= 0) {
    342             insert(t, (double) !flipped, bottomPt);
    343         }
    344         for (int index = 0; index < 2; ++index) {
    345             if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
    346                 insert((double) index, flipped ? 1 - t : t, line[index]);
    347             }
    348         }
    349     }
    350     int result = vertical_coincident(line, x);
    351     if (result == 1 && fUsed == 0) {
    352         fT[0][0] = vertical_intercept(line, x);
    353         double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
    354         if (between(top, yIntercept, bottom)) {
    355             fT[1][0] = (yIntercept - top) / (bottom - top);
    356             if (flipped) {
    357                 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
    358                 for (int index = 0; index < result; ++index) {
    359                     fT[1][index] = 1 - fT[1][index];
    360                 }
    361             }
    362             fPt[0].fX = x;
    363             fPt[0].fY = yIntercept;
    364             fUsed = 1;
    365         }
    366     }
    367     if (fAllowNear || result == 2) {
    368         if ((t = line.nearPoint(topPt, NULL)) >= 0) {
    369             insert(t, (double) flipped, topPt);
    370         }
    371         if (top != bottom) {
    372             SkDPoint bottomPt = { x, bottom };
    373             if ((t = line.nearPoint(bottomPt, NULL)) >= 0) {
    374                 insert(t, (double) !flipped, bottomPt);
    375             }
    376             for (int index = 0; index < 2; ++index) {
    377                 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
    378                     insert((double) index, flipped ? 1 - t : t, line[index]);
    379                 }
    380             }
    381         }
    382     }
    383     cleanUpParallelLines(result == 2);
    384     SkASSERT(fUsed <= 2);
    385     return fUsed;
    386 }
    387 
    388 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
    389 // 4 subs, 2 muls, 1 cmp
    390 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
    391     return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
    392 }
    393 
    394 // 16 subs, 8 muls, 6 cmps
    395 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
    396     return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
    397             && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
    398 }
    399