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 // Skip if each segment contributes to one end point. 177 if (nearCount != 2 || aNotB[0] == aNotB[1]) { 178 for (int iA = 0; iA < 2; ++iA) { 179 if (!aNotB[iA]) { 180 continue; 181 } 182 int nearer = aNearB[iA] > 0.5; 183 if (!bNotA[nearer]) { 184 continue; 185 } 186 SkASSERT(a[iA] != b[nearer]); 187 SkASSERT(iA == (bNearA[nearer] > 0.5)); 188 fNearlySame[iA] = true; 189 insertNear(iA, nearer, a[iA], b[nearer]); 190 aNearB[iA] = -1; 191 bNearA[nearer] = -1; 192 nearCount -= 2; 193 } 194 } 195 if (nearCount > 0) { 196 for (int iA = 0; iA < 2; ++iA) { 197 if (aNearB[iA] >= 0) { 198 insert(iA, aNearB[iA], a[iA]); 199 } 200 } 201 for (int iB = 0; iB < 2; ++iB) { 202 if (bNearA[iB] >= 0) { 203 insert(bNearA[iB], iB, b[iB]); 204 } 205 } 206 } 207 } 208 } 209 cleanUpParallelLines(!unparallel); 210 SkASSERT(fUsed <= 2); 211 return fUsed; 212 } 213 214 static int horizontal_coincident(const SkDLine& line, double y) { 215 double min = line[0].fY; 216 double max = line[1].fY; 217 if (min > max) { 218 SkTSwap(min, max); 219 } 220 if (min > y || max < y) { 221 return 0; 222 } 223 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) { 224 return 2; 225 } 226 return 1; 227 } 228 229 static double horizontal_intercept(const SkDLine& line, double y) { 230 return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY)); 231 } 232 233 int SkIntersections::horizontal(const SkDLine& line, double y) { 234 fMax = 2; 235 int horizontalType = horizontal_coincident(line, y); 236 if (horizontalType == 1) { 237 fT[0][0] = horizontal_intercept(line, y); 238 } else if (horizontalType == 2) { 239 fT[0][0] = 0; 240 fT[0][1] = 1; 241 } 242 return fUsed = horizontalType; 243 } 244 245 int SkIntersections::horizontal(const SkDLine& line, double left, double right, 246 double y, bool flipped) { 247 fMax = 3; // clean up parallel at the end will limit the result to 2 at the most 248 // see if end points intersect the opposite line 249 double t; 250 const SkDPoint leftPt = { left, y }; 251 if ((t = line.exactPoint(leftPt)) >= 0) { 252 insert(t, (double) flipped, leftPt); 253 } 254 if (left != right) { 255 const SkDPoint rightPt = { right, y }; 256 if ((t = line.exactPoint(rightPt)) >= 0) { 257 insert(t, (double) !flipped, rightPt); 258 } 259 for (int index = 0; index < 2; ++index) { 260 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) { 261 insert((double) index, flipped ? 1 - t : t, line[index]); 262 } 263 } 264 } 265 int result = horizontal_coincident(line, y); 266 if (result == 1 && fUsed == 0) { 267 fT[0][0] = horizontal_intercept(line, y); 268 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); 269 if (between(left, xIntercept, right)) { 270 fT[1][0] = (xIntercept - left) / (right - left); 271 if (flipped) { 272 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX 273 for (int index = 0; index < result; ++index) { 274 fT[1][index] = 1 - fT[1][index]; 275 } 276 } 277 fPt[0].fX = xIntercept; 278 fPt[0].fY = y; 279 fUsed = 1; 280 } 281 } 282 if (fAllowNear || result == 2) { 283 if ((t = line.nearPoint(leftPt, NULL)) >= 0) { 284 insert(t, (double) flipped, leftPt); 285 } 286 if (left != right) { 287 const SkDPoint rightPt = { right, y }; 288 if ((t = line.nearPoint(rightPt, NULL)) >= 0) { 289 insert(t, (double) !flipped, rightPt); 290 } 291 for (int index = 0; index < 2; ++index) { 292 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) { 293 insert((double) index, flipped ? 1 - t : t, line[index]); 294 } 295 } 296 } 297 } 298 cleanUpParallelLines(result == 2); 299 return fUsed; 300 } 301 302 static int vertical_coincident(const SkDLine& line, double x) { 303 double min = line[0].fX; 304 double max = line[1].fX; 305 if (min > max) { 306 SkTSwap(min, max); 307 } 308 if (!precisely_between(min, x, max)) { 309 return 0; 310 } 311 if (AlmostEqualUlps(min, max)) { 312 return 2; 313 } 314 return 1; 315 } 316 317 static double vertical_intercept(const SkDLine& line, double x) { 318 return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX)); 319 } 320 321 int SkIntersections::vertical(const SkDLine& line, double x) { 322 fMax = 2; 323 int verticalType = vertical_coincident(line, x); 324 if (verticalType == 1) { 325 fT[0][0] = vertical_intercept(line, x); 326 } else if (verticalType == 2) { 327 fT[0][0] = 0; 328 fT[0][1] = 1; 329 } 330 return fUsed = verticalType; 331 } 332 333 int SkIntersections::vertical(const SkDLine& line, double top, double bottom, 334 double x, bool flipped) { 335 fMax = 3; // cleanup parallel lines will bring this back line 336 // see if end points intersect the opposite line 337 double t; 338 SkDPoint topPt = { x, top }; 339 if ((t = line.exactPoint(topPt)) >= 0) { 340 insert(t, (double) flipped, topPt); 341 } 342 if (top != bottom) { 343 SkDPoint bottomPt = { x, bottom }; 344 if ((t = line.exactPoint(bottomPt)) >= 0) { 345 insert(t, (double) !flipped, bottomPt); 346 } 347 for (int index = 0; index < 2; ++index) { 348 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) { 349 insert((double) index, flipped ? 1 - t : t, line[index]); 350 } 351 } 352 } 353 int result = vertical_coincident(line, x); 354 if (result == 1 && fUsed == 0) { 355 fT[0][0] = vertical_intercept(line, x); 356 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); 357 if (between(top, yIntercept, bottom)) { 358 fT[1][0] = (yIntercept - top) / (bottom - top); 359 if (flipped) { 360 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY 361 for (int index = 0; index < result; ++index) { 362 fT[1][index] = 1 - fT[1][index]; 363 } 364 } 365 fPt[0].fX = x; 366 fPt[0].fY = yIntercept; 367 fUsed = 1; 368 } 369 } 370 if (fAllowNear || result == 2) { 371 if ((t = line.nearPoint(topPt, NULL)) >= 0) { 372 insert(t, (double) flipped, topPt); 373 } 374 if (top != bottom) { 375 SkDPoint bottomPt = { x, bottom }; 376 if ((t = line.nearPoint(bottomPt, NULL)) >= 0) { 377 insert(t, (double) !flipped, bottomPt); 378 } 379 for (int index = 0; index < 2; ++index) { 380 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) { 381 insert((double) index, flipped ? 1 - t : t, line[index]); 382 } 383 } 384 } 385 } 386 cleanUpParallelLines(result == 2); 387 SkASSERT(fUsed <= 2); 388 return fUsed; 389 } 390 391 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py 392 // 4 subs, 2 muls, 1 cmp 393 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) { 394 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX); 395 } 396 397 // 16 subs, 8 muls, 6 cmps 398 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) { 399 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1]) 400 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]); 401 } 402