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