blob: 31e857ed414c69fc23a86ce14dffbf6f94f6d46d [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.comc6825902012-02-03 22:07:47 +000042 double aMin = aPtr[0];
43 double aMax = aPtr[2];
44 double bMin = bPtr[0];
45 double bMax = bPtr[2];
46 if (aMin > aMax) {
47 std::swap(aMin, aMax);
caryclark@google.com639df892012-01-10 21:46:10 +000048 }
caryclark@google.comc6825902012-02-03 22:07:47 +000049 if (bMin > bMax) {
50 std::swap(bMin, bMax);
51 }
52 if (aMax < bMin || bMax < aMin) {
53 return 0;
54 }
55 if (aRange) {
56 aRange[0] = bMin <= aMin ? 0 : (bMin - aMin) / (aMax - aMin);
57 aRange[1] = bMax >= aMax ? 1 : (bMax - aMin) / (aMax - aMin);
58 }
caryclark@google.com495f8e42012-05-31 13:13:11 +000059 int bIn = (aPtr[0] - aPtr[2]) * (bPtr[0] - bPtr[2]) < 0;
caryclark@google.comc6825902012-02-03 22:07:47 +000060 if (bRange) {
caryclark@google.com495f8e42012-05-31 13:13:11 +000061 bRange[bIn] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
62 bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
caryclark@google.comc6825902012-02-03 22:07:47 +000063 }
caryclark@google.com4917f172012-03-05 22:01:21 +000064 return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
caryclark@google.com639df892012-01-10 21:46:10 +000065 }
caryclark@google.com639df892012-01-10 21:46:10 +000066 }
67 double ab0y = a[0].y - b[0].y;
68 double ab0x = a[0].x - b[0].x;
69 double numerA = ab0y * bxLen - byLen * ab0x;
caryclark@google.com639df892012-01-10 21:46:10 +000070 if (numerA < 0 && denom > numerA || numerA > 0 && denom < numerA) {
caryclark@google.comc6825902012-02-03 22:07:47 +000071 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +000072 }
caryclark@google.comc6825902012-02-03 22:07:47 +000073 double numerB = ab0y * axLen - ayLen * ab0x;
caryclark@google.com639df892012-01-10 21:46:10 +000074 if (numerB < 0 && denom > numerB || numerB > 0 && denom < numerB) {
caryclark@google.comc6825902012-02-03 22:07:47 +000075 return 0;
caryclark@google.com639df892012-01-10 21:46:10 +000076 }
caryclark@google.com639df892012-01-10 21:46:10 +000077 /* Is the intersection along the the segments */
caryclark@google.comc6825902012-02-03 22:07:47 +000078 if (aRange) {
79 aRange[0] = numerA / denom;
80 }
81 if (bRange) {
82 bRange[0] = numerB / denom;
83 }
84 return 1;
caryclark@google.com639df892012-01-10 21:46:10 +000085}
86
caryclark@google.comc6825902012-02-03 22:07:47 +000087int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
88 double min = line[0].y;
89 double max = line[1].y;
90 if (min > max) {
91 std::swap(min, max);
92 }
93 if (min > y || max < y) {
94 return 0;
95 }
96 if (approximately_equal(min, max)) {
97 tRange[0] = 0;
98 tRange[1] = 1;
99 return 2;
100 }
101 tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
102 return 1;
103}
caryclark@google.com639df892012-01-10 21:46:10 +0000104
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000105// OPTIMIZATION Given: dy = line[1].y - line[0].y
106// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
107// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
108// Assuming that dy is always > 0, the line segment intercepts if:
109// left * dy <= xIntercept * dy <= right * dy
110// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
111// (clever as this is, it does not give us the t value, so may be useful only
112// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
113int horizontalLineIntersect(const _Line& line, double left, double right,
114 double y, double tRange[2]) {
115 int result = horizontalIntersect(line, y, tRange);
116 if (result != 1) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000117 // FIXME: this is incorrect if result == 2
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000118 return result;
119 }
caryclark@google.com2e7f4c82012-03-20 21:11:59 +0000120 double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
121 if (xIntercept > right || xIntercept < left) {
122 return 0;
123 }
124 return result;
125}
126
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000127int horizontalIntersect(const _Line& line, double left, double right,
128 double y, bool flipped, Intersections& intersections) {
129 int result = horizontalIntersect(line, y, intersections.fT[0]);
130 switch (result) {
131 case 0:
132 break;
133 case 1: {
134 double xIntercept = line[0].x + intersections.fT[0][0]
135 * (line[1].x - line[0].x);
136 if (xIntercept > right || xIntercept < left) {
137 return 0;
138 }
139 intersections.fT[1][0] = (xIntercept - left) / (right - left);
140 break;
141 }
142 case 2:
143 double lineL = line[0].x;
144 double lineR = line[1].x;
145 if (lineL > lineR) {
146 std::swap(lineL, lineR);
147 }
148 double overlapL = std::max(left, lineL);
149 double overlapR = std::min(right, lineR);
150 if (overlapL > overlapR) {
151 return 0;
152 }
153 if (overlapL == overlapR) {
154 result = 1;
155 }
156 intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
157 intersections.fT[1][0] = (overlapL - left) / (right - left);
158 if (result > 1) {
159 intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
160 intersections.fT[1][1] = (overlapR - left) / (right - left);
161 }
162 break;
163 }
164 if (flipped) {
165 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
166 for (int index = 0; index < result; ++index) {
167 intersections.fT[1][index] = 1 - intersections.fT[1][index];
168 }
169 }
170 return result;
171}
172
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000173static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000174 double min = line[0].x;
175 double max = line[1].x;
176 if (min > max) {
177 std::swap(min, max);
178 }
179 if (min > x || max < x) {
180 return 0;
181 }
182 if (approximately_equal(min, max)) {
183 tRange[0] = 0;
184 tRange[1] = 1;
185 return 2;
186 }
187 tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
188 return 1;
189}
190
191int verticalIntersect(const _Line& line, double top, double bottom,
192 double x, bool flipped, Intersections& intersections) {
193 int result = verticalIntersect(line, x, intersections.fT[0]);
194 switch (result) {
195 case 0:
196 break;
197 case 1: {
198 double yIntercept = line[0].y + intersections.fT[0][0]
199 * (line[1].y - line[0].y);
200 if (yIntercept > bottom || yIntercept < top) {
201 return 0;
202 }
203 intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
204 break;
205 }
206 case 2:
207 double lineT = line[0].y;
208 double lineB = line[1].y;
209 if (lineT > lineB) {
210 std::swap(lineT, lineB);
211 }
212 double overlapT = std::max(top, lineT);
213 double overlapB = std::min(bottom, lineB);
214 if (overlapT > overlapB) {
215 return 0;
216 }
217 if (overlapT == overlapB) {
218 result = 1;
219 }
220 intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
221 intersections.fT[1][0] = (overlapT - top) / (bottom - top);
222 if (result > 1) {
223 intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
224 intersections.fT[1][1] = (overlapB - top) / (bottom - top);
225 }
226 break;
227 }
228 if (flipped) {
229 // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
230 for (int index = 0; index < result; ++index) {
231 intersections.fT[1][index] = 1 - intersections.fT[1][index];
232 }
233 }
234 return result;
235}
236
caryclark@google.com639df892012-01-10 21:46:10 +0000237// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
238// 4 subs, 2 muls, 1 cmp
239static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
240 return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
241}
242
243// 16 subs, 8 muls, 6 cmps
244bool testIntersect(const _Line& a, const _Line& b) {
245 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
246 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
247}