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 "LineUtilities.h"
     10 #include "QuadraticUtilities.h"
     11 
     12 /*
     13 Find the interection of a line and quadratic by solving for valid t values.
     14 
     15 From http://stackoverflow.com/questions/1853637/how-to-find-the-mathematical-function-defining-a-bezier-curve
     16 
     17 "A Bezier curve is a parametric function. A quadratic Bezier curve (i.e. three
     18 control points) can be expressed as: F(t) = A(1 - t)^2 + B(1 - t)t + Ct^2 where
     19 A, B and C are points and t goes from zero to one.
     20 
     21 This will give you two equations:
     22 
     23   x = a(1 - t)^2 + b(1 - t)t + ct^2
     24   y = d(1 - t)^2 + e(1 - t)t + ft^2
     25 
     26 If you add for instance the line equation (y = kx + m) to that, you'll end up
     27 with three equations and three unknowns (x, y and t)."
     28 
     29 Similar to above, the quadratic is represented as
     30   x = a(1-t)^2 + 2b(1-t)t + ct^2
     31   y = d(1-t)^2 + 2e(1-t)t + ft^2
     32 and the line as
     33   y = g*x + h
     34 
     35 Using Mathematica, solve for the values of t where the quadratic intersects the
     36 line:
     37 
     38   (in)  t1 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - x,
     39                        d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - g*x - h, x]
     40   (out) -d + h + 2 d t - 2 e t - d t^2 + 2 e t^2 - f t^2 +
     41          g  (a - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2)
     42   (in)  Solve[t1 == 0, t]
     43   (out) {
     44     {t -> (-2 d + 2 e +   2 a g - 2 b g    -
     45       Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
     46           4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
     47          (2 (-d + 2 e - f + a g - 2 b g    + c g))
     48          },
     49     {t -> (-2 d + 2 e +   2 a g - 2 b g    +
     50       Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
     51           4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
     52          (2 (-d + 2 e - f + a g - 2 b g    + c g))
     53          }
     54         }
     55 
     56 Using the results above (when the line tends towards horizontal)
     57        A =   (-(d - 2*e + f) + g*(a - 2*b + c)     )
     58        B = 2*( (d -   e    ) - g*(a -   b    )     )
     59        C =   (-(d          ) + g*(a          ) + h )
     60 
     61 If g goes to infinity, we can rewrite the line in terms of x.
     62   x = g'*y + h'
     63 
     64 And solve accordingly in Mathematica:
     65 
     66   (in)  t2 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - g'*y - h',
     67                        d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - y, y]
     68   (out)  a - h' - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2 -
     69          g'  (d - 2 d t + 2 e t + d t^2 - 2 e t^2 + f t^2)
     70   (in)  Solve[t2 == 0, t]
     71   (out) {
     72     {t -> (2 a - 2 b -   2 d g' + 2 e g'    -
     73     Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
     74           4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')]) /
     75          (2 (a - 2 b + c - d g' + 2 e g' - f g'))
     76          },
     77     {t -> (2 a - 2 b -   2 d g' + 2 e g'    +
     78     Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
     79           4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')])/
     80          (2 (a - 2 b + c - d g' + 2 e g' - f g'))
     81          }
     82         }
     83 
     84 Thus, if the slope of the line tends towards vertical, we use:
     85        A =   ( (a - 2*b + c) - g'*(d  - 2*e + f)      )
     86        B = 2*(-(a -   b    ) + g'*(d  -   e    )      )
     87        C =   ( (a          ) - g'*(d           ) - h' )
     88  */
     89 
     90 
     91 class LineQuadraticIntersections {
     92 public:
     93 
     94 LineQuadraticIntersections(const Quadratic& q, const _Line& l, Intersections& i)
     95     : quad(q)
     96     , line(l)
     97     , intersections(i) {
     98 }
     99 
    100 int intersectRay(double roots[2]) {
    101 /*
    102     solve by rotating line+quad so line is horizontal, then finding the roots
    103     set up matrix to rotate quad to x-axis
    104     |cos(a) -sin(a)|
    105     |sin(a)  cos(a)|
    106     note that cos(a) = A(djacent) / Hypoteneuse
    107               sin(a) = O(pposite) / Hypoteneuse
    108     since we are computing Ts, we can ignore hypoteneuse, the scale factor:
    109     |  A     -O    |
    110     |  O      A    |
    111     A = line[1].x - line[0].x (adjacent side of the right triangle)
    112     O = line[1].y - line[0].y (opposite side of the right triangle)
    113     for each of the three points (e.g. n = 0 to 2)
    114     quad[n].y' = (quad[n].y - line[0].y) * A - (quad[n].x - line[0].x) * O
    115 */
    116     double adj = line[1].x - line[0].x;
    117     double opp = line[1].y - line[0].y;
    118     double r[3];
    119     for (int n = 0; n < 3; ++n) {
    120         r[n] = (quad[n].y - line[0].y) * adj - (quad[n].x - line[0].x) * opp;
    121     }
    122     double A = r[2];
    123     double B = r[1];
    124     double C = r[0];
    125     A += C - 2 * B; // A = a - 2*b + c
    126     B -= C; // B = -(b - c)
    127     return quadraticRootsValidT(A, 2 * B, C, roots);
    128 }
    129 
    130 int intersect() {
    131     addEndPoints();
    132     double rootVals[2];
    133     int roots = intersectRay(rootVals);
    134     for (int index = 0; index < roots; ++index) {
    135         double quadT = rootVals[index];
    136         double lineT = findLineT(quadT);
    137         if (pinTs(quadT, lineT)) {
    138             _Point pt;
    139             xy_at_t(line, lineT, pt.x, pt.y);
    140             intersections.insert(quadT, lineT, pt);
    141         }
    142     }
    143     return intersections.fUsed;
    144 }
    145 
    146 int horizontalIntersect(double axisIntercept, double roots[2]) {
    147     double D = quad[2].y; // f
    148     double E = quad[1].y; // e
    149     double F = quad[0].y; // d
    150     D += F - 2 * E; // D = d - 2*e + f
    151     E -= F; // E = -(d - e)
    152     F -= axisIntercept;
    153     return quadraticRootsValidT(D, 2 * E, F, roots);
    154 }
    155 
    156 int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
    157     addHorizontalEndPoints(left, right, axisIntercept);
    158     double rootVals[2];
    159     int roots = horizontalIntersect(axisIntercept, rootVals);
    160     for (int index = 0; index < roots; ++index) {
    161         _Point pt;
    162         double quadT = rootVals[index];
    163         xy_at_t(quad, quadT, pt.x, pt.y);
    164         double lineT = (pt.x - left) / (right - left);
    165         if (pinTs(quadT, lineT)) {
    166             intersections.insert(quadT, lineT, pt);
    167         }
    168     }
    169     if (flipped) {
    170         flip();
    171     }
    172     return intersections.fUsed;
    173 }
    174 
    175 int verticalIntersect(double axisIntercept, double roots[2]) {
    176     double D = quad[2].x; // f
    177     double E = quad[1].x; // e
    178     double F = quad[0].x; // d
    179     D += F - 2 * E; // D = d - 2*e + f
    180     E -= F; // E = -(d - e)
    181     F -= axisIntercept;
    182     return quadraticRootsValidT(D, 2 * E, F, roots);
    183 }
    184 
    185 int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
    186     addVerticalEndPoints(top, bottom, axisIntercept);
    187     double rootVals[2];
    188     int roots = verticalIntersect(axisIntercept, rootVals);
    189     for (int index = 0; index < roots; ++index) {
    190         _Point pt;
    191         double quadT = rootVals[index];
    192         xy_at_t(quad, quadT, pt.x, pt.y);
    193         double lineT = (pt.y - top) / (bottom - top);
    194         if (pinTs(quadT, lineT)) {
    195             intersections.insert(quadT, lineT, pt);
    196         }
    197     }
    198     if (flipped) {
    199         flip();
    200     }
    201     return intersections.fUsed;
    202 }
    203 
    204 protected:
    205 
    206 // add endpoints first to get zero and one t values exactly
    207 void addEndPoints()
    208 {
    209     for (int qIndex = 0; qIndex < 3; qIndex += 2) {
    210         for (int lIndex = 0; lIndex < 2; lIndex++) {
    211             if (quad[qIndex] == line[lIndex]) {
    212                 intersections.insert(qIndex >> 1, lIndex, line[lIndex]);
    213             }
    214         }
    215     }
    216 }
    217 
    218 void addHorizontalEndPoints(double left, double right, double y)
    219 {
    220     for (int qIndex = 0; qIndex < 3; qIndex += 2) {
    221         if (quad[qIndex].y != y) {
    222             continue;
    223         }
    224         if (quad[qIndex].x == left) {
    225             intersections.insert(qIndex >> 1, 0, quad[qIndex]);
    226         }
    227         if (quad[qIndex].x == right) {
    228             intersections.insert(qIndex >> 1, 1, quad[qIndex]);
    229         }
    230     }
    231 }
    232 
    233 void addVerticalEndPoints(double top, double bottom, double x)
    234 {
    235     for (int qIndex = 0; qIndex < 3; qIndex += 2) {
    236         if (quad[qIndex].x != x) {
    237             continue;
    238         }
    239         if (quad[qIndex].y == top) {
    240             intersections.insert(qIndex >> 1, 0, quad[qIndex]);
    241         }
    242         if (quad[qIndex].y == bottom) {
    243             intersections.insert(qIndex >> 1, 1, quad[qIndex]);
    244         }
    245     }
    246 }
    247 
    248 double findLineT(double t) {
    249     double x, y;
    250     xy_at_t(quad, t, x, y);
    251     double dx = line[1].x - line[0].x;
    252     double dy = line[1].y - line[0].y;
    253     if (fabs(dx) > fabs(dy)) {
    254         return (x - line[0].x) / dx;
    255     }
    256     return (y - line[0].y) / dy;
    257 }
    258 
    259 void flip() {
    260     // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
    261     int roots = intersections.fUsed;
    262     for (int index = 0; index < roots; ++index) {
    263         intersections.fT[1][index] = 1 - intersections.fT[1][index];
    264     }
    265 }
    266 
    267 static bool pinTs(double& quadT, double& lineT) {
    268     if (!approximately_one_or_less(lineT)) {
    269         return false;
    270     }
    271     if (!approximately_zero_or_more(lineT)) {
    272         return false;
    273     }
    274     if (precisely_less_than_zero(quadT)) {
    275         quadT = 0;
    276     } else if (precisely_greater_than_one(quadT)) {
    277         quadT = 1;
    278     }
    279     if (precisely_less_than_zero(lineT)) {
    280         lineT = 0;
    281     } else if (precisely_greater_than_one(lineT)) {
    282         lineT = 1;
    283     }
    284     return true;
    285 }
    286 
    287 private:
    288 
    289 const Quadratic& quad;
    290 const _Line& line;
    291 Intersections& intersections;
    292 };
    293 
    294 // utility for pairs of coincident quads
    295 static double horizontalIntersect(const Quadratic& quad, const _Point& pt) {
    296     LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
    297     double rootVals[2];
    298     int roots = q.horizontalIntersect(pt.y, rootVals);
    299     for (int index = 0; index < roots; ++index) {
    300         double x;
    301         double t = rootVals[index];
    302         xy_at_t(quad, t, x, *(double*) 0);
    303         if (AlmostEqualUlps(x, pt.x)) {
    304             return t;
    305         }
    306     }
    307     return -1;
    308 }
    309 
    310 static double verticalIntersect(const Quadratic& quad, const _Point& pt) {
    311     LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
    312     double rootVals[2];
    313     int roots = q.verticalIntersect(pt.x, rootVals);
    314     for (int index = 0; index < roots; ++index) {
    315         double y;
    316         double t = rootVals[index];
    317         xy_at_t(quad, t, *(double*) 0, y);
    318         if (AlmostEqualUlps(y, pt.y)) {
    319             return t;
    320         }
    321     }
    322     return -1;
    323 }
    324 
    325 double axialIntersect(const Quadratic& q1, const _Point& p, bool vertical) {
    326     if (vertical) {
    327         return verticalIntersect(q1, p);
    328     }
    329     return horizontalIntersect(q1, p);
    330 }
    331 
    332 int horizontalIntersect(const Quadratic& quad, double left, double right,
    333         double y, double tRange[2]) {
    334     LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
    335     double rootVals[2];
    336     int result = q.horizontalIntersect(y, rootVals);
    337     int tCount = 0;
    338     for (int index = 0; index < result; ++index) {
    339         double x, y;
    340         xy_at_t(quad, rootVals[index], x, y);
    341         if (x < left || x > right) {
    342             continue;
    343         }
    344         tRange[tCount++] = rootVals[index];
    345     }
    346     return tCount;
    347 }
    348 
    349 int horizontalIntersect(const Quadratic& quad, double left, double right, double y,
    350         bool flipped, Intersections& intersections) {
    351     LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
    352     return q.horizontalIntersect(y, left, right, flipped);
    353 }
    354 
    355 int verticalIntersect(const Quadratic& quad, double top, double bottom, double x,
    356         bool flipped, Intersections& intersections) {
    357     LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
    358     return q.verticalIntersect(x, top, bottom, flipped);
    359 }
    360 
    361 int intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
    362     LineQuadraticIntersections q(quad, line, i);
    363     return q.intersect();
    364 }
    365 
    366 int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i) {
    367     LineQuadraticIntersections q(quad, line, i);
    368     return q.intersectRay(i.fT[0]);
    369 }
    370