blob: 6ab393684fe89d5421a29b49ccbe078238c94dfe [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.com639df892012-01-10 21:46:10 +000046 double ab0y = a[0].y - b[0].y;
47 double ab0x = a[0].x - b[0].x;
48 double numerA = ab0y * bxLen - byLen * ab0x;
caryclark@google.comc6825902012-02-03 22:07:47 +000049 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.comf9502d72013-02-04 14:06:49 +000050 bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom < numerA)
51 || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB);
52 numerA /= denom;
53 numerB /= denom;
caryclark@google.combeda3892013-02-07 13:13:41 +000054 if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA)
55 && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA)
56 && !sk_double_isnan(numerB)) {
caryclark@google.comf9502d72013-02-04 14:06:49 +000057 if (mayNotOverlap) {
58 return 0;
59 }
60 if (aRange) {
61 aRange[0] = numerA;
62 }
63 if (bRange) {
64 bRange[0] = numerB;
65 }
66 return 1;
67 }
68 /* See if the axis intercepts match:
69 ay - ax * ayLen / axLen == by - bx * ayLen / axLen
70 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
71 axLen * ay - ax * ayLen == axLen * by - bx * ayLen
72 */
73 // FIXME: need to use AlmostEqualUlps variant instead
74 if (!approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
75 axLen * b[0].y - ayLen * b[0].x)) {
caryclark@google.comc6825902012-02-03 22:07:47 +000076 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +000077 }
caryclark@google.comf9502d72013-02-04 14:06:49 +000078 const double* aPtr;
79 const double* bPtr;
80 if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
81 aPtr = &a[0].x;
82 bPtr = &b[0].x;
83 } else {
84 aPtr = &a[0].y;
85 bPtr = &b[0].y;
caryclark@google.comc6825902012-02-03 22:07:47 +000086 }
caryclark@google.comf9502d72013-02-04 14:06:49 +000087 SkASSERT(aRange);
88 SkASSERT(bRange);
89 double a0 = aPtr[0];
90 double a1 = aPtr[2];
91 double b0 = bPtr[0];
92 double b1 = bPtr[2];
93 // OPTIMIZATION: restructure to reject before the divide
94 // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1))
95 // (except efficient)
96 double aDenom = a0 - a1;
97 if (approximately_zero(aDenom)) {
98 if (!between(b0, a0, b1)) {
99 return 0;
100 }
101 aRange[0] = aRange[1] = 0;
102 } else {
103 double at0 = (a0 - b0) / aDenom;
104 double at1 = (a0 - b1) / aDenom;
105 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
106 return 0;
107 }
108 aRange[0] = SkTMax(SkTMin(at0, 1.0), 0.0);
109 aRange[1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.comc6825902012-02-03 22:07:47 +0000110 }
caryclark@google.comf9502d72013-02-04 14:06:49 +0000111 double bDenom = b0 - b1;
112 if (approximately_zero(bDenom)) {
113 bRange[0] = bRange[1] = 0;
114 } else {
115 int bIn = aDenom * bDenom < 0;
116 bRange[bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0);
117 bRange[!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0);
118 }
119 bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
120 SkASSERT((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
121 return 1 + second;
caryclark@google.com639df892012-01-10 21:46:10 +0000122}
123
caryclark@google.comc6825902012-02-03 22:07:47 +0000124int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
125 double min = line[0].y;
126 double max = line[1].y;
127 if (min > max) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000128 SkTSwap(min, max);
caryclark@google.comc6825902012-02-03 22:07:47 +0000129 }
130 if (min > y || max < y) {
131 return 0;
132 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000133 if (AlmostEqualUlps(min, max)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000134 tRange[0] = 0;
135 tRange[1] = 1;
136 return 2;
137 }
138 tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
139 return 1;
140}
caryclark@google.com639df892012-01-10 21:46:10 +0000141
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000142// OPTIMIZATION Given: dy = line[1].y - line[0].y
143// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
144// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
145// Assuming that dy is always > 0, the line segment intercepts if:
146// left * dy <= xIntercept * dy <= right * dy
147// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
148// (clever as this is, it does not give us the t value, so may be useful only
149// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
150int horizontalLineIntersect(const _Line& line, double left, double right,
151 double y, double tRange[2]) {
152 int result = horizontalIntersect(line, y, tRange);
153 if (result != 1) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000154 // FIXME: this is incorrect if result == 2
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000155 return result;
156 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000157 double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
158 if (xIntercept > right || xIntercept < left) {
159 return 0;
160 }
161 return result;
162}
163
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000164int horizontalIntersect(const _Line& line, double left, double right,
165 double y, bool flipped, Intersections& intersections) {
166 int result = horizontalIntersect(line, y, intersections.fT[0]);
167 switch (result) {
168 case 0:
169 break;
170 case 1: {
171 double xIntercept = line[0].x + intersections.fT[0][0]
172 * (line[1].x - line[0].x);
173 if (xIntercept > right || xIntercept < left) {
174 return 0;
175 }
176 intersections.fT[1][0] = (xIntercept - left) / (right - left);
177 break;
178 }
179 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000180 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000181 double lineL = line[0].x;
182 double lineR = line[1].x;
183 if (lineL > lineR) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000184 SkTSwap(lineL, lineR);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000185 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000186 double overlapL = SkTMax(left, lineL);
187 double overlapR = SkTMin(right, lineR);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000188 if (overlapL > overlapR) {
189 return 0;
190 }
191 if (overlapL == overlapR) {
192 result = 1;
193 }
194 intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
195 intersections.fT[1][0] = (overlapL - left) / (right - left);
196 if (result > 1) {
197 intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
198 intersections.fT[1][1] = (overlapR - left) / (right - left);
199 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000200 #else
201 double a0 = line[0].x;
202 double a1 = line[1].x;
203 double b0 = flipped ? right : left;
204 double b1 = flipped ? left : right;
205 // FIXME: share common code below
206 double at0 = (a0 - b0) / (a0 - a1);
207 double at1 = (a0 - b1) / (a0 - a1);
208 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
209 return 0;
210 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000211 intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
212 intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000213 int bIn = (a0 - a1) * (b0 - b1) < 0;
caryclark@google.comaa358312013-01-29 20:28:49 +0000214 intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000215 1.0), 0.0);
caryclark@google.comaa358312013-01-29 20:28:49 +0000216 intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000217 1.0), 0.0);
218 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
219 > FLT_EPSILON;
caryclark@google.comaa358312013-01-29 20:28:49 +0000220 SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000221 <= FLT_EPSILON) ^ second);
222 return 1 + second;
223 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000224 break;
225 }
226 if (flipped) {
227 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
228 for (int index = 0; index < result; ++index) {
229 intersections.fT[1][index] = 1 - intersections.fT[1][index];
230 }
231 }
232 return result;
233}
234
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000235static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000236 double min = line[0].x;
237 double max = line[1].x;
238 if (min > max) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000239 SkTSwap(min, max);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000240 }
241 if (min > x || max < x) {
242 return 0;
243 }
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000244 if (AlmostEqualUlps(min, max)) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000245 tRange[0] = 0;
246 tRange[1] = 1;
247 return 2;
248 }
249 tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
250 return 1;
251}
252
253int verticalIntersect(const _Line& line, double top, double bottom,
254 double x, bool flipped, Intersections& intersections) {
255 int result = verticalIntersect(line, x, intersections.fT[0]);
256 switch (result) {
257 case 0:
258 break;
259 case 1: {
260 double yIntercept = line[0].y + intersections.fT[0][0]
261 * (line[1].y - line[0].y);
262 if (yIntercept > bottom || yIntercept < top) {
263 return 0;
264 }
265 intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
266 break;
267 }
268 case 2:
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000269 #if 0 // sorting edges fails to preserve original direction
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000270 double lineT = line[0].y;
271 double lineB = line[1].y;
272 if (lineT > lineB) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000273 SkTSwap(lineT, lineB);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000274 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000275 double overlapT = SkTMax(top, lineT);
276 double overlapB = SkTMin(bottom, lineB);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000277 if (overlapT > overlapB) {
278 return 0;
279 }
280 if (overlapT == overlapB) {
281 result = 1;
282 }
283 intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
284 intersections.fT[1][0] = (overlapT - top) / (bottom - top);
285 if (result > 1) {
286 intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
287 intersections.fT[1][1] = (overlapB - top) / (bottom - top);
288 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000289 #else
290 double a0 = line[0].y;
291 double a1 = line[1].y;
292 double b0 = flipped ? bottom : top;
293 double b1 = flipped ? top : bottom;
294 // FIXME: share common code above
295 double at0 = (a0 - b0) / (a0 - a1);
296 double at1 = (a0 - b1) / (a0 - a1);
297 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
298 return 0;
299 }
caryclark@google.comaa358312013-01-29 20:28:49 +0000300 intersections.fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0);
301 intersections.fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000302 int bIn = (a0 - a1) * (b0 - b1) < 0;
caryclark@google.comaa358312013-01-29 20:28:49 +0000303 intersections.fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000304 1.0), 0.0);
caryclark@google.comaa358312013-01-29 20:28:49 +0000305 intersections.fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1),
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000306 1.0), 0.0);
307 bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
308 > FLT_EPSILON;
caryclark@google.comaa358312013-01-29 20:28:49 +0000309 SkASSERT((fabs(intersections.fT[1][0] - intersections.fT[1][1])
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000310 <= FLT_EPSILON) ^ second);
311 return 1 + second;
312 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000313 break;
314 }
315 if (flipped) {
316 // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
317 for (int index = 0; index < result; ++index) {
318 intersections.fT[1][index] = 1 - intersections.fT[1][index];
319 }
320 }
321 return result;
322}
323
caryclark@google.com639df892012-01-10 21:46:10 +0000324// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
325// 4 subs, 2 muls, 1 cmp
326static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000327 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 +0000328}
329
330// 16 subs, 8 muls, 6 cmps
331bool testIntersect(const _Line& a, const _Line& b) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000332 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
caryclark@google.com639df892012-01-10 21:46:10 +0000333 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
334}