OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 #include "SkIntersections.h" | 7 #include "SkIntersections.h" |
8 #include "SkPathOpsLine.h" | 8 #include "SkPathOpsLine.h" |
9 | 9 |
10 /* Determine the intersection point of two lines. This assumes the lines are not
parallel, | 10 /* Determine the intersection point of two lines. This assumes the lines are not
parallel, |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 return fUsed = 0; | 68 return fUsed = 0; |
69 } | 69 } |
70 // there's no great answer for intersection points for coincident rays,
but return something | 70 // there's no great answer for intersection points for coincident rays,
but return something |
71 fT[0][0] = fT[1][0] = 0; | 71 fT[0][0] = fT[1][0] = 0; |
72 fT[1][0] = fT[1][1] = 1; | 72 fT[1][0] = fT[1][1] = 1; |
73 used = 2; | 73 used = 2; |
74 } | 74 } |
75 return computePoints(a, used); | 75 return computePoints(a, used); |
76 } | 76 } |
77 | 77 |
78 static bool checkEndPoint(double x, double y, const SkDLine& l, double* tPtr, in
t useX) { | |
79 if (!between(l[0].fX, x, l[1].fX) || !between(l[0].fY, y, l[1].fY)) { | |
80 return false; | |
81 } | |
82 double xLen = l[1].fX - l[0].fX; | |
83 double yLen = l[1].fY - l[0].fY; | |
84 if (useX < 0) { | |
85 useX = SkTAbs(xLen) > SkTAbs(yLen); | |
86 } | |
87 // OPTIMIZATION: do between test before divide | |
88 double t = useX ? (x - l[0].fX) / xLen : (y - l[0].fY) / yLen; | |
89 if (!between(0, t, 1)) { | |
90 return false; | |
91 } | |
92 double opp = useX ? (1 - t) * l[0].fY + t * l[1].fY : (1 - t) * l[0].fX + t
* l[1].fX; | |
93 if (!AlmostEqualUlps(opp, useX ? y : x)) { | |
94 return false; | |
95 } | |
96 *tPtr = t; | |
97 return true; | |
98 } | |
99 | |
100 // note that this only works if both lines are neither horizontal nor vertical | 78 // note that this only works if both lines are neither horizontal nor vertical |
101 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { | 79 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { |
102 // see if end points intersect the opposite line | 80 // see if end points intersect the opposite line |
103 double t; | 81 double t; |
104 for (int iA = 0; iA < 2; ++iA) { | 82 for (int iA = 0; iA < 2; ++iA) { |
105 if (!checkEndPoint(a[iA].fX, a[iA].fY, b, &t, -1)) { | 83 if ((t = b.exactPoint(a[iA])) >= 0) { |
106 continue; | 84 insert(iA, t, a[iA]); |
107 } | 85 } |
108 insert(iA, t, a[iA]); | |
109 } | 86 } |
110 for (int iB = 0; iB < 2; ++iB) { | 87 for (int iB = 0; iB < 2; ++iB) { |
111 if (!checkEndPoint(b[iB].fX, b[iB].fY, a, &t, -1)) { | 88 if ((t = a.exactPoint(b[iB])) >= 0) { |
112 continue; | 89 insert(t, iB, b[iB]); |
113 } | 90 } |
114 insert(t, iB, b[iB]); | |
115 } | |
116 if (used() > 0) { | |
117 SkASSERT(fUsed <= 2); | |
118 return used(); // coincident lines are returned here | |
119 } | 91 } |
120 /* Determine the intersection point of two line segments | 92 /* Determine the intersection point of two line segments |
121 Return FALSE if the lines don't intersect | 93 Return FALSE if the lines don't intersect |
122 from: http://paulbourke.net/geometry/lineline2d/ */ | 94 from: http://paulbourke.net/geometry/lineline2d/ */ |
123 double axLen = a[1].fX - a[0].fX; | 95 double axLen = a[1].fX - a[0].fX; |
124 double ayLen = a[1].fY - a[0].fY; | 96 double ayLen = a[1].fY - a[0].fY; |
125 double bxLen = b[1].fX - b[0].fX; | 97 double bxLen = b[1].fX - b[0].fX; |
126 double byLen = b[1].fY - b[0].fY; | 98 double byLen = b[1].fY - b[0].fY; |
127 /* Slopes match when denom goes to zero: | 99 /* Slopes match when denom goes to zero: |
128 axLen / ayLen == bxLen / byLen | 100 axLen / ayLen == bxLen / byLen |
129 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen | 101 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen |
130 byLen * axLen == ayLen * bxLen | 102 byLen * axLen == ayLen * bxLen |
131 byLen * axLen - ayLen * bxLen == 0 ( == denom ) | 103 byLen * axLen - ayLen * bxLen == 0 ( == denom ) |
132 */ | 104 */ |
133 double denom = byLen * axLen - ayLen * bxLen; | 105 double denom = byLen * axLen - ayLen * bxLen; |
134 double ab0y = a[0].fY - b[0].fY; | 106 if (0 != denom) { |
135 double ab0x = a[0].fX - b[0].fX; | 107 double ab0y = a[0].fY - b[0].fY; |
136 double numerA = ab0y * bxLen - byLen * ab0x; | 108 double ab0x = a[0].fX - b[0].fX; |
137 double numerB = ab0y * axLen - ayLen * ab0x; | 109 double numerA = ab0y * bxLen - byLen * ab0x; |
138 bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom
< numerA) | 110 double numerB = ab0y * axLen - ayLen * ab0x; |
139 || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB); | 111 if (between(0, numerA, denom) && between(0, numerB, denom)) { |
140 numerA /= denom; | 112 fT[0][0] = numerA / denom; |
141 numerB /= denom; | 113 fT[1][0] = numerB / denom; |
142 if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA) | 114 return computePoints(a, 1); |
143 && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA) | |
144 && !sk_double_isnan(numerB)) { | |
145 if (mayNotOverlap) { | |
146 return 0; | |
147 } | 115 } |
148 fT[0][0] = numerA; | |
149 fT[1][0] = numerB; | |
150 fPt[0] = a.xyAtT(numerA); | |
151 return computePoints(a, 1); | |
152 } | 116 } |
153 return 0; | 117 if (fAllowNear || 0 == denom) { |
| 118 for (int iA = 0; iA < 2; ++iA) { |
| 119 if ((t = b.nearPoint(a[iA])) >= 0) { |
| 120 insert(iA, t, a[iA]); |
| 121 } |
| 122 } |
| 123 for (int iB = 0; iB < 2; ++iB) { |
| 124 if ((t = a.nearPoint(b[iB])) >= 0) { |
| 125 insert(t, iB, b[iB]); |
| 126 } |
| 127 } |
| 128 } |
| 129 return fUsed; |
154 } | 130 } |
155 | 131 |
156 int SkIntersections::horizontal(const SkDLine& line, double y) { | 132 static int horizontal_coincident(const SkDLine& line, double y) { |
157 double min = line[0].fY; | 133 double min = line[0].fY; |
158 double max = line[1].fY; | 134 double max = line[1].fY; |
159 if (min > max) { | 135 if (min > max) { |
160 SkTSwap(min, max); | 136 SkTSwap(min, max); |
161 } | 137 } |
162 if (min > y || max < y) { | 138 if (min > y || max < y) { |
163 return fUsed = 0; | 139 return 0; |
164 } | 140 } |
165 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX))
{ | 141 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX))
{ |
| 142 return 2; |
| 143 } |
| 144 return 1; |
| 145 } |
| 146 |
| 147 static double horizontal_intercept(const SkDLine& line, double y) { |
| 148 return (y - line[0].fY) / (line[1].fY - line[0].fY); |
| 149 } |
| 150 |
| 151 int SkIntersections::horizontal(const SkDLine& line, double y) { |
| 152 int horizontalType = horizontal_coincident(line, y); |
| 153 if (horizontalType == 1) { |
| 154 fT[0][0] = horizontal_intercept(line, y); |
| 155 } else if (horizontalType == 2) { |
166 fT[0][0] = 0; | 156 fT[0][0] = 0; |
167 fT[0][1] = 1; | 157 fT[0][1] = 1; |
168 return fUsed = 2; | |
169 } | 158 } |
170 fT[0][0] = (y - line[0].fY) / (line[1].fY - line[0].fY); | 159 return fUsed = horizontalType; |
171 return fUsed = 1; | |
172 } | |
173 | |
174 static bool checkEndPointH(const SkDPoint& end, double left, double right, | |
175 double y, bool flipped, double* tPtr) { | |
176 if (!between(left, end.fX, right) || !AlmostEqualUlps(y, end.fY)) { | |
177 return false; | |
178 } | |
179 double t = (end.fX - left) / (right - left); | |
180 SkASSERT(between(0, t, 1)); | |
181 *tPtr = flipped ? 1 - t : t; | |
182 return true; | |
183 } | 160 } |
184 | 161 |
185 int SkIntersections::horizontal(const SkDLine& line, double left, double right, | 162 int SkIntersections::horizontal(const SkDLine& line, double left, double right, |
186 double y, bool flipped) { | 163 double y, bool flipped) { |
187 // see if end points intersect the opposite line | 164 // see if end points intersect the opposite line |
188 double t; | 165 double t; |
189 if (checkEndPoint(left, y, line, &t, true)) { | 166 const SkDPoint leftPt = { left, y }; |
190 insert(t, flipped, left, y); | 167 if ((t = line.exactPoint(leftPt)) >= 0) { |
| 168 insert(t, (double) flipped, leftPt); |
191 } | 169 } |
192 if (left != right) { | 170 if (left != right) { |
193 if (checkEndPoint(right, y, line, &t, true)) { | 171 const SkDPoint rightPt = { right, y }; |
194 insert(t, !flipped, right, y); | 172 if ((t = line.exactPoint(rightPt)) >= 0) { |
| 173 insert(t, (double) !flipped, rightPt); |
195 } | 174 } |
196 for (int index = 0; index < 2; ++index) { | 175 for (int index = 0; index < 2; ++index) { |
197 if (!checkEndPointH(line[index], left, right, y, flipped, &t)) { | 176 if ((t = SkDLine::ExactPointH(line[index], left, right, y)) >= 0) { |
198 continue; | 177 insert((double) index, flipped ? 1 - t : t, line[index]); |
199 } | 178 } |
200 insert(index, t, line[index]); | |
201 } | 179 } |
202 } | 180 } |
203 if (used() > 0) { | 181 int result = horizontal_coincident(line, y); |
204 SkASSERT(fUsed <= 2); | 182 if (result == 1 && fUsed == 0) { |
205 return used(); // coincident lines are returned here | 183 fT[0][0] = horizontal_intercept(line, y); |
206 } | 184 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); |
207 int result = horizontal(line, y); | 185 if (between(left, xIntercept, right)) { |
208 if (!result) { | 186 fT[1][0] = (xIntercept - left) / (right - left); |
209 return 0; | 187 if (flipped) { |
210 } | 188 // OPTIMIZATION: ? instead of swapping, pass original line, use
[1].fX - [0].fX |
211 SkASSERT(result == 1); | 189 for (int index = 0; index < result; ++index) { |
212 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); | 190 fT[1][index] = 1 - fT[1][index]; |
213 if (!precisely_between(left, xIntercept, right)) { | 191 } |
214 return fUsed = 0; | 192 } |
215 } | 193 return computePoints(line, result); |
216 fT[1][0] = (xIntercept - left) / (right - left); | |
217 if (flipped) { | |
218 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX -
[0].fX | |
219 for (int index = 0; index < result; ++index) { | |
220 fT[1][index] = 1 - fT[1][index]; | |
221 } | 194 } |
222 } | 195 } |
223 return computePoints(line, result); | 196 if (!fAllowNear && result != 2) { |
| 197 return fUsed; |
| 198 } |
| 199 if ((t = line.nearPoint(leftPt)) >= 0) { |
| 200 insert(t, (double) flipped, leftPt); |
| 201 } |
| 202 if (left != right) { |
| 203 const SkDPoint rightPt = { right, y }; |
| 204 if ((t = line.nearPoint(rightPt)) >= 0) { |
| 205 insert(t, (double) !flipped, rightPt); |
| 206 } |
| 207 for (int index = 0; index < 2; ++index) { |
| 208 if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) { |
| 209 insert((double) index, flipped ? 1 - t : t, line[index]); |
| 210 } |
| 211 } |
| 212 } |
| 213 return fUsed; |
224 } | 214 } |
225 | 215 |
226 int SkIntersections::vertical(const SkDLine& line, double x) { | 216 static int vertical_coincident(const SkDLine& line, double x) { |
227 double min = line[0].fX; | 217 double min = line[0].fX; |
228 double max = line[1].fX; | 218 double max = line[1].fX; |
229 if (min > max) { | 219 if (min > max) { |
230 SkTSwap(min, max); | 220 SkTSwap(min, max); |
231 } | 221 } |
232 if (!precisely_between(min, x, max)) { | 222 if (!precisely_between(min, x, max)) { |
233 return fUsed = 0; | 223 return 0; |
234 } | 224 } |
235 if (AlmostEqualUlps(min, max)) { | 225 if (AlmostEqualUlps(min, max)) { |
| 226 return 2; |
| 227 } |
| 228 return 1; |
| 229 } |
| 230 |
| 231 static double vertical_intercept(const SkDLine& line, double x) { |
| 232 return (x - line[0].fX) / (line[1].fX - line[0].fX); |
| 233 } |
| 234 |
| 235 int SkIntersections::vertical(const SkDLine& line, double x) { |
| 236 int verticalType = vertical_coincident(line, x); |
| 237 if (verticalType == 1) { |
| 238 fT[0][0] = vertical_intercept(line, x); |
| 239 } else if (verticalType == 2) { |
236 fT[0][0] = 0; | 240 fT[0][0] = 0; |
237 fT[0][1] = 1; | 241 fT[0][1] = 1; |
238 return fUsed = 2; | |
239 } | 242 } |
240 fT[0][0] = (x - line[0].fX) / (line[1].fX - line[0].fX); | 243 return fUsed = verticalType; |
241 return fUsed = 1; | |
242 } | |
243 | |
244 static bool checkEndPointV(const SkDPoint& end, double top, double bottom, | |
245 double x, bool flipped, double* tPtr) { | |
246 if (!between(top, end.fY, bottom) || !AlmostEqualUlps(x, end.fX)) { | |
247 return false; | |
248 } | |
249 double t = (end.fY - top) / (bottom - top); | |
250 SkASSERT(between(0, t, 1)); | |
251 *tPtr = flipped ? 1 - t : t; | |
252 return true; | |
253 } | 244 } |
254 | 245 |
255 int SkIntersections::vertical(const SkDLine& line, double top, double bottom, | 246 int SkIntersections::vertical(const SkDLine& line, double top, double bottom, |
256 double x, bool flipped) { | 247 double x, bool flipped) { |
257 // see if end points intersect the opposite line | 248 // see if end points intersect the opposite line |
258 double t; | 249 double t; |
259 if (checkEndPoint(x, top, line, &t, false)) { | 250 SkDPoint topPt = { x, top }; |
260 insert(t, flipped, x, top); | 251 if ((t = line.exactPoint(topPt)) >= 0) { |
| 252 insert(t, (double) flipped, topPt); |
261 } | 253 } |
262 if (top != bottom) { | 254 if (top != bottom) { |
263 if (checkEndPoint(x, bottom,line, &t, false)) { | 255 SkDPoint bottomPt = { x, bottom }; |
264 insert(t, !flipped, x, bottom); | 256 if ((t = line.exactPoint(bottomPt)) >= 0) { |
| 257 insert(t, (double) !flipped, bottomPt); |
265 } | 258 } |
266 for (int index = 0; index < 2; ++index) { | 259 for (int index = 0; index < 2; ++index) { |
267 if (!checkEndPointV(line[index], top, bottom, x, flipped, &t)) { | 260 if ((t = SkDLine::ExactPointV(line[index], top, bottom, x)) >= 0) { |
268 continue; | 261 insert((double) index, flipped ? 1 - t : t, line[index]); |
269 } | 262 } |
270 insert( index, t, line[index]); | |
271 } | 263 } |
272 } | 264 } |
273 if (used() > 0) { | 265 int result = vertical_coincident(line, x); |
274 SkASSERT(fUsed <= 2); | 266 if (result == 1 && fUsed == 0) { |
275 return used(); // coincident lines are returned here | 267 fT[0][0] = vertical_intercept(line, x); |
276 } | 268 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); |
277 int result = vertical(line, x); | 269 if (between(top, yIntercept, bottom)) { |
278 if (!result) { | 270 fT[1][0] = (yIntercept - top) / (bottom - top); |
279 return 0; | 271 if (flipped) { |
280 } | 272 // OPTIMIZATION: instead of swapping, pass original line, use [1
].fY - [0].fY |
281 SkASSERT(result == 1); | 273 for (int index = 0; index < result; ++index) { |
282 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); | 274 fT[1][index] = 1 - fT[1][index]; |
283 if (!precisely_between(top, yIntercept, bottom)) { | 275 } |
284 return fUsed = 0; | 276 } |
285 } | 277 return computePoints(line, result); |
286 fT[1][0] = (yIntercept - top) / (bottom - top); | |
287 if (flipped) { | |
288 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [
0].fY | |
289 for (int index = 0; index < result; ++index) { | |
290 fT[1][index] = 1 - fT[1][index]; | |
291 } | 278 } |
292 } | 279 } |
293 return computePoints(line, result); | 280 if (!fAllowNear && result != 2) { |
| 281 return fUsed; |
| 282 } |
| 283 if ((t = line.nearPoint(topPt)) >= 0) { |
| 284 insert(t, (double) flipped, topPt); |
| 285 } |
| 286 if (top != bottom) { |
| 287 SkDPoint bottomPt = { x, bottom }; |
| 288 if ((t = line.nearPoint(bottomPt)) >= 0) { |
| 289 insert(t, (double) !flipped, bottomPt); |
| 290 } |
| 291 for (int index = 0; index < 2; ++index) { |
| 292 if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) { |
| 293 insert((double) index, flipped ? 1 - t : t, line[index]); |
| 294 } |
| 295 } |
| 296 } |
| 297 return fUsed; |
294 } | 298 } |
295 | 299 |
296 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.p
y | 300 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.p
y |
297 // 4 subs, 2 muls, 1 cmp | 301 // 4 subs, 2 muls, 1 cmp |
298 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) { | 302 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) { |
299 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX); | 303 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX); |
300 } | 304 } |
301 | 305 |
302 // 16 subs, 8 muls, 6 cmps | 306 // 16 subs, 8 muls, 6 cmps |
303 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) { | 307 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) { |
304 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1]) | 308 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1]) |
305 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]); | 309 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]); |
306 } | 310 } |
OLD | NEW |