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