blob: 4b818dcb97f70be50b2a9c229be3a64bea25c43b [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
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 */
14SkDPoint 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
29int SkIntersections::computePoints(const SkDLine& line, int used) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000030 fPt[0] = line.ptAtT(fT[0][0]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000031 if ((fUsed = used) == 2) {
caryclark@google.com4fdbb222013-07-23 15:27:41 +000032 fPt[1] = line.ptAtT(fT[0][1]);
caryclark@google.com07393ca2013-04-08 11:47:37 +000033 }
34 return fUsed;
35}
36
caryclark@google.comcffbcc32013-06-04 17:59:42 +000037int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
caryclark@google.com570863f2013-09-16 15:55:01 +000038 SkDVector aLen = a[1] - a[0];
39 SkDVector bLen = b[1] - b[0];
caryclark@google.comcffbcc32013-06-04 17:59:42 +000040 /* Slopes match when denom goes to zero:
41 axLen / ayLen == bxLen / byLen
42 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
43 byLen * axLen == ayLen * bxLen
44 byLen * axLen - ayLen * bxLen == 0 ( == denom )
45 */
caryclark@google.com570863f2013-09-16 15:55:01 +000046 double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX;
47 SkDVector ab0 = a[0] - b[0];
48 double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX;
49 double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX;
caryclark@google.comcffbcc32013-06-04 17:59:42 +000050 numerA /= denom;
51 numerB /= denom;
52 int used;
53 if (!approximately_zero(denom)) {
54 fT[0][0] = numerA;
55 fT[1][0] = numerB;
56 used = 1;
57 } else {
58 /* See if the axis intercepts match:
59 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
60 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
61 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
62 */
caryclark@google.com570863f2013-09-16 15:55:01 +000063 if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX,
64 aLen.fX * b[0].fY - aLen.fY * b[0].fX)) {
caryclark@google.comcffbcc32013-06-04 17:59:42 +000065 return fUsed = 0;
66 }
67 // there's no great answer for intersection points for coincident rays, but return something
68 fT[0][0] = fT[1][0] = 0;
69 fT[1][0] = fT[1][1] = 1;
70 used = 2;
71 }
72 return computePoints(a, used);
73}
74
caryclark@google.com07e97fc2013-07-08 17:17:02 +000075// note that this only works if both lines are neither horizontal nor vertical
caryclark@google.com07393ca2013-04-08 11:47:37 +000076int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +000077 // see if end points intersect the opposite line
78 double t;
79 for (int iA = 0; iA < 2; ++iA) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000080 if ((t = b.exactPoint(a[iA])) >= 0) {
81 insert(iA, t, a[iA]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000082 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000083 }
84 for (int iB = 0; iB < 2; ++iB) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +000085 if ((t = a.exactPoint(b[iB])) >= 0) {
86 insert(t, iB, b[iB]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +000087 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +000088 }
89 /* Determine the intersection point of two line segments
90 Return FALSE if the lines don't intersect
91 from: http://paulbourke.net/geometry/lineline2d/ */
caryclark@google.com07393ca2013-04-08 11:47:37 +000092 double axLen = a[1].fX - a[0].fX;
93 double ayLen = a[1].fY - a[0].fY;
94 double bxLen = b[1].fX - b[0].fX;
95 double byLen = b[1].fY - b[0].fY;
96 /* Slopes match when denom goes to zero:
97 axLen / ayLen == bxLen / byLen
98 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
99 byLen * axLen == ayLen * bxLen
100 byLen * axLen - ayLen * bxLen == 0 ( == denom )
101 */
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000102 double axByLen = axLen * byLen;
103 double ayBxLen = ayLen * bxLen;
104 // detect parallel lines the same way here and in SkOpAngle operator <
105 // so that non-parallel means they are also sortable
caryclark@google.com570863f2013-09-16 15:55:01 +0000106 bool unparallel = NotAlmostEqualUlps(axByLen, ayBxLen);
107 if (unparallel) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000108 double ab0y = a[0].fY - b[0].fY;
109 double ab0x = a[0].fX - b[0].fX;
110 double numerA = ab0y * bxLen - byLen * ab0x;
111 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000112 double denom = axByLen - ayBxLen;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000113 if (between(0, numerA, denom) && between(0, numerB, denom)) {
114 fT[0][0] = numerA / denom;
115 fT[1][0] = numerB / denom;
caryclark@google.com4fdbb222013-07-23 15:27:41 +0000116 computePoints(a, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000117 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000119 if (fAllowNear || !unparallel) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000120 for (int iA = 0; iA < 2; ++iA) {
121 if ((t = b.nearPoint(a[iA])) >= 0) {
122 insert(iA, t, a[iA]);
123 }
124 }
125 for (int iB = 0; iB < 2; ++iB) {
126 if ((t = a.nearPoint(b[iB])) >= 0) {
127 insert(t, iB, b[iB]);
128 }
129 }
130 }
caryclark@google.com570863f2013-09-16 15:55:01 +0000131 while (fUsed > 2) {
132 removeOne(1);
133 }
134 if (fUsed == 2 && unparallel) {
135 bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
136 bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
137 if (!startMatch && !endMatch) {
138 SkASSERT(startMatch || endMatch);
139 removeOne(endMatch);
140 }
141 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000142 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000143}
144
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000145static int horizontal_coincident(const SkDLine& line, double y) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000146 double min = line[0].fY;
147 double max = line[1].fY;
148 if (min > max) {
149 SkTSwap(min, max);
150 }
151 if (min > y || max < y) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000152 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000153 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000154 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000155 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000157 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000158}
159
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000160static double horizontal_intercept(const SkDLine& line, double y) {
161 return (y - line[0].fY) / (line[1].fY - line[0].fY);
162}
163
164int SkIntersections::horizontal(const SkDLine& line, double y) {
165 int horizontalType = horizontal_coincident(line, y);
166 if (horizontalType == 1) {
167 fT[0][0] = horizontal_intercept(line, y);
168 } else if (horizontalType == 2) {
169 fT[0][0] = 0;
170 fT[0][1] = 1;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000171 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000172 return fUsed = horizontalType;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000173}
174
caryclark@google.com07393ca2013-04-08 11:47:37 +0000175int SkIntersections::horizontal(const SkDLine& line, double left, double right,
176 double y, bool flipped) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000177 // see if end points intersect the opposite line
178 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000179 const SkDPoint leftPt = { left, y };
180 if ((t = line.exactPoint(leftPt)) >= 0) {
181 insert(t, (double) flipped, leftPt);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000182 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000183 if (left != right) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000184 const SkDPoint rightPt = { right, y };
185 if ((t = line.exactPoint(rightPt)) >= 0) {
186 insert(t, (double) !flipped, rightPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000187 }
188 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000189 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) {
190 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000191 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000192 }
193 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000194 int result = horizontal_coincident(line, y);
195 if (result == 1 && fUsed == 0) {
196 fT[0][0] = horizontal_intercept(line, y);
197 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX);
198 if (between(left, xIntercept, right)) {
199 fT[1][0] = (xIntercept - left) / (right - left);
200 if (flipped) {
201 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX - [0].fX
202 for (int index = 0; index < result; ++index) {
203 fT[1][index] = 1 - fT[1][index];
204 }
205 }
206 return computePoints(line, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000207 }
208 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000209 if (!fAllowNear && result != 2) {
210 return fUsed;
211 }
212 if ((t = line.nearPoint(leftPt)) >= 0) {
213 insert(t, (double) flipped, leftPt);
214 }
215 if (left != right) {
216 const SkDPoint rightPt = { right, y };
217 if ((t = line.nearPoint(rightPt)) >= 0) {
218 insert(t, (double) !flipped, rightPt);
219 }
220 for (int index = 0; index < 2; ++index) {
221 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
222 insert((double) index, flipped ? 1 - t : t, line[index]);
223 }
224 }
225 }
226 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000227}
228
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000229static int vertical_coincident(const SkDLine& line, double x) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000230 double min = line[0].fX;
231 double max = line[1].fX;
232 if (min > max) {
233 SkTSwap(min, max);
234 }
caryclark@google.com03610322013-04-18 15:58:21 +0000235 if (!precisely_between(min, x, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000236 return 0;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000237 }
238 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000239 return 2;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000240 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000241 return 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000242}
243
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000244static double vertical_intercept(const SkDLine& line, double x) {
245 return (x - line[0].fX) / (line[1].fX - line[0].fX);
246}
247
248int SkIntersections::vertical(const SkDLine& line, double x) {
249 int verticalType = vertical_coincident(line, x);
250 if (verticalType == 1) {
251 fT[0][0] = vertical_intercept(line, x);
252 } else if (verticalType == 2) {
253 fT[0][0] = 0;
254 fT[0][1] = 1;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000256 return fUsed = verticalType;
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000257}
258
259int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000260 double x, bool flipped) {
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000261 // see if end points intersect the opposite line
262 double t;
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000263 SkDPoint topPt = { x, top };
264 if ((t = line.exactPoint(topPt)) >= 0) {
265 insert(t, (double) flipped, topPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000266 }
267 if (top != bottom) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000268 SkDPoint bottomPt = { x, bottom };
269 if ((t = line.exactPoint(bottomPt)) >= 0) {
270 insert(t, (double) !flipped, bottomPt);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000271 }
272 for (int index = 0; index < 2; ++index) {
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000273 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) {
274 insert((double) index, flipped ? 1 - t : t, line[index]);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000275 }
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000276 }
277 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000278 int result = vertical_coincident(line, x);
279 if (result == 1 && fUsed == 0) {
280 fT[0][0] = vertical_intercept(line, x);
281 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY);
282 if (between(top, yIntercept, bottom)) {
283 fT[1][0] = (yIntercept - top) / (bottom - top);
284 if (flipped) {
285 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [0].fY
286 for (int index = 0; index < result; ++index) {
287 fT[1][index] = 1 - fT[1][index];
288 }
289 }
290 return computePoints(line, result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000291 }
292 }
caryclark@google.comfa2aeee2013-07-15 13:29:13 +0000293 if (!fAllowNear && result != 2) {
294 return fUsed;
295 }
296 if ((t = line.nearPoint(topPt)) >= 0) {
297 insert(t, (double) flipped, topPt);
298 }
299 if (top != bottom) {
300 SkDPoint bottomPt = { x, bottom };
301 if ((t = line.nearPoint(bottomPt)) >= 0) {
302 insert(t, (double) !flipped, bottomPt);
303 }
304 for (int index = 0; index < 2; ++index) {
305 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
306 insert((double) index, flipped ? 1 - t : t, line[index]);
307 }
308 }
309 }
310 return fUsed;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000311}
312
313// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
314// 4 subs, 2 muls, 1 cmp
315static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) {
316 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX);
317}
318
319// 16 subs, 8 muls, 6 cmps
320bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) {
321 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
322 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
323}