blob: 5308a26474ce95c536e059846f169ab4bdf5779c [file] [log] [blame]
caryclark@google.com9e49fb62012-08-27 14:11:33 +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 */
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00007#include "CurveIntersection.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008#include "Intersections.h"
caryclark@google.com639df892012-01-10 21:46:10 +00009#include "LineIntersection.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000010#include <algorithm> // used for std::swap
caryclark@google.com639df892012-01-10 21:46:10 +000011
caryclark@google.com73ca6242013-01-17 21:02:47 +000012/* Determine the intersection point of two lines. This assumes the lines are not parallel,
13 and that that the lines are infinite.
14 From http://en.wikipedia.org/wiki/Line-line_intersection
15 */
16void lineIntersect(const _Line& a, const _Line& b, _Point& p) {
17 double axLen = a[1].x - a[0].x;
18 double ayLen = a[1].y - a[0].y;
19 double bxLen = b[1].x - b[0].x;
20 double byLen = b[1].y - b[0].y;
21 double denom = byLen * axLen - ayLen * bxLen;
22 assert(denom);
23 double term1 = a[1].x * a[0].y - a[1].y * a[0].x;
24 double term2 = b[1].x * b[0].y - b[1].y * b[0].x;
25 p.x = (term1 * bxLen - axLen * term2) / denom;
26 p.y = (term1 * byLen - ayLen * term2) / denom;
27}
caryclark@google.com639df892012-01-10 21:46:10 +000028
caryclark@google.com639df892012-01-10 21:46:10 +000029/*
30 Determine the intersection point of two line segments
31 Return FALSE if the lines don't intersect
32 from: http://paulbourke.net/geometry/lineline2d/
caryclark@google.com73ca6242013-01-17 21:02:47 +000033 */
caryclark@google.com639df892012-01-10 21:46:10 +000034
caryclark@google.comc6825902012-02-03 22:07:47 +000035int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]) {
caryclark@google.com639df892012-01-10 21:46:10 +000036 double axLen = a[1].x - a[0].x;
37 double ayLen = a[1].y - a[0].y;
38 double bxLen = b[1].x - b[0].x;
39 double byLen = b[1].y - b[0].y;
rmistry@google.comd6176b02012-08-23 18:14:13 +000040 /* Slopes match when denom goes to zero:
caryclark@google.comc6825902012-02-03 22:07:47 +000041 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.com73ca6242013-01-17 21:02:47 +000046 double denom = byLen * axLen - ayLen * bxLen;
caryclark@google.comd1688742012-09-18 20:08:37 +000047 if (approximately_zero(denom)) {
caryclark@google.comc6825902012-02-03 22:07:47 +000048 /* See if the axis intercepts match:
49 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
50 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
51 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
52 */
caryclark@google.com6d0032a2013-01-04 19:41:13 +000053 // FIXME: need to use AlmostEqualUlps variant instead
caryclark@google.comc6825902012-02-03 22:07:47 +000054 if (approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
55 axLen * b[0].y - ayLen * b[0].x)) {
56 const double* aPtr;
57 const double* bPtr;
58 if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
59 aPtr = &a[0].x;
60 bPtr = &b[0].x;
caryclark@google.com639df892012-01-10 21:46:10 +000061 } else {
caryclark@google.comc6825902012-02-03 22:07:47 +000062 aPtr = &a[0].y;
63 bPtr = &b[0].y;
caryclark@google.com639df892012-01-10 21:46:10 +000064 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +000065 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comc6825902012-02-03 22:07:47 +000066 double aMin = aPtr[0];
67 double aMax = aPtr[2];
68 double bMin = bPtr[0];
69 double bMax = bPtr[2];
70 if (aMin > aMax) {
71 std::swap(aMin, aMax);
caryclark@google.com639df892012-01-10 21:46:10 +000072 }
caryclark@google.comc6825902012-02-03 22:07:47 +000073 if (bMin > bMax) {
74 std::swap(bMin, bMax);
75 }
76 if (aMax < bMin || bMax < aMin) {
77 return 0;
78 }
79 if (aRange) {
80 aRange[0] = bMin <= aMin ? 0 : (bMin - aMin) / (aMax - aMin);
81 aRange[1] = bMax >= aMax ? 1 : (bMax - aMin) / (aMax - aMin);
82 }
caryclark@google.com495f8e42012-05-31 13:13:11 +000083 int bIn = (aPtr[0] - aPtr[2]) * (bPtr[0] - bPtr[2]) < 0;
caryclark@google.comc6825902012-02-03 22:07:47 +000084 if (bRange) {
caryclark@google.com495f8e42012-05-31 13:13:11 +000085 bRange[bIn] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
86 bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
caryclark@google.comc6825902012-02-03 22:07:47 +000087 }
caryclark@google.com4917f172012-03-05 22:01:21 +000088 return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
caryclark@google.com8dcf1142012-07-02 20:27:02 +000089 #else
90 assert(aRange);
91 assert(bRange);
92 double a0 = aPtr[0];
93 double a1 = aPtr[2];
94 double b0 = bPtr[0];
95 double b1 = bPtr[2];
caryclark@google.comdb0b3e02012-12-21 21:34:36 +000096 // OPTIMIZATION: restructure to reject before the divide
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +000097 // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
caryclark@google.comdb0b3e02012-12-21 21:34:36 +000098 // (except efficient)
caryclark@google.com8dcf1142012-07-02 20:27:02 +000099 double at0 = (a0 - b0) / (a0 - a1);
100 double at1 = (a0 - b1) / (a0 - a1);
101 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
102 return 0;
103 }
104 aRange[0] = std::max(std::min(at0, 1.0), 0.0);
105 aRange[1] = std::max(std::min(at1, 1.0), 0.0);
106 int bIn = (a0 - a1) * (b0 - b1) < 0;
107 bRange[bIn] = std::max(std::min((b0 - a0) / (b0 - b1), 1.0), 0.0);
108 bRange[!bIn] = std::max(std::min((b0 - a1) / (b0 - b1), 1.0), 0.0);
109 bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
110 assert((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
111 return 1 + second;
112 #endif
caryclark@google.com639df892012-01-10 21:46:10 +0000113 }
caryclark@google.com639df892012-01-10 21:46:10 +0000114 }
115 double ab0y = a[0].y - b[0].y;
116 double ab0x = a[0].x - b[0].x;
117 double numerA = ab0y * bxLen - byLen * ab0x;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000118 if ((numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000119 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +0000120 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000121 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000122 if ((numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000123 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +0000124 }
caryclark@google.com639df892012-01-10 21:46:10 +0000125 /* Is the intersection along the the segments */
caryclark@google.comc6825902012-02-03 22:07:47 +0000126 if (aRange) {
127 aRange[0] = numerA / denom;
128 }
129 if (bRange) {
130 bRange[0] = numerB / denom;
131 }
132 return 1;
caryclark@google.com639df892012-01-10 21:46:10 +0000133}
134
caryclark@google.comc6825902012-02-03 22:07:47 +0000135int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
136 double min = line[0].y;
137 double max = line[1].y;
138 if (min > max) {
139 std::swap(min, max);
140 }
141 if (min > y || max < y) {
142 return 0;
143 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000144 if (AlmostEqualUlps(min, max)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000145 tRange[0] = 0;
146 tRange[1] = 1;
147 return 2;
148 }
149 tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
150 return 1;
151}
caryclark@google.com639df892012-01-10 21:46:10 +0000152
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000153// OPTIMIZATION Given: dy = line[1].y - line[0].y
154// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
155// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
156// Assuming that dy is always > 0, the line segment intercepts if:
157// left * dy <= xIntercept * dy <= right * dy
158// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
159// (clever as this is, it does not give us the t value, so may be useful only
160// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
161int horizontalLineIntersect(const _Line& line, double left, double right,
162 double y, double tRange[2]) {
163 int result = horizontalIntersect(line, y, tRange);
164 if (result != 1) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000165 // FIXME: this is incorrect if result == 2
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000166 return result;
167 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000168 double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
169 if (xIntercept > right || xIntercept < left) {
170 return 0;
171 }
172 return result;
173}
174
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000175int horizontalIntersect(const _Line& line, double left, double right,
176 double y, bool flipped, Intersections& intersections) {
177 int result = horizontalIntersect(line, y, intersections.fT[0]);
178 switch (result) {
179 case 0:
180 break;
181 case 1: {
182 double xIntercept = line[0].x + intersections.fT[0][0]
183 * (line[1].x - line[0].x);
184 if (xIntercept > right || xIntercept < left) {
185 return 0;
186 }
187 intersections.fT[1][0] = (xIntercept - left) / (right - left);
188 break;
189 }
190 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000191 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000192 double lineL = line[0].x;
193 double lineR = line[1].x;
194 if (lineL > lineR) {
195 std::swap(lineL, lineR);
196 }
197 double overlapL = std::max(left, lineL);
198 double overlapR = std::min(right, lineR);
199 if (overlapL > overlapR) {
200 return 0;
201 }
202 if (overlapL == overlapR) {
203 result = 1;
204 }
205 intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
206 intersections.fT[1][0] = (overlapL - left) / (right - left);
207 if (result > 1) {
208 intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
209 intersections.fT[1][1] = (overlapR - left) / (right - left);
210 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000211 #else
212 double a0 = line[0].x;
213 double a1 = line[1].x;
214 double b0 = flipped ? right : left;
215 double b1 = flipped ? left : right;
216 // FIXME: share common code below
217 double at0 = (a0 - b0) / (a0 - a1);
218 double at1 = (a0 - b1) / (a0 - a1);
219 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
220 return 0;
221 }
222 intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
223 intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
224 int bIn = (a0 - a1) * (b0 - b1) < 0;
225 intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
226 1.0), 0.0);
227 intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
228 1.0), 0.0);
229 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
230 > FLT_EPSILON;
231 assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
232 <= FLT_EPSILON) ^ second);
233 return 1 + second;
234 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000235 break;
236 }
237 if (flipped) {
238 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
239 for (int index = 0; index < result; ++index) {
240 intersections.fT[1][index] = 1 - intersections.fT[1][index];
241 }
242 }
243 return result;
244}
245
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000246static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000247 double min = line[0].x;
248 double max = line[1].x;
249 if (min > max) {
250 std::swap(min, max);
251 }
252 if (min > x || max < x) {
253 return 0;
254 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000255 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000256 tRange[0] = 0;
257 tRange[1] = 1;
258 return 2;
259 }
260 tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
261 return 1;
262}
263
264int verticalIntersect(const _Line& line, double top, double bottom,
265 double x, bool flipped, Intersections& intersections) {
266 int result = verticalIntersect(line, x, intersections.fT[0]);
267 switch (result) {
268 case 0:
269 break;
270 case 1: {
271 double yIntercept = line[0].y + intersections.fT[0][0]
272 * (line[1].y - line[0].y);
273 if (yIntercept > bottom || yIntercept < top) {
274 return 0;
275 }
276 intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
277 break;
278 }
279 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000280 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000281 double lineT = line[0].y;
282 double lineB = line[1].y;
283 if (lineT > lineB) {
284 std::swap(lineT, lineB);
285 }
286 double overlapT = std::max(top, lineT);
287 double overlapB = std::min(bottom, lineB);
288 if (overlapT > overlapB) {
289 return 0;
290 }
291 if (overlapT == overlapB) {
292 result = 1;
293 }
294 intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
295 intersections.fT[1][0] = (overlapT - top) / (bottom - top);
296 if (result > 1) {
297 intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
298 intersections.fT[1][1] = (overlapB - top) / (bottom - top);
299 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000300 #else
301 double a0 = line[0].y;
302 double a1 = line[1].y;
303 double b0 = flipped ? bottom : top;
304 double b1 = flipped ? top : bottom;
305 // FIXME: share common code above
306 double at0 = (a0 - b0) / (a0 - a1);
307 double at1 = (a0 - b1) / (a0 - a1);
308 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
309 return 0;
310 }
311 intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
312 intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
313 int bIn = (a0 - a1) * (b0 - b1) < 0;
314 intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
315 1.0), 0.0);
316 intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
317 1.0), 0.0);
318 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
319 > FLT_EPSILON;
320 assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
321 <= FLT_EPSILON) ^ second);
322 return 1 + second;
323 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000324 break;
325 }
326 if (flipped) {
327 // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
328 for (int index = 0; index < result; ++index) {
329 intersections.fT[1][index] = 1 - intersections.fT[1][index];
330 }
331 }
332 return result;
333}
334
caryclark@google.com639df892012-01-10 21:46:10 +0000335// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
336// 4 subs, 2 muls, 1 cmp
337static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000338 return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
caryclark@google.com639df892012-01-10 21:46:10 +0000339}
340
341// 16 subs, 8 muls, 6 cmps
342bool testIntersect(const _Line& a, const _Line& b) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000343 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
caryclark@google.com639df892012-01-10 21:46:10 +0000344 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
345}