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 10void SkIntersections::cleanUpParallelLines(bool parallel) { 11 while (fUsed > 2) { 12 removeOne(1); 13 } 14 if (fUsed == 2 && !parallel) { 15 bool startMatch = fT[0][0] == 0 || zero_or_one(fT[1][0]); 16 bool endMatch = fT[0][1] == 1 || zero_or_one(fT[1][1]); 17 if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) { 18 SkASSERT(startMatch || endMatch); 19 if (startMatch && endMatch && (fT[0][0] != 0 || !zero_or_one(fT[1][0])) 20 && fT[0][1] == 1 && zero_or_one(fT[1][1])) { 21 removeOne(0); 22 } else { 23 removeOne(endMatch); 24 } 25 } 26 } 27 if (fUsed == 2) { 28 fIsCoincident[0] = fIsCoincident[1] = 0x03; 29 } 30} 31 32void SkIntersections::computePoints(const SkDLine& line, int used) { 33 fPt[0] = line.ptAtT(fT[0][0]); 34 if ((fUsed = used) == 2) { 35 fPt[1] = line.ptAtT(fT[0][1]); 36 } 37} 38 39int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) { 40 fMax = 2; 41 SkDVector aLen = a[1] - a[0]; 42 SkDVector bLen = b[1] - b[0]; 43 /* Slopes match when denom goes to zero: 44 axLen / ayLen == bxLen / byLen 45 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen 46 byLen * axLen == ayLen * bxLen 47 byLen * axLen - ayLen * bxLen == 0 ( == denom ) 48 */ 49 double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX; 50 SkDVector ab0 = a[0] - b[0]; 51 double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX; 52 double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX; 53 numerA /= denom; 54 numerB /= denom; 55 int used; 56 if (!approximately_zero(denom)) { 57 fT[0][0] = numerA; 58 fT[1][0] = numerB; 59 used = 1; 60 } else { 61 /* See if the axis intercepts match: 62 ay - ax * ayLen / axLen == by - bx * ayLen / axLen 63 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen) 64 axLen * ay - ax * ayLen == axLen * by - bx * ayLen 65 */ 66 if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX, 67 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) { 68 return fUsed = 0; 69 } 70 // there's no great answer for intersection points for coincident rays, but return something 71 fT[0][0] = fT[1][0] = 0; 72 fT[1][0] = fT[1][1] = 1; 73 used = 2; 74 } 75 computePoints(a, used); 76 return fUsed; 77} 78 79// note that this only works if both lines are neither horizontal nor vertical 80int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { 81 fMax = 3; // note that we clean up so that there is no more than two in the end 82 // see if end points intersect the opposite line 83 double t; 84 for (int iA = 0; iA < 2; ++iA) { 85 if ((t = b.exactPoint(a[iA])) >= 0) { 86 insert(iA, t, a[iA]); 87 } 88 } 89 for (int iB = 0; iB < 2; ++iB) { 90 if ((t = a.exactPoint(b[iB])) >= 0) { 91 insert(t, iB, b[iB]); 92 } 93 } 94 /* Determine the intersection point of two line segments 95 Return FALSE if the lines don't intersect 96 from: http://paulbourke.net/geometry/lineline2d/ */ 97 double axLen = a[1].fX - a[0].fX; 98 double ayLen = a[1].fY - a[0].fY; 99 double bxLen = b[1].fX - b[0].fX; 100 double byLen = b[1].fY - b[0].fY; 101 /* Slopes match when denom goes to zero: 102 axLen / ayLen == bxLen / byLen 103 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen 104 byLen * axLen == ayLen * bxLen 105 byLen * axLen - ayLen * bxLen == 0 ( == denom ) 106 */ 107 double axByLen = axLen * byLen; 108 double ayBxLen = ayLen * bxLen; 109 // detect parallel lines the same way here and in SkOpAngle operator < 110 // so that non-parallel means they are also sortable 111 bool unparallel = fAllowNear ? NotAlmostEqualUlps_Pin(axByLen, ayBxLen) 112 : NotAlmostDequalUlps(axByLen, ayBxLen); 113 if (unparallel && fUsed == 0) { 114 double ab0y = a[0].fY - b[0].fY; 115 double ab0x = a[0].fX - b[0].fX; 116 double numerA = ab0y * bxLen - byLen * ab0x; 117 double numerB = ab0y * axLen - ayLen * ab0x; 118 double denom = axByLen - ayBxLen; 119 if (between(0, numerA, denom) && between(0, numerB, denom)) { 120 fT[0][0] = numerA / denom; 121 fT[1][0] = numerB / denom; 122 computePoints(a, 1); 123 } 124 } 125/* Allow tracking that both sets of end points are near each other -- the lines are entirely 126 coincident -- even when the end points are not exactly the same. 127 Mark this as a 'wild card' for the end points, so that either point is considered totally 128 coincident. Then, avoid folding the lines over each other, but allow either end to mate 129 to the next set of lines. 130 */ 131 if (fAllowNear || !unparallel) { 132 double aNearB[2]; 133 double bNearA[2]; 134 bool aNotB[2] = {false, false}; 135 bool bNotA[2] = {false, false}; 136 int nearCount = 0; 137 for (int index = 0; index < 2; ++index) { 138 aNearB[index] = t = b.nearPoint(a[index], &aNotB[index]); 139 nearCount += t >= 0; 140 bNearA[index] = t = a.nearPoint(b[index], &bNotA[index]); 141 nearCount += t >= 0; 142 } 143 if (nearCount > 0) { 144 // Skip if each segment contributes to one end point. 145 if (nearCount != 2 || aNotB[0] == aNotB[1]) { 146 for (int iA = 0; iA < 2; ++iA) { 147 if (!aNotB[iA]) { 148 continue; 149 } 150 int nearer = aNearB[iA] > 0.5; 151 if (!bNotA[nearer]) { 152 continue; 153 } 154 SkASSERT(a[iA] != b[nearer]); 155 SkOPASSERT(iA == (bNearA[nearer] > 0.5)); 156 insertNear(iA, nearer, a[iA], b[nearer]); 157 aNearB[iA] = -1; 158 bNearA[nearer] = -1; 159 nearCount -= 2; 160 } 161 } 162 if (nearCount > 0) { 163 for (int iA = 0; iA < 2; ++iA) { 164 if (aNearB[iA] >= 0) { 165 insert(iA, aNearB[iA], a[iA]); 166 } 167 } 168 for (int iB = 0; iB < 2; ++iB) { 169 if (bNearA[iB] >= 0) { 170 insert(bNearA[iB], iB, b[iB]); 171 } 172 } 173 } 174 } 175 } 176 cleanUpParallelLines(!unparallel); 177 SkASSERT(fUsed <= 2); 178 return fUsed; 179} 180 181static int horizontal_coincident(const SkDLine& line, double y) { 182 double min = line[0].fY; 183 double max = line[1].fY; 184 if (min > max) { 185 SkTSwap(min, max); 186 } 187 if (min > y || max < y) { 188 return 0; 189 } 190 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) { 191 return 2; 192 } 193 return 1; 194} 195 196double SkIntersections::HorizontalIntercept(const SkDLine& line, double y) { 197 return SkPinT((y - line[0].fY) / (line[1].fY - line[0].fY)); 198} 199 200int SkIntersections::horizontal(const SkDLine& line, double left, double right, 201 double y, bool flipped) { 202 fMax = 3; // clean up parallel at the end will limit the result to 2 at the most 203 // see if end points intersect the opposite line 204 double t; 205 const SkDPoint leftPt = { left, y }; 206 if ((t = line.exactPoint(leftPt)) >= 0) { 207 insert(t, (double) flipped, leftPt); 208 } 209 if (left != right) { 210 const SkDPoint rightPt = { right, y }; 211 if ((t = line.exactPoint(rightPt)) >= 0) { 212 insert(t, (double) !flipped, rightPt); 213 } 214 for (int index = 0; index < 2; ++index) { 215 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) { 216 insert((double) index, flipped ? 1 - t : t, line[index]); 217 } 218 } 219 } 220 int result = horizontal_coincident(line, y); 221 if (result == 1 && fUsed == 0) { 222 fT[0][0] = HorizontalIntercept(line, y); 223 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); 224 if (between(left, xIntercept, right)) { 225 fT[1][0] = (xIntercept - left) / (right - left); 226 if (flipped) { 227 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX 228 for (int index = 0; index < result; ++index) { 229 fT[1][index] = 1 - fT[1][index]; 230 } 231 } 232 fPt[0].fX = xIntercept; 233 fPt[0].fY = y; 234 fUsed = 1; 235 } 236 } 237 if (fAllowNear || result == 2) { 238 if ((t = line.nearPoint(leftPt, nullptr)) >= 0) { 239 insert(t, (double) flipped, leftPt); 240 } 241 if (left != right) { 242 const SkDPoint rightPt = { right, y }; 243 if ((t = line.nearPoint(rightPt, nullptr)) >= 0) { 244 insert(t, (double) !flipped, rightPt); 245 } 246 for (int index = 0; index < 2; ++index) { 247 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) { 248 insert((double) index, flipped ? 1 - t : t, line[index]); 249 } 250 } 251 } 252 } 253 cleanUpParallelLines(result == 2); 254 return fUsed; 255} 256 257static int vertical_coincident(const SkDLine& line, double x) { 258 double min = line[0].fX; 259 double max = line[1].fX; 260 if (min > max) { 261 SkTSwap(min, max); 262 } 263 if (!precisely_between(min, x, max)) { 264 return 0; 265 } 266 if (AlmostEqualUlps(min, max)) { 267 return 2; 268 } 269 return 1; 270} 271 272double SkIntersections::VerticalIntercept(const SkDLine& line, double x) { 273 return SkPinT((x - line[0].fX) / (line[1].fX - line[0].fX)); 274} 275 276int SkIntersections::vertical(const SkDLine& line, double top, double bottom, 277 double x, bool flipped) { 278 fMax = 3; // cleanup parallel lines will bring this back line 279 // see if end points intersect the opposite line 280 double t; 281 SkDPoint topPt = { x, top }; 282 if ((t = line.exactPoint(topPt)) >= 0) { 283 insert(t, (double) flipped, topPt); 284 } 285 if (top != bottom) { 286 SkDPoint bottomPt = { x, bottom }; 287 if ((t = line.exactPoint(bottomPt)) >= 0) { 288 insert(t, (double) !flipped, bottomPt); 289 } 290 for (int index = 0; index < 2; ++index) { 291 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) { 292 insert((double) index, flipped ? 1 - t : t, line[index]); 293 } 294 } 295 } 296 int result = vertical_coincident(line, x); 297 if (result == 1 && fUsed == 0) { 298 fT[0][0] = VerticalIntercept(line, x); 299 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); 300 if (between(top, yIntercept, bottom)) { 301 fT[1][0] = (yIntercept - top) / (bottom - top); 302 if (flipped) { 303 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY 304 for (int index = 0; index < result; ++index) { 305 fT[1][index] = 1 - fT[1][index]; 306 } 307 } 308 fPt[0].fX = x; 309 fPt[0].fY = yIntercept; 310 fUsed = 1; 311 } 312 } 313 if (fAllowNear || result == 2) { 314 if ((t = line.nearPoint(topPt, nullptr)) >= 0) { 315 insert(t, (double) flipped, topPt); 316 } 317 if (top != bottom) { 318 SkDPoint bottomPt = { x, bottom }; 319 if ((t = line.nearPoint(bottomPt, nullptr)) >= 0) { 320 insert(t, (double) !flipped, bottomPt); 321 } 322 for (int index = 0; index < 2; ++index) { 323 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) { 324 insert((double) index, flipped ? 1 - t : t, line[index]); 325 } 326 } 327 } 328 } 329 cleanUpParallelLines(result == 2); 330 SkASSERT(fUsed <= 2); 331 return fUsed; 332} 333 334