LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pathops - SkDQuadLineIntersection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 241 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 28 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "SkPathOpsCurve.h"
       9             : #include "SkPathOpsLine.h"
      10             : #include "SkPathOpsQuad.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             : class LineQuadraticIntersections {
      91             : public:
      92             :     enum PinTPoint {
      93             :         kPointUninitialized,
      94             :         kPointInitialized
      95             :     };
      96             : 
      97           0 :     LineQuadraticIntersections(const SkDQuad& q, const SkDLine& l, SkIntersections* i)
      98           0 :         : fQuad(q)
      99             :         , fLine(&l)
     100             :         , fIntersections(i)
     101           0 :         , fAllowNear(true) {
     102           0 :         i->setMax(5);  // allow short partial coincidence plus discrete intersections
     103           0 :     }
     104             : 
     105           0 :     LineQuadraticIntersections(const SkDQuad& q)
     106           0 :         : fQuad(q)
     107             :         SkDEBUGPARAMS(fLine(nullptr))
     108             :         SkDEBUGPARAMS(fIntersections(nullptr))
     109           0 :         SkDEBUGPARAMS(fAllowNear(false)) {
     110           0 :     }
     111             : 
     112           0 :     void allowNear(bool allow) {
     113           0 :         fAllowNear = allow;
     114           0 :     }
     115             : 
     116           0 :     void checkCoincident() {
     117           0 :         int last = fIntersections->used() - 1;
     118           0 :         for (int index = 0; index < last; ) {
     119           0 :             double quadMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2;
     120           0 :             SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
     121           0 :             double t = fLine->nearPoint(quadMidPt, nullptr);
     122           0 :             if (t < 0) {
     123           0 :                 ++index;
     124           0 :                 continue;
     125             :             }
     126           0 :             if (fIntersections->isCoincident(index)) {
     127           0 :                 fIntersections->removeOne(index);
     128           0 :                 --last;
     129           0 :             } else if (fIntersections->isCoincident(index + 1)) {
     130           0 :                 fIntersections->removeOne(index + 1);
     131           0 :                 --last;
     132             :             } else {
     133           0 :                 fIntersections->setCoincident(index++);
     134             :             }
     135           0 :             fIntersections->setCoincident(index);
     136             :         }
     137           0 :     }
     138             : 
     139           0 :     int intersectRay(double roots[2]) {
     140             :     /*
     141             :         solve by rotating line+quad so line is horizontal, then finding the roots
     142             :         set up matrix to rotate quad to x-axis
     143             :         |cos(a) -sin(a)|
     144             :         |sin(a)  cos(a)|
     145             :         note that cos(a) = A(djacent) / Hypoteneuse
     146             :                   sin(a) = O(pposite) / Hypoteneuse
     147             :         since we are computing Ts, we can ignore hypoteneuse, the scale factor:
     148             :         |  A     -O    |
     149             :         |  O      A    |
     150             :         A = line[1].fX - line[0].fX (adjacent side of the right triangle)
     151             :         O = line[1].fY - line[0].fY (opposite side of the right triangle)
     152             :         for each of the three points (e.g. n = 0 to 2)
     153             :         quad[n].fY' = (quad[n].fY - line[0].fY) * A - (quad[n].fX - line[0].fX) * O
     154             :     */
     155           0 :         double adj = (*fLine)[1].fX - (*fLine)[0].fX;
     156           0 :         double opp = (*fLine)[1].fY - (*fLine)[0].fY;
     157             :         double r[3];
     158           0 :         for (int n = 0; n < 3; ++n) {
     159           0 :             r[n] = (fQuad[n].fY - (*fLine)[0].fY) * adj - (fQuad[n].fX - (*fLine)[0].fX) * opp;
     160             :         }
     161           0 :         double A = r[2];
     162           0 :         double B = r[1];
     163           0 :         double C = r[0];
     164           0 :         A += C - 2 * B;  // A = a - 2*b + c
     165           0 :         B -= C;  // B = -(b - c)
     166           0 :         return SkDQuad::RootsValidT(A, 2 * B, C, roots);
     167             :     }
     168             : 
     169           0 :     int intersect() {
     170           0 :         addExactEndPoints();
     171           0 :         if (fAllowNear) {
     172           0 :             addNearEndPoints();
     173             :         }
     174             :         double rootVals[2];
     175           0 :         int roots = intersectRay(rootVals);
     176           0 :         for (int index = 0; index < roots; ++index) {
     177           0 :             double quadT = rootVals[index];
     178           0 :             double lineT = findLineT(quadT);
     179             :             SkDPoint pt;
     180           0 :             if (pinTs(&quadT, &lineT, &pt, kPointUninitialized) && uniqueAnswer(quadT, pt)) {
     181           0 :                 fIntersections->insert(quadT, lineT, pt);
     182             :             }
     183             :         }
     184           0 :         checkCoincident();
     185           0 :         return fIntersections->used();
     186             :     }
     187             : 
     188           0 :     int horizontalIntersect(double axisIntercept, double roots[2]) {
     189           0 :         double D = fQuad[2].fY;  // f
     190           0 :         double E = fQuad[1].fY;  // e
     191           0 :         double F = fQuad[0].fY;  // d
     192           0 :         D += F - 2 * E;         // D = d - 2*e + f
     193           0 :         E -= F;                 // E = -(d - e)
     194           0 :         F -= axisIntercept;
     195           0 :         return SkDQuad::RootsValidT(D, 2 * E, F, roots);
     196             :     }
     197             : 
     198           0 :     int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
     199           0 :         addExactHorizontalEndPoints(left, right, axisIntercept);
     200           0 :         if (fAllowNear) {
     201           0 :             addNearHorizontalEndPoints(left, right, axisIntercept);
     202             :         }
     203             :         double rootVals[2];
     204           0 :         int roots = horizontalIntersect(axisIntercept, rootVals);
     205           0 :         for (int index = 0; index < roots; ++index) {
     206           0 :             double quadT = rootVals[index];
     207           0 :             SkDPoint pt = fQuad.ptAtT(quadT);
     208           0 :             double lineT = (pt.fX - left) / (right - left);
     209           0 :             if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
     210           0 :                 fIntersections->insert(quadT, lineT, pt);
     211             :             }
     212             :         }
     213           0 :         if (flipped) {
     214           0 :             fIntersections->flip();
     215             :         }
     216           0 :         checkCoincident();
     217           0 :         return fIntersections->used();
     218             :     }
     219             : 
     220           0 :     bool uniqueAnswer(double quadT, const SkDPoint& pt) {
     221           0 :         for (int inner = 0; inner < fIntersections->used(); ++inner) {
     222           0 :             if (fIntersections->pt(inner) != pt) {
     223           0 :                 continue;
     224             :             }
     225           0 :             double existingQuadT = (*fIntersections)[0][inner];
     226           0 :             if (quadT == existingQuadT) {
     227           0 :                 return false;
     228             :             }
     229             :             // check if midway on quad is also same point. If so, discard this
     230           0 :             double quadMidT = (existingQuadT + quadT) / 2;
     231           0 :             SkDPoint quadMidPt = fQuad.ptAtT(quadMidT);
     232           0 :             if (quadMidPt.approximatelyEqual(pt)) {
     233           0 :                 return false;
     234             :             }
     235             :         }
     236             : #if ONE_OFF_DEBUG
     237             :         SkDPoint qPt = fQuad.ptAtT(quadT);
     238             :         SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
     239             :                 qPt.fX, qPt.fY);
     240             : #endif
     241           0 :         return true;
     242             :     }
     243             : 
     244           0 :     int verticalIntersect(double axisIntercept, double roots[2]) {
     245           0 :         double D = fQuad[2].fX;  // f
     246           0 :         double E = fQuad[1].fX;  // e
     247           0 :         double F = fQuad[0].fX;  // d
     248           0 :         D += F - 2 * E;         // D = d - 2*e + f
     249           0 :         E -= F;                 // E = -(d - e)
     250           0 :         F -= axisIntercept;
     251           0 :         return SkDQuad::RootsValidT(D, 2 * E, F, roots);
     252             :     }
     253             : 
     254           0 :     int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
     255           0 :         addExactVerticalEndPoints(top, bottom, axisIntercept);
     256           0 :         if (fAllowNear) {
     257           0 :             addNearVerticalEndPoints(top, bottom, axisIntercept);
     258             :         }
     259             :         double rootVals[2];
     260           0 :         int roots = verticalIntersect(axisIntercept, rootVals);
     261           0 :         for (int index = 0; index < roots; ++index) {
     262           0 :             double quadT = rootVals[index];
     263           0 :             SkDPoint pt = fQuad.ptAtT(quadT);
     264           0 :             double lineT = (pt.fY - top) / (bottom - top);
     265           0 :             if (pinTs(&quadT, &lineT, &pt, kPointInitialized) && uniqueAnswer(quadT, pt)) {
     266           0 :                 fIntersections->insert(quadT, lineT, pt);
     267             :             }
     268             :         }
     269           0 :         if (flipped) {
     270           0 :             fIntersections->flip();
     271             :         }
     272           0 :         checkCoincident();
     273           0 :         return fIntersections->used();
     274             :     }
     275             : 
     276             : protected:
     277             :     // add endpoints first to get zero and one t values exactly
     278           0 :     void addExactEndPoints() {
     279           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     280           0 :             double lineT = fLine->exactPoint(fQuad[qIndex]);
     281           0 :             if (lineT < 0) {
     282           0 :                 continue;
     283             :             }
     284           0 :             double quadT = (double) (qIndex >> 1);
     285           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     286             :         }
     287           0 :     }
     288             : 
     289           0 :     void addNearEndPoints() {
     290           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     291           0 :             double quadT = (double) (qIndex >> 1);
     292           0 :             if (fIntersections->hasT(quadT)) {
     293           0 :                 continue;
     294             :             }
     295           0 :             double lineT = fLine->nearPoint(fQuad[qIndex], nullptr);
     296           0 :             if (lineT < 0) {
     297           0 :                 continue;
     298             :             }
     299           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     300             :         }
     301           0 :         this->addLineNearEndPoints();
     302           0 :     }
     303             : 
     304           0 :     void addLineNearEndPoints() {
     305           0 :         for (int lIndex = 0; lIndex < 2; ++lIndex) {
     306           0 :             double lineT = (double) lIndex;
     307           0 :             if (fIntersections->hasOppT(lineT)) {
     308           0 :                 continue;
     309             :             }
     310           0 :             double quadT = ((SkDCurve*) &fQuad)->nearPoint(SkPath::kQuad_Verb,
     311           0 :                     (*fLine)[lIndex], (*fLine)[!lIndex]);
     312           0 :             if (quadT < 0) {
     313           0 :                 continue;
     314             :             }
     315           0 :             fIntersections->insert(quadT, lineT, (*fLine)[lIndex]);
     316             :         }
     317           0 :     }
     318             : 
     319           0 :     void addExactHorizontalEndPoints(double left, double right, double y) {
     320           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     321           0 :             double lineT = SkDLine::ExactPointH(fQuad[qIndex], left, right, y);
     322           0 :             if (lineT < 0) {
     323           0 :                 continue;
     324             :             }
     325           0 :             double quadT = (double) (qIndex >> 1);
     326           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     327             :         }
     328           0 :     }
     329             : 
     330           0 :     void addNearHorizontalEndPoints(double left, double right, double y) {
     331           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     332           0 :             double quadT = (double) (qIndex >> 1);
     333           0 :             if (fIntersections->hasT(quadT)) {
     334           0 :                 continue;
     335             :             }
     336           0 :             double lineT = SkDLine::NearPointH(fQuad[qIndex], left, right, y);
     337           0 :             if (lineT < 0) {
     338           0 :                 continue;
     339             :             }
     340           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     341             :         }
     342           0 :         this->addLineNearEndPoints();
     343           0 :     }
     344             : 
     345           0 :     void addExactVerticalEndPoints(double top, double bottom, double x) {
     346           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     347           0 :             double lineT = SkDLine::ExactPointV(fQuad[qIndex], top, bottom, x);
     348           0 :             if (lineT < 0) {
     349           0 :                 continue;
     350             :             }
     351           0 :             double quadT = (double) (qIndex >> 1);
     352           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     353             :         }
     354           0 :     }
     355             : 
     356           0 :     void addNearVerticalEndPoints(double top, double bottom, double x) {
     357           0 :         for (int qIndex = 0; qIndex < 3; qIndex += 2) {
     358           0 :             double quadT = (double) (qIndex >> 1);
     359           0 :             if (fIntersections->hasT(quadT)) {
     360           0 :                 continue;
     361             :             }
     362           0 :             double lineT = SkDLine::NearPointV(fQuad[qIndex], top, bottom, x);
     363           0 :             if (lineT < 0) {
     364           0 :                 continue;
     365             :             }
     366           0 :             fIntersections->insert(quadT, lineT, fQuad[qIndex]);
     367             :         }
     368           0 :         this->addLineNearEndPoints();
     369           0 :     }
     370             : 
     371           0 :     double findLineT(double t) {
     372           0 :         SkDPoint xy = fQuad.ptAtT(t);
     373           0 :         double dx = (*fLine)[1].fX - (*fLine)[0].fX;
     374           0 :         double dy = (*fLine)[1].fY - (*fLine)[0].fY;
     375           0 :         if (fabs(dx) > fabs(dy)) {
     376           0 :             return (xy.fX - (*fLine)[0].fX) / dx;
     377             :         }
     378           0 :         return (xy.fY - (*fLine)[0].fY) / dy;
     379             :     }
     380             : 
     381           0 :     bool pinTs(double* quadT, double* lineT, SkDPoint* pt, PinTPoint ptSet) {
     382           0 :         if (!approximately_one_or_less_double(*lineT)) {
     383           0 :             return false;
     384             :         }
     385           0 :         if (!approximately_zero_or_more_double(*lineT)) {
     386           0 :             return false;
     387             :         }
     388           0 :         double qT = *quadT = SkPinT(*quadT);
     389           0 :         double lT = *lineT = SkPinT(*lineT);
     390           0 :         if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) {
     391           0 :             *pt = (*fLine).ptAtT(lT);
     392           0 :         } else if (ptSet == kPointUninitialized) {
     393           0 :             *pt = fQuad.ptAtT(qT);
     394             :         }
     395           0 :         SkPoint gridPt = pt->asSkPoint();
     396           0 :         if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) {
     397           0 :             *pt = (*fLine)[0];
     398           0 :             *lineT = 0;
     399           0 :         } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())) {
     400           0 :             *pt = (*fLine)[1];
     401           0 :             *lineT = 1;
     402             :         }
     403           0 :         if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) {
     404           0 :             return false;
     405             :         }
     406           0 :         if (gridPt == fQuad[0].asSkPoint()) {
     407           0 :             *pt = fQuad[0];
     408           0 :             *quadT = 0;
     409           0 :         } else if (gridPt == fQuad[2].asSkPoint()) {
     410           0 :             *pt = fQuad[2];
     411           0 :             *quadT = 1;
     412             :         }
     413           0 :         return true;
     414             :     }
     415             : 
     416             : private:
     417             :     const SkDQuad& fQuad;
     418             :     const SkDLine* fLine;
     419             :     SkIntersections* fIntersections;
     420             :     bool fAllowNear;
     421             : };
     422             : 
     423           0 : int SkIntersections::horizontal(const SkDQuad& quad, double left, double right, double y,
     424             :                                 bool flipped) {
     425           0 :     SkDLine line = {{{ left, y }, { right, y }}};
     426           0 :     LineQuadraticIntersections q(quad, line, this);
     427           0 :     return q.horizontalIntersect(y, left, right, flipped);
     428             : }
     429             : 
     430           0 : int SkIntersections::vertical(const SkDQuad& quad, double top, double bottom, double x,
     431             :                               bool flipped) {
     432           0 :     SkDLine line = {{{ x, top }, { x, bottom }}};
     433           0 :     LineQuadraticIntersections q(quad, line, this);
     434           0 :     return q.verticalIntersect(x, top, bottom, flipped);
     435             : }
     436             : 
     437           0 : int SkIntersections::intersect(const SkDQuad& quad, const SkDLine& line) {
     438           0 :     LineQuadraticIntersections q(quad, line, this);
     439           0 :     q.allowNear(fAllowNear);
     440           0 :     return q.intersect();
     441             : }
     442             : 
     443           0 : int SkIntersections::intersectRay(const SkDQuad& quad, const SkDLine& line) {
     444           0 :     LineQuadraticIntersections q(quad, line, this);
     445           0 :     fUsed = q.intersectRay(fT[0]);
     446           0 :     for (int index = 0; index < fUsed; ++index) {
     447           0 :         fPt[index] = quad.ptAtT(fT[0][index]);
     448             :     }
     449           0 :     return fUsed;
     450             : }
     451             : 
     452           0 : int SkIntersections::HorizontalIntercept(const SkDQuad& quad, SkScalar y, double* roots) {
     453           0 :     LineQuadraticIntersections q(quad);
     454           0 :     return q.horizontalIntersect(y, roots);
     455             : }
     456             : 
     457           0 : int SkIntersections::VerticalIntercept(const SkDQuad& quad, SkScalar x, double* roots) {
     458           0 :     LineQuadraticIntersections q(quad);
     459           0 :     return q.verticalIntersect(x, roots);
     460             : }
     461             : 
     462             : // SkDQuad accessors to Intersection utilities
     463             : 
     464           0 : int SkDQuad::horizontalIntersect(double yIntercept, double roots[2]) const {
     465           0 :     return SkIntersections::HorizontalIntercept(*this, yIntercept, roots);
     466             : }
     467             : 
     468           0 : int SkDQuad::verticalIntersect(double xIntercept, double roots[2]) const {
     469           0 :     return SkIntersections::VerticalIntercept(*this, xIntercept, roots);
     470             : }

Generated by: LCOV version 1.13