1 /* 2 * Copyright 2015 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 "SkPathOpsConic.h" 9 #include "SkPathOpsCurve.h" 10 #include "SkPathOpsLine.h" 11 12 class LineConicIntersections { 13 public: 14 enum PinTPoint { 15 kPointUninitialized, 16 kPointInitialized 17 }; 18 19 LineConicIntersections(const SkDConic& c, const SkDLine& l, SkIntersections* i) 20 : fConic(c) 21 , fLine(&l) 22 , fIntersections(i) 23 , fAllowNear(true) { 24 i->setMax(4); // allow short partial coincidence plus discrete intersection 25 } 26 27 LineConicIntersections(const SkDConic& c) 28 : fConic(c) 29 SkDEBUGPARAMS(fLine(nullptr)) 30 SkDEBUGPARAMS(fIntersections(nullptr)) 31 SkDEBUGPARAMS(fAllowNear(false)) { 32 } 33 34 void allowNear(bool allow) { 35 fAllowNear = allow; 36 } 37 38 void checkCoincident() { 39 int last = fIntersections->used() - 1; 40 for (int index = 0; index < last; ) { 41 double conicMidT = ((*fIntersections)[0][index] + (*fIntersections)[0][index + 1]) / 2; 42 SkDPoint conicMidPt = fConic.ptAtT(conicMidT); 43 double t = fLine->nearPoint(conicMidPt, nullptr); 44 if (t < 0) { 45 ++index; 46 continue; 47 } 48 if (fIntersections->isCoincident(index)) { 49 fIntersections->removeOne(index); 50 --last; 51 } else if (fIntersections->isCoincident(index + 1)) { 52 fIntersections->removeOne(index + 1); 53 --last; 54 } else { 55 fIntersections->setCoincident(index++); 56 } 57 fIntersections->setCoincident(index); 58 } 59 } 60 61 #ifdef SK_DEBUG 62 static bool close_to(double a, double b, const double c[3]) { 63 double max = SkTMax(-SkTMin(SkTMin(c[0], c[1]), c[2]), SkTMax(SkTMax(c[0], c[1]), c[2])); 64 return approximately_zero_when_compared_to(a - b, max); 65 } 66 #endif 67 int horizontalIntersect(double axisIntercept, double roots[2]) { 68 double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY }; 69 return this->validT(conicVals, axisIntercept, roots); 70 } 71 72 int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) { 73 this->addExactHorizontalEndPoints(left, right, axisIntercept); 74 if (fAllowNear) { 75 this->addNearHorizontalEndPoints(left, right, axisIntercept); 76 } 77 double roots[2]; 78 int count = this->horizontalIntersect(axisIntercept, roots); 79 for (int index = 0; index < count; ++index) { 80 double conicT = roots[index]; 81 SkDPoint pt = fConic.ptAtT(conicT); 82 SkDEBUGCODE(double conicVals[] = { fConic[0].fY, fConic[1].fY, fConic[2].fY }); 83 SkOPOBJASSERT(fIntersections, close_to(pt.fY, axisIntercept, conicVals)); 84 double lineT = (pt.fX - left) / (right - left); 85 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) 86 && this->uniqueAnswer(conicT, pt)) { 87 fIntersections->insert(conicT, lineT, pt); 88 } 89 } 90 if (flipped) { 91 fIntersections->flip(); 92 } 93 this->checkCoincident(); 94 return fIntersections->used(); 95 } 96 97 int intersect() { 98 this->addExactEndPoints(); 99 if (fAllowNear) { 100 this->addNearEndPoints(); 101 } 102 double rootVals[2]; 103 int roots = this->intersectRay(rootVals); 104 for (int index = 0; index < roots; ++index) { 105 double conicT = rootVals[index]; 106 double lineT = this->findLineT(conicT); 107 #ifdef SK_DEBUG 108 if (!fIntersections->globalState() 109 || !fIntersections->globalState()->debugSkipAssert()) { 110 SkDEBUGCODE(SkDPoint conicPt = fConic.ptAtT(conicT)); 111 SkDEBUGCODE(SkDPoint linePt = fLine->ptAtT(lineT)); 112 SkASSERT(conicPt.approximatelyDEqual(linePt)); 113 } 114 #endif 115 SkDPoint pt; 116 if (this->pinTs(&conicT, &lineT, &pt, kPointUninitialized) 117 && this->uniqueAnswer(conicT, pt)) { 118 fIntersections->insert(conicT, lineT, pt); 119 } 120 } 121 this->checkCoincident(); 122 return fIntersections->used(); 123 } 124 125 int intersectRay(double roots[2]) { 126 double adj = (*fLine)[1].fX - (*fLine)[0].fX; 127 double opp = (*fLine)[1].fY - (*fLine)[0].fY; 128 double r[3]; 129 for (int n = 0; n < 3; ++n) { 130 r[n] = (fConic[n].fY - (*fLine)[0].fY) * adj - (fConic[n].fX - (*fLine)[0].fX) * opp; 131 } 132 return this->validT(r, 0, roots); 133 } 134 135 int validT(double r[3], double axisIntercept, double roots[2]) { 136 double A = r[2]; 137 double B = r[1] * fConic.fWeight - axisIntercept * fConic.fWeight + axisIntercept; 138 double C = r[0]; 139 A += C - 2 * B; // A = a + c - 2*(b*w - xCept*w + xCept) 140 B -= C; // B = b*w - w * xCept + xCept - a 141 C -= axisIntercept; 142 return SkDQuad::RootsValidT(A, 2 * B, C, roots); 143 } 144 145 int verticalIntersect(double axisIntercept, double roots[2]) { 146 double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX }; 147 return this->validT(conicVals, axisIntercept, roots); 148 } 149 150 int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) { 151 this->addExactVerticalEndPoints(top, bottom, axisIntercept); 152 if (fAllowNear) { 153 this->addNearVerticalEndPoints(top, bottom, axisIntercept); 154 } 155 double roots[2]; 156 int count = this->verticalIntersect(axisIntercept, roots); 157 for (int index = 0; index < count; ++index) { 158 double conicT = roots[index]; 159 SkDPoint pt = fConic.ptAtT(conicT); 160 SkDEBUGCODE(double conicVals[] = { fConic[0].fX, fConic[1].fX, fConic[2].fX }); 161 SkOPOBJASSERT(fIntersections, close_to(pt.fX, axisIntercept, conicVals)); 162 double lineT = (pt.fY - top) / (bottom - top); 163 if (this->pinTs(&conicT, &lineT, &pt, kPointInitialized) 164 && this->uniqueAnswer(conicT, pt)) { 165 fIntersections->insert(conicT, lineT, pt); 166 } 167 } 168 if (flipped) { 169 fIntersections->flip(); 170 } 171 this->checkCoincident(); 172 return fIntersections->used(); 173 } 174 175 protected: 176 // OPTIMIZE: Functions of the form add .. points are indentical to the conic routines. 177 // add endpoints first to get zero and one t values exactly 178 void addExactEndPoints() { 179 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 180 double lineT = fLine->exactPoint(fConic[cIndex]); 181 if (lineT < 0) { 182 continue; 183 } 184 double conicT = (double) (cIndex >> 1); 185 fIntersections->insert(conicT, lineT, fConic[cIndex]); 186 } 187 } 188 189 void addNearEndPoints() { 190 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 191 double conicT = (double) (cIndex >> 1); 192 if (fIntersections->hasT(conicT)) { 193 continue; 194 } 195 double lineT = fLine->nearPoint(fConic[cIndex], nullptr); 196 if (lineT < 0) { 197 continue; 198 } 199 fIntersections->insert(conicT, lineT, fConic[cIndex]); 200 } 201 this->addLineNearEndPoints(); 202 } 203 204 void addLineNearEndPoints() { 205 for (int lIndex = 0; lIndex < 2; ++lIndex) { 206 double lineT = (double) lIndex; 207 if (fIntersections->hasOppT(lineT)) { 208 continue; 209 } 210 double conicT = ((SkDCurve*) &fConic)->nearPoint(SkPath::kConic_Verb, 211 (*fLine)[lIndex], (*fLine)[!lIndex]); 212 if (conicT < 0) { 213 continue; 214 } 215 fIntersections->insert(conicT, lineT, (*fLine)[lIndex]); 216 } 217 } 218 219 void addExactHorizontalEndPoints(double left, double right, double y) { 220 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 221 double lineT = SkDLine::ExactPointH(fConic[cIndex], left, right, y); 222 if (lineT < 0) { 223 continue; 224 } 225 double conicT = (double) (cIndex >> 1); 226 fIntersections->insert(conicT, lineT, fConic[cIndex]); 227 } 228 } 229 230 void addNearHorizontalEndPoints(double left, double right, double y) { 231 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 232 double conicT = (double) (cIndex >> 1); 233 if (fIntersections->hasT(conicT)) { 234 continue; 235 } 236 double lineT = SkDLine::NearPointH(fConic[cIndex], left, right, y); 237 if (lineT < 0) { 238 continue; 239 } 240 fIntersections->insert(conicT, lineT, fConic[cIndex]); 241 } 242 this->addLineNearEndPoints(); 243 } 244 245 void addExactVerticalEndPoints(double top, double bottom, double x) { 246 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 247 double lineT = SkDLine::ExactPointV(fConic[cIndex], top, bottom, x); 248 if (lineT < 0) { 249 continue; 250 } 251 double conicT = (double) (cIndex >> 1); 252 fIntersections->insert(conicT, lineT, fConic[cIndex]); 253 } 254 } 255 256 void addNearVerticalEndPoints(double top, double bottom, double x) { 257 for (int cIndex = 0; cIndex < SkDConic::kPointCount; cIndex += SkDConic::kPointLast) { 258 double conicT = (double) (cIndex >> 1); 259 if (fIntersections->hasT(conicT)) { 260 continue; 261 } 262 double lineT = SkDLine::NearPointV(fConic[cIndex], top, bottom, x); 263 if (lineT < 0) { 264 continue; 265 } 266 fIntersections->insert(conicT, lineT, fConic[cIndex]); 267 } 268 this->addLineNearEndPoints(); 269 } 270 271 double findLineT(double t) { 272 SkDPoint xy = fConic.ptAtT(t); 273 double dx = (*fLine)[1].fX - (*fLine)[0].fX; 274 double dy = (*fLine)[1].fY - (*fLine)[0].fY; 275 if (fabs(dx) > fabs(dy)) { 276 return (xy.fX - (*fLine)[0].fX) / dx; 277 } 278 return (xy.fY - (*fLine)[0].fY) / dy; 279 } 280 281 bool pinTs(double* conicT, double* lineT, SkDPoint* pt, PinTPoint ptSet) { 282 if (!approximately_one_or_less_double(*lineT)) { 283 return false; 284 } 285 if (!approximately_zero_or_more_double(*lineT)) { 286 return false; 287 } 288 double qT = *conicT = SkPinT(*conicT); 289 double lT = *lineT = SkPinT(*lineT); 290 if (lT == 0 || lT == 1 || (ptSet == kPointUninitialized && qT != 0 && qT != 1)) { 291 *pt = (*fLine).ptAtT(lT); 292 } else if (ptSet == kPointUninitialized) { 293 *pt = fConic.ptAtT(qT); 294 } 295 SkPoint gridPt = pt->asSkPoint(); 296 if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[0].asSkPoint())) { 297 *pt = (*fLine)[0]; 298 *lineT = 0; 299 } else if (SkDPoint::ApproximatelyEqual(gridPt, (*fLine)[1].asSkPoint())) { 300 *pt = (*fLine)[1]; 301 *lineT = 1; 302 } 303 if (fIntersections->used() > 0 && approximately_equal((*fIntersections)[1][0], *lineT)) { 304 return false; 305 } 306 if (gridPt == fConic[0].asSkPoint()) { 307 *pt = fConic[0]; 308 *conicT = 0; 309 } else if (gridPt == fConic[2].asSkPoint()) { 310 *pt = fConic[2]; 311 *conicT = 1; 312 } 313 return true; 314 } 315 316 bool uniqueAnswer(double conicT, const SkDPoint& pt) { 317 for (int inner = 0; inner < fIntersections->used(); ++inner) { 318 if (fIntersections->pt(inner) != pt) { 319 continue; 320 } 321 double existingConicT = (*fIntersections)[0][inner]; 322 if (conicT == existingConicT) { 323 return false; 324 } 325 // check if midway on conic is also same point. If so, discard this 326 double conicMidT = (existingConicT + conicT) / 2; 327 SkDPoint conicMidPt = fConic.ptAtT(conicMidT); 328 if (conicMidPt.approximatelyEqual(pt)) { 329 return false; 330 } 331 } 332 #if ONE_OFF_DEBUG 333 SkDPoint qPt = fConic.ptAtT(conicT); 334 SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY, 335 qPt.fX, qPt.fY); 336 #endif 337 return true; 338 } 339 340 private: 341 const SkDConic& fConic; 342 const SkDLine* fLine; 343 SkIntersections* fIntersections; 344 bool fAllowNear; 345 }; 346 347 int SkIntersections::horizontal(const SkDConic& conic, double left, double right, double y, 348 bool flipped) { 349 SkDLine line = {{{ left, y }, { right, y }}}; 350 LineConicIntersections c(conic, line, this); 351 return c.horizontalIntersect(y, left, right, flipped); 352 } 353 354 int SkIntersections::vertical(const SkDConic& conic, double top, double bottom, double x, 355 bool flipped) { 356 SkDLine line = {{{ x, top }, { x, bottom }}}; 357 LineConicIntersections c(conic, line, this); 358 return c.verticalIntersect(x, top, bottom, flipped); 359 } 360 361 int SkIntersections::intersect(const SkDConic& conic, const SkDLine& line) { 362 LineConicIntersections c(conic, line, this); 363 c.allowNear(fAllowNear); 364 return c.intersect(); 365 } 366 367 int SkIntersections::intersectRay(const SkDConic& conic, const SkDLine& line) { 368 LineConicIntersections c(conic, line, this); 369 fUsed = c.intersectRay(fT[0]); 370 for (int index = 0; index < fUsed; ++index) { 371 fPt[index] = conic.ptAtT(fT[0][index]); 372 } 373 return fUsed; 374 } 375 376 int SkIntersections::HorizontalIntercept(const SkDConic& conic, SkScalar y, double* roots) { 377 LineConicIntersections c(conic); 378 return c.horizontalIntersect(y, roots); 379 } 380 381 int SkIntersections::VerticalIntercept(const SkDConic& conic, SkScalar x, double* roots) { 382 LineConicIntersections c(conic); 383 return c.verticalIntersect(x, roots); 384 } 385