blob: 3efa240d83ba8c102b5341d6d7a632e029f8d19f [file] [log] [blame]
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001#include "CurveIntersection.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002#include "Intersections.h"
caryclark@google.com639df892012-01-10 21:46:10 +00003#include "LineIntersection.h"
caryclark@google.comc6825902012-02-03 22:07:47 +00004#include <algorithm> // used for std::swap
caryclark@google.com639df892012-01-10 21:46:10 +00005
6
caryclark@google.com639df892012-01-10 21:46:10 +00007/*
8 Determine the intersection point of two line segments
9 Return FALSE if the lines don't intersect
10 from: http://paulbourke.net/geometry/lineline2d/
caryclark@google.com639df892012-01-10 21:46:10 +000011*/
12
caryclark@google.comc6825902012-02-03 22:07:47 +000013int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]) {
caryclark@google.com639df892012-01-10 21:46:10 +000014 double axLen = a[1].x - a[0].x;
15 double ayLen = a[1].y - a[0].y;
16 double bxLen = b[1].x - b[0].x;
17 double byLen = b[1].y - b[0].y;
caryclark@google.comc6825902012-02-03 22:07:47 +000018 /* Slopes match when denom goes to zero:
19 axLen / ayLen == bxLen / byLen
20 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
21 byLen * axLen == ayLen * bxLen
22 byLen * axLen - ayLen * bxLen == 0 ( == denom )
23 */
caryclark@google.com639df892012-01-10 21:46:10 +000024 double denom = byLen * axLen - ayLen * bxLen;
caryclark@google.comc6825902012-02-03 22:07:47 +000025 if (approximately_zero_squared(denom)) {
26 /* See if the axis intercepts match:
27 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
28 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
29 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
30 */
31 if (approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
32 axLen * b[0].y - ayLen * b[0].x)) {
33 const double* aPtr;
34 const double* bPtr;
35 if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
36 aPtr = &a[0].x;
37 bPtr = &b[0].x;
caryclark@google.com639df892012-01-10 21:46:10 +000038 } else {
caryclark@google.comc6825902012-02-03 22:07:47 +000039 aPtr = &a[0].y;
40 bPtr = &b[0].y;
caryclark@google.com639df892012-01-10 21:46:10 +000041 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +000042 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comc6825902012-02-03 22:07:47 +000043 double aMin = aPtr[0];
44 double aMax = aPtr[2];
45 double bMin = bPtr[0];
46 double bMax = bPtr[2];
47 if (aMin > aMax) {
48 std::swap(aMin, aMax);
caryclark@google.com639df892012-01-10 21:46:10 +000049 }
caryclark@google.comc6825902012-02-03 22:07:47 +000050 if (bMin > bMax) {
51 std::swap(bMin, bMax);
52 }
53 if (aMax < bMin || bMax < aMin) {
54 return 0;
55 }
56 if (aRange) {
57 aRange[0] = bMin <= aMin ? 0 : (bMin - aMin) / (aMax - aMin);
58 aRange[1] = bMax >= aMax ? 1 : (bMax - aMin) / (aMax - aMin);
59 }
caryclark@google.com495f8e42012-05-31 13:13:11 +000060 int bIn = (aPtr[0] - aPtr[2]) * (bPtr[0] - bPtr[2]) < 0;
caryclark@google.comc6825902012-02-03 22:07:47 +000061 if (bRange) {
caryclark@google.com495f8e42012-05-31 13:13:11 +000062 bRange[bIn] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
63 bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
caryclark@google.comc6825902012-02-03 22:07:47 +000064 }
caryclark@google.com4917f172012-03-05 22:01:21 +000065 return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
caryclark@google.com8dcf1142012-07-02 20:27:02 +000066 #else
67 assert(aRange);
68 assert(bRange);
69 double a0 = aPtr[0];
70 double a1 = aPtr[2];
71 double b0 = bPtr[0];
72 double b1 = bPtr[2];
73 double at0 = (a0 - b0) / (a0 - a1);
74 double at1 = (a0 - b1) / (a0 - a1);
75 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
76 return 0;
77 }
78 aRange[0] = std::max(std::min(at0, 1.0), 0.0);
79 aRange[1] = std::max(std::min(at1, 1.0), 0.0);
80 int bIn = (a0 - a1) * (b0 - b1) < 0;
81 bRange[bIn] = std::max(std::min((b0 - a0) / (b0 - b1), 1.0), 0.0);
82 bRange[!bIn] = std::max(std::min((b0 - a1) / (b0 - b1), 1.0), 0.0);
83 bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
84 assert((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
85 return 1 + second;
86 #endif
caryclark@google.com639df892012-01-10 21:46:10 +000087 }
caryclark@google.com639df892012-01-10 21:46:10 +000088 }
89 double ab0y = a[0].y - b[0].y;
90 double ab0x = a[0].x - b[0].x;
91 double numerA = ab0y * bxLen - byLen * ab0x;
caryclark@google.com639df892012-01-10 21:46:10 +000092 if (numerA < 0 && denom > numerA || numerA > 0 && denom < numerA) {
caryclark@google.comc6825902012-02-03 22:07:47 +000093 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +000094 }
caryclark@google.comc6825902012-02-03 22:07:47 +000095 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com639df892012-01-10 21:46:10 +000096 if (numerB < 0 && denom > numerB || numerB > 0 && denom < numerB) {
caryclark@google.comc6825902012-02-03 22:07:47 +000097 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +000098 }
caryclark@google.com639df892012-01-10 21:46:10 +000099 /* Is the intersection along the the segments */
caryclark@google.comc6825902012-02-03 22:07:47 +0000100 if (aRange) {
101 aRange[0] = numerA / denom;
102 }
103 if (bRange) {
104 bRange[0] = numerB / denom;
105 }
106 return 1;
caryclark@google.com639df892012-01-10 21:46:10 +0000107}
108
caryclark@google.comc6825902012-02-03 22:07:47 +0000109int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
110 double min = line[0].y;
111 double max = line[1].y;
112 if (min > max) {
113 std::swap(min, max);
114 }
115 if (min > y || max < y) {
116 return 0;
117 }
118 if (approximately_equal(min, max)) {
119 tRange[0] = 0;
120 tRange[1] = 1;
121 return 2;
122 }
123 tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
124 return 1;
125}
caryclark@google.com639df892012-01-10 21:46:10 +0000126
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000127// OPTIMIZATION Given: dy = line[1].y - line[0].y
128// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
129// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
130// Assuming that dy is always > 0, the line segment intercepts if:
131// left * dy <= xIntercept * dy <= right * dy
132// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
133// (clever as this is, it does not give us the t value, so may be useful only
134// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
135int horizontalLineIntersect(const _Line& line, double left, double right,
136 double y, double tRange[2]) {
137 int result = horizontalIntersect(line, y, tRange);
138 if (result != 1) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000139 // FIXME: this is incorrect if result == 2
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000140 return result;
141 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000142 double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
143 if (xIntercept > right || xIntercept < left) {
144 return 0;
145 }
146 return result;
147}
148
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000149int horizontalIntersect(const _Line& line, double left, double right,
150 double y, bool flipped, Intersections& intersections) {
151 int result = horizontalIntersect(line, y, intersections.fT[0]);
152 switch (result) {
153 case 0:
154 break;
155 case 1: {
156 double xIntercept = line[0].x + intersections.fT[0][0]
157 * (line[1].x - line[0].x);
158 if (xIntercept > right || xIntercept < left) {
159 return 0;
160 }
161 intersections.fT[1][0] = (xIntercept - left) / (right - left);
162 break;
163 }
164 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000165 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000166 double lineL = line[0].x;
167 double lineR = line[1].x;
168 if (lineL > lineR) {
169 std::swap(lineL, lineR);
170 }
171 double overlapL = std::max(left, lineL);
172 double overlapR = std::min(right, lineR);
173 if (overlapL > overlapR) {
174 return 0;
175 }
176 if (overlapL == overlapR) {
177 result = 1;
178 }
179 intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
180 intersections.fT[1][0] = (overlapL - left) / (right - left);
181 if (result > 1) {
182 intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
183 intersections.fT[1][1] = (overlapR - left) / (right - left);
184 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000185 #else
186 double a0 = line[0].x;
187 double a1 = line[1].x;
188 double b0 = flipped ? right : left;
189 double b1 = flipped ? left : right;
190 // FIXME: share common code below
191 double at0 = (a0 - b0) / (a0 - a1);
192 double at1 = (a0 - b1) / (a0 - a1);
193 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
194 return 0;
195 }
196 intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
197 intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
198 int bIn = (a0 - a1) * (b0 - b1) < 0;
199 intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
200 1.0), 0.0);
201 intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
202 1.0), 0.0);
203 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
204 > FLT_EPSILON;
205 assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
206 <= FLT_EPSILON) ^ second);
207 return 1 + second;
208 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000209 break;
210 }
211 if (flipped) {
212 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
213 for (int index = 0; index < result; ++index) {
214 intersections.fT[1][index] = 1 - intersections.fT[1][index];
215 }
216 }
217 return result;
218}
219
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000220static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000221 double min = line[0].x;
222 double max = line[1].x;
223 if (min > max) {
224 std::swap(min, max);
225 }
226 if (min > x || max < x) {
227 return 0;
228 }
229 if (approximately_equal(min, max)) {
230 tRange[0] = 0;
231 tRange[1] = 1;
232 return 2;
233 }
234 tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
235 return 1;
236}
237
238int verticalIntersect(const _Line& line, double top, double bottom,
239 double x, bool flipped, Intersections& intersections) {
240 int result = verticalIntersect(line, x, intersections.fT[0]);
241 switch (result) {
242 case 0:
243 break;
244 case 1: {
245 double yIntercept = line[0].y + intersections.fT[0][0]
246 * (line[1].y - line[0].y);
247 if (yIntercept > bottom || yIntercept < top) {
248 return 0;
249 }
250 intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
251 break;
252 }
253 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000254 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000255 double lineT = line[0].y;
256 double lineB = line[1].y;
257 if (lineT > lineB) {
258 std::swap(lineT, lineB);
259 }
260 double overlapT = std::max(top, lineT);
261 double overlapB = std::min(bottom, lineB);
262 if (overlapT > overlapB) {
263 return 0;
264 }
265 if (overlapT == overlapB) {
266 result = 1;
267 }
268 intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
269 intersections.fT[1][0] = (overlapT - top) / (bottom - top);
270 if (result > 1) {
271 intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
272 intersections.fT[1][1] = (overlapB - top) / (bottom - top);
273 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000274 #else
275 double a0 = line[0].y;
276 double a1 = line[1].y;
277 double b0 = flipped ? bottom : top;
278 double b1 = flipped ? top : bottom;
279 // FIXME: share common code above
280 double at0 = (a0 - b0) / (a0 - a1);
281 double at1 = (a0 - b1) / (a0 - a1);
282 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
283 return 0;
284 }
285 intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
286 intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
287 int bIn = (a0 - a1) * (b0 - b1) < 0;
288 intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
289 1.0), 0.0);
290 intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
291 1.0), 0.0);
292 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
293 > FLT_EPSILON;
294 assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
295 <= FLT_EPSILON) ^ second);
296 return 1 + second;
297 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000298 break;
299 }
300 if (flipped) {
301 // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
302 for (int index = 0; index < result; ++index) {
303 intersections.fT[1][index] = 1 - intersections.fT[1][index];
304 }
305 }
306 return result;
307}
308
caryclark@google.com639df892012-01-10 21:46:10 +0000309// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
310// 4 subs, 2 muls, 1 cmp
311static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
312 return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
313}
314
315// 16 subs, 8 muls, 6 cmps
316bool testIntersect(const _Line& a, const _Line& b) {
317 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
318 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
319}