blob: f5501db11a9b5ec9f46c5a4578ee7d8498d29da5 [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.com639df892012-01-10 21:46:10 +000010
caryclark@google.com73ca6242013-01-17 21:02:47 +000011/* Determine the intersection point of two lines. This assumes the lines are not parallel,
12 and that that the lines are infinite.
13 From http://en.wikipedia.org/wiki/Line-line_intersection
14 */
15void lineIntersect(const _Line& a, const _Line& b, _Point& p) {
16 double axLen = a[1].x - a[0].x;
17 double ayLen = a[1].y - a[0].y;
18 double bxLen = b[1].x - b[0].x;
19 double byLen = b[1].y - b[0].y;
20 double denom = byLen * axLen - ayLen * bxLen;
caryclark@google.comaa358312013-01-29 20:28:49 +000021 SkASSERT(denom);
caryclark@google.com73ca6242013-01-17 21:02:47 +000022 double term1 = a[1].x * a[0].y - a[1].y * a[0].x;
23 double term2 = b[1].x * b[0].y - b[1].y * b[0].x;
24 p.x = (term1 * bxLen - axLen * term2) / denom;
25 p.y = (term1 * byLen - ayLen * term2) / denom;
26}
caryclark@google.com639df892012-01-10 21:46:10 +000027
caryclark@google.com639df892012-01-10 21:46:10 +000028/*
29 Determine the intersection point of two line segments
30 Return FALSE if the lines don't intersect
31 from: http://paulbourke.net/geometry/lineline2d/
caryclark@google.com73ca6242013-01-17 21:02:47 +000032 */
caryclark@google.com639df892012-01-10 21:46:10 +000033
caryclark@google.comc6825902012-02-03 22:07:47 +000034int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]) {
caryclark@google.com639df892012-01-10 21:46:10 +000035 double axLen = a[1].x - a[0].x;
36 double ayLen = a[1].y - a[0].y;
37 double bxLen = b[1].x - b[0].x;
38 double byLen = b[1].y - b[0].y;
rmistry@google.comd6176b02012-08-23 18:14:13 +000039 /* Slopes match when denom goes to zero:
caryclark@google.comc6825902012-02-03 22:07:47 +000040 axLen / ayLen == bxLen / byLen
41 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
42 byLen * axLen == ayLen * bxLen
43 byLen * axLen - ayLen * bxLen == 0 ( == denom )
44 */
caryclark@google.com73ca6242013-01-17 21:02:47 +000045 double denom = byLen * axLen - ayLen * bxLen;
caryclark@google.comd1688742012-09-18 20:08:37 +000046 if (approximately_zero(denom)) {
caryclark@google.comc6825902012-02-03 22:07:47 +000047 /* See if the axis intercepts match:
48 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
49 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
50 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
51 */
caryclark@google.com6d0032a2013-01-04 19:41:13 +000052 // FIXME: need to use AlmostEqualUlps variant instead
caryclark@google.comc6825902012-02-03 22:07:47 +000053 if (approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
54 axLen * b[0].y - ayLen * b[0].x)) {
55 const double* aPtr;
56 const double* bPtr;
57 if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
58 aPtr = &a[0].x;
59 bPtr = &b[0].x;
caryclark@google.com639df892012-01-10 21:46:10 +000060 } else {
caryclark@google.comc6825902012-02-03 22:07:47 +000061 aPtr = &a[0].y;
62 bPtr = &b[0].y;
caryclark@google.com639df892012-01-10 21:46:10 +000063 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +000064 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comc6825902012-02-03 22:07:47 +000065 double aMin = aPtr[0];
66 double aMax = aPtr[2];
67 double bMin = bPtr[0];
68 double bMax = bPtr[2];
69 if (aMin > aMax) {
caryclark@google.comaa358312013-01-29 20:28:49 +000070 SkTSwap(aMin, aMax);
caryclark@google.com639df892012-01-10 21:46:10 +000071 }
caryclark@google.comc6825902012-02-03 22:07:47 +000072 if (bMin > bMax) {
caryclark@google.comaa358312013-01-29 20:28:49 +000073 SkTSwap(bMin, bMax);
caryclark@google.comc6825902012-02-03 22:07:47 +000074 }
75 if (aMax < bMin || bMax < aMin) {
76 return 0;
77 }
78 if (aRange) {
79 aRange[0] = bMin <= aMin ? 0 : (bMin - aMin) / (aMax - aMin);
80 aRange[1] = bMax >= aMax ? 1 : (bMax - aMin) / (aMax - aMin);
81 }
caryclark@google.com495f8e42012-05-31 13:13:11 +000082 int bIn = (aPtr[0] - aPtr[2]) * (bPtr[0] - bPtr[2]) < 0;
caryclark@google.comc6825902012-02-03 22:07:47 +000083 if (bRange) {
caryclark@google.com495f8e42012-05-31 13:13:11 +000084 bRange[bIn] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
85 bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
caryclark@google.comc6825902012-02-03 22:07:47 +000086 }
caryclark@google.com4917f172012-03-05 22:01:21 +000087 return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
caryclark@google.com8dcf1142012-07-02 20:27:02 +000088 #else
caryclark@google.comaa358312013-01-29 20:28:49 +000089 SkASSERT(aRange);
90 SkASSERT(bRange);
caryclark@google.com8dcf1142012-07-02 20:27:02 +000091 double a0 = aPtr[0];
92 double a1 = aPtr[2];
93 double b0 = bPtr[0];
94 double b1 = bPtr[2];
caryclark@google.comdb0b3e02012-12-21 21:34:36 +000095 // OPTIMIZATION: restructure to reject before the divide
skia.committer@gmail.comb89a03c2012-12-22 02:02:33 +000096 // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
caryclark@google.comdb0b3e02012-12-21 21:34:36 +000097 // (except efficient)
caryclark@google.com8dcf1142012-07-02 20:27:02 +000098 double at0 = (a0 - b0) / (a0 - a1);
99 double at1 = (a0 - b1) / (a0 - a1);
100 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
101 return 0;
102 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000103 aRange[0] = SkTMax(SkTMin(at0, 1.0), 0.0);
104 aRange[1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000105 int bIn = (a0 - a1) * (b0 - b1) < 0;
caryclark@google.comaa358312013-01-29 20:28:49 +0000106 double bDenom = b0 - b1;
107 if (approximately_zero(bDenom)) {
108 bRange[0] = bRange[1] = 0;
109 } else {
110 bRange[bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0);
111 bRange[!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0);
112 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000113 bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
caryclark@google.comaa358312013-01-29 20:28:49 +0000114 SkASSERT((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000115 return 1 + second;
116 #endif
caryclark@google.com639df892012-01-10 21:46:10 +0000117 }
caryclark@google.com639df892012-01-10 21:46:10 +0000118 }
119 double ab0y = a[0].y - b[0].y;
120 double ab0x = a[0].x - b[0].x;
121 double numerA = ab0y * bxLen - byLen * ab0x;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000122 if ((numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000123 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +0000124 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000125 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com9f3e9a52012-12-10 12:50:53 +0000126 if ((numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000127 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +0000128 }
caryclark@google.com639df892012-01-10 21:46:10 +0000129 /* Is the intersection along the the segments */
caryclark@google.comc6825902012-02-03 22:07:47 +0000130 if (aRange) {
131 aRange[0] = numerA / denom;
132 }
133 if (bRange) {
134 bRange[0] = numerB / denom;
135 }
136 return 1;
caryclark@google.com639df892012-01-10 21:46:10 +0000137}
138
caryclark@google.comc6825902012-02-03 22:07:47 +0000139int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
140 double min = line[0].y;
141 double max = line[1].y;
142 if (min > max) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000143 SkTSwap(min, max);
caryclark@google.comc6825902012-02-03 22:07:47 +0000144 }
145 if (min > y || max < y) {
146 return 0;
147 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000148 if (AlmostEqualUlps(min, max)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000149 tRange[0] = 0;
150 tRange[1] = 1;
151 return 2;
152 }
153 tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
154 return 1;
155}
caryclark@google.com639df892012-01-10 21:46:10 +0000156
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000157// OPTIMIZATION Given: dy = line[1].y - line[0].y
158// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
159// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
160// Assuming that dy is always > 0, the line segment intercepts if:
161// left * dy <= xIntercept * dy <= right * dy
162// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
163// (clever as this is, it does not give us the t value, so may be useful only
164// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
165int horizontalLineIntersect(const _Line& line, double left, double right,
166 double y, double tRange[2]) {
167 int result = horizontalIntersect(line, y, tRange);
168 if (result != 1) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000169 // FIXME: this is incorrect if result == 2
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000170 return result;
171 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000172 double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
173 if (xIntercept > right || xIntercept < left) {
174 return 0;
175 }
176 return result;
177}
178
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000179int horizontalIntersect(const _Line& line, double left, double right,
180 double y, bool flipped, Intersections& intersections) {
181 int result = horizontalIntersect(line, y, intersections.fT[0]);
182 switch (result) {
183 case 0:
184 break;
185 case 1: {
186 double xIntercept = line[0].x + intersections.fT[0][0]
187 * (line[1].x - line[0].x);
188 if (xIntercept > right || xIntercept < left) {
189 return 0;
190 }
191 intersections.fT[1][0] = (xIntercept - left) / (right - left);
192 break;
193 }
194 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000195 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000196 double lineL = line[0].x;
197 double lineR = line[1].x;
198 if (lineL > lineR) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000199 SkTSwap(lineL, lineR);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000200 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000201 double overlapL = SkTMax(left, lineL);
202 double overlapR = SkTMin(right, lineR);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000203 if (overlapL > overlapR) {
204 return 0;
205 }
206 if (overlapL == overlapR) {
207 result = 1;
208 }
209 intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
210 intersections.fT[1][0] = (overlapL - left) / (right - left);
211 if (result > 1) {
212 intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
213 intersections.fT[1][1] = (overlapR - left) / (right - left);
214 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000215 #else
216 double a0 = line[0].x;
217 double a1 = line[1].x;
218 double b0 = flipped ? right : left;
219 double b1 = flipped ? left : right;
220 // FIXME: share common code below
221 double at0 = (a0 - b0) / (a0 - a1);
222 double at1 = (a0 - b1) / (a0 - a1);
223 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
224 return 0;
225 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000226 intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
227 intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000228 int bIn = (a0 - a1) * (b0 - b1) < 0;
caryclark@google.comaa358312013-01-29 20:28:49 +0000229 intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000230 1.0), 0.0);
caryclark@google.comaa358312013-01-29 20:28:49 +0000231 intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000232 1.0), 0.0);
233 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
234 > FLT_EPSILON;
caryclark@google.comaa358312013-01-29 20:28:49 +0000235 SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000236 <= FLT_EPSILON) ^ second);
237 return 1 + second;
238 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000239 break;
240 }
241 if (flipped) {
242 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
243 for (int index = 0; index < result; ++index) {
244 intersections.fT[1][index] = 1 - intersections.fT[1][index];
245 }
246 }
247 return result;
248}
249
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000250static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000251 double min = line[0].x;
252 double max = line[1].x;
253 if (min > max) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000254 SkTSwap(min, max);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000255 }
256 if (min > x || max < x) {
257 return 0;
258 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000259 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000260 tRange[0] = 0;
261 tRange[1] = 1;
262 return 2;
263 }
264 tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
265 return 1;
266}
267
268int verticalIntersect(const _Line& line, double top, double bottom,
269 double x, bool flipped, Intersections& intersections) {
270 int result = verticalIntersect(line, x, intersections.fT[0]);
271 switch (result) {
272 case 0:
273 break;
274 case 1: {
275 double yIntercept = line[0].y + intersections.fT[0][0]
276 * (line[1].y - line[0].y);
277 if (yIntercept > bottom || yIntercept < top) {
278 return 0;
279 }
280 intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
281 break;
282 }
283 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000284 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000285 double lineT = line[0].y;
286 double lineB = line[1].y;
287 if (lineT > lineB) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000288 SkTSwap(lineT, lineB);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000289 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000290 double overlapT = SkTMax(top, lineT);
291 double overlapB = SkTMin(bottom, lineB);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292 if (overlapT > overlapB) {
293 return 0;
294 }
295 if (overlapT == overlapB) {
296 result = 1;
297 }
298 intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
299 intersections.fT[1][0] = (overlapT - top) / (bottom - top);
300 if (result > 1) {
301 intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
302 intersections.fT[1][1] = (overlapB - top) / (bottom - top);
303 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000304 #else
305 double a0 = line[0].y;
306 double a1 = line[1].y;
307 double b0 = flipped ? bottom : top;
308 double b1 = flipped ? top : bottom;
309 // FIXME: share common code above
310 double at0 = (a0 - b0) / (a0 - a1);
311 double at1 = (a0 - b1) / (a0 - a1);
312 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
313 return 0;
314 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000315 intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
316 intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000317 int bIn = (a0 - a1) * (b0 - b1) < 0;
caryclark@google.comaa358312013-01-29 20:28:49 +0000318 intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000319 1.0), 0.0);
caryclark@google.comaa358312013-01-29 20:28:49 +0000320 intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000321 1.0), 0.0);
322 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
323 > FLT_EPSILON;
caryclark@google.comaa358312013-01-29 20:28:49 +0000324 SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000325 <= FLT_EPSILON) ^ second);
326 return 1 + second;
327 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000328 break;
329 }
330 if (flipped) {
331 // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
332 for (int index = 0; index < result; ++index) {
333 intersections.fT[1][index] = 1 - intersections.fT[1][index];
334 }
335 }
336 return result;
337}
338
caryclark@google.com639df892012-01-10 21:46:10 +0000339// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
340// 4 subs, 2 muls, 1 cmp
341static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000342 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 +0000343}
344
345// 16 subs, 8 muls, 6 cmps
346bool testIntersect(const _Line& a, const _Line& b) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000347 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
caryclark@google.com639df892012-01-10 21:46:10 +0000348 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
349}