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 /* | 78 static bool checkEndPoint(double x, double y, const SkDLine& l, double* tPtr, in
t useX) { |
79 Determine the intersection point of two line segments | 79 if (!between(l[0].fX, x, l[1].fX) || !between(l[0].fY, y, l[1].fY)) { |
80 Return FALSE if the lines don't intersect | 80 return false; |
81 from: http://paulbourke.net/geometry/lineline2d/ | 81 } |
82 */ | 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 } |
83 | 99 |
| 100 // note that this only works if both lines are neither horizontal nor vertical |
84 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { | 101 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) { |
| 102 // see if end points intersect the opposite line |
| 103 double t; |
| 104 for (int iA = 0; iA < 2; ++iA) { |
| 105 if (!checkEndPoint(a[iA].fX, a[iA].fY, b, &t, -1)) { |
| 106 continue; |
| 107 } |
| 108 insert(iA, t, a[iA]); |
| 109 } |
| 110 for (int iB = 0; iB < 2; ++iB) { |
| 111 if (!checkEndPoint(b[iB].fX, b[iB].fY, a, &t, -1)) { |
| 112 continue; |
| 113 } |
| 114 insert(t, iB, b[iB]); |
| 115 } |
| 116 if (used() > 0) { |
| 117 SkASSERT(fUsed <= 2); |
| 118 return used(); // coincident lines are returned here |
| 119 } |
| 120 /* Determine the intersection point of two line segments |
| 121 Return FALSE if the lines don't intersect |
| 122 from: http://paulbourke.net/geometry/lineline2d/ */ |
85 double axLen = a[1].fX - a[0].fX; | 123 double axLen = a[1].fX - a[0].fX; |
86 double ayLen = a[1].fY - a[0].fY; | 124 double ayLen = a[1].fY - a[0].fY; |
87 double bxLen = b[1].fX - b[0].fX; | 125 double bxLen = b[1].fX - b[0].fX; |
88 double byLen = b[1].fY - b[0].fY; | 126 double byLen = b[1].fY - b[0].fY; |
89 /* Slopes match when denom goes to zero: | 127 /* Slopes match when denom goes to zero: |
90 axLen / ayLen == bxLen / byLen | 128 axLen / ayLen == bxLen / byLen |
91 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen | 129 (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen |
92 byLen * axLen == ayLen * bxLen | 130 byLen * axLen == ayLen * bxLen |
93 byLen * axLen - ayLen * bxLen == 0 ( == denom ) | 131 byLen * axLen - ayLen * bxLen == 0 ( == denom ) |
94 */ | 132 */ |
95 double denom = byLen * axLen - ayLen * bxLen; | 133 double denom = byLen * axLen - ayLen * bxLen; |
96 double ab0y = a[0].fY - b[0].fY; | 134 double ab0y = a[0].fY - b[0].fY; |
97 double ab0x = a[0].fX - b[0].fX; | 135 double ab0x = a[0].fX - b[0].fX; |
98 double numerA = ab0y * bxLen - byLen * ab0x; | 136 double numerA = ab0y * bxLen - byLen * ab0x; |
99 double numerB = ab0y * axLen - ayLen * ab0x; | 137 double numerB = ab0y * axLen - ayLen * ab0x; |
100 bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom
< numerA) | 138 bool mayNotOverlap = (numerA < 0 && denom > numerA) || (numerA > 0 && denom
< numerA) |
101 || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB); | 139 || (numerB < 0 && denom > numerB) || (numerB > 0 && denom < numerB); |
102 numerA /= denom; | 140 numerA /= denom; |
103 numerB /= denom; | 141 numerB /= denom; |
104 if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA) | 142 if ((!approximately_zero(denom) || (!approximately_zero_inverse(numerA) |
105 && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA) | 143 && !approximately_zero_inverse(numerB))) && !sk_double_isnan(numerA) |
106 && !sk_double_isnan(numerB)) { | 144 && !sk_double_isnan(numerB)) { |
107 if (mayNotOverlap) { | 145 if (mayNotOverlap) { |
108 return fUsed = 0; | 146 return 0; |
109 } | 147 } |
110 fT[0][0] = numerA; | 148 fT[0][0] = numerA; |
111 fT[1][0] = numerB; | 149 fT[1][0] = numerB; |
112 fPt[0] = a.xyAtT(numerA); | 150 fPt[0] = a.xyAtT(numerA); |
113 return computePoints(a, 1); | 151 return computePoints(a, 1); |
114 } | 152 } |
115 /* See if the axis intercepts match: | 153 return 0; |
116 ay - ax * ayLen / axLen == by - bx * ayLen / axLen | |
117 axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen) | |
118 axLen * ay - ax * ayLen == axLen * by - bx * ayLen | |
119 */ | |
120 if (!AlmostEqualUlps(axLen * a[0].fY - ayLen * a[0].fX, | |
121 axLen * b[0].fY - ayLen * b[0].fX)) { | |
122 return fUsed = 0; | |
123 } | |
124 const double* aPtr; | |
125 const double* bPtr; | |
126 if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) { | |
127 aPtr = &a[0].fX; | |
128 bPtr = &b[0].fX; | |
129 } else { | |
130 aPtr = &a[0].fY; | |
131 bPtr = &b[0].fY; | |
132 } | |
133 double a0 = aPtr[0]; | |
134 double a1 = aPtr[2]; | |
135 double b0 = bPtr[0]; | |
136 double b1 = bPtr[2]; | |
137 // OPTIMIZATION: restructure to reject before the divide | |
138 // e.g., if ((a0 - b0) * (a0 - a1) < 0 || abs(a0 - b0) > abs(a0 - a1)) | |
139 // (except efficient) | |
140 double aDenom = a0 - a1; | |
141 if (approximately_zero(aDenom)) { | |
142 if (!between(b0, a0, b1)) { | |
143 return fUsed = 0; | |
144 } | |
145 fT[0][0] = fT[0][1] = 0; | |
146 } else { | |
147 double at0 = (a0 - b0) / aDenom; | |
148 double at1 = (a0 - b1) / aDenom; | |
149 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) { | |
150 return fUsed = 0; | |
151 } | |
152 fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0); | |
153 fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0); | |
154 } | |
155 double bDenom = b0 - b1; | |
156 if (approximately_zero(bDenom)) { | |
157 fT[1][0] = fT[1][1] = 0; | |
158 } else { | |
159 int bIn = aDenom * bDenom < 0; | |
160 fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / bDenom, 1.0), 0.0); | |
161 fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / bDenom, 1.0), 0.0); | |
162 } | |
163 bool second = fabs(fT[0][0] - fT[0][1]) > FLT_EPSILON; | |
164 SkASSERT((fabs(fT[1][0] - fT[1][1]) <= FLT_EPSILON) ^ second); | |
165 return computePoints(a, 1 + second); | |
166 } | 154 } |
167 | 155 |
168 int SkIntersections::horizontal(const SkDLine& line, double y) { | 156 int SkIntersections::horizontal(const SkDLine& line, double y) { |
169 double min = line[0].fY; | 157 double min = line[0].fY; |
170 double max = line[1].fY; | 158 double max = line[1].fY; |
171 if (min > max) { | 159 if (min > max) { |
172 SkTSwap(min, max); | 160 SkTSwap(min, max); |
173 } | 161 } |
174 if (min > y || max < y) { | 162 if (min > y || max < y) { |
175 return fUsed = 0; | 163 return fUsed = 0; |
176 } | 164 } |
177 if (AlmostEqualUlps(min, max)) { | 165 if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX))
{ |
178 fT[0][0] = 0; | 166 fT[0][0] = 0; |
179 fT[0][1] = 1; | 167 fT[0][1] = 1; |
180 return fUsed = 2; | 168 return fUsed = 2; |
181 } | 169 } |
182 fT[0][0] = (y - line[0].fY) / (line[1].fY - line[0].fY); | 170 fT[0][0] = (y - line[0].fY) / (line[1].fY - line[0].fY); |
183 return fUsed = 1; | 171 return fUsed = 1; |
184 } | 172 } |
185 | 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 } |
| 184 |
186 int SkIntersections::horizontal(const SkDLine& line, double left, double right, | 185 int SkIntersections::horizontal(const SkDLine& line, double left, double right, |
187 double y, bool flipped) { | 186 double y, bool flipped) { |
| 187 // see if end points intersect the opposite line |
| 188 double t; |
| 189 if (checkEndPoint(left, y, line, &t, true)) { |
| 190 insert(t, flipped, left, y); |
| 191 } |
| 192 if (left != right) { |
| 193 if (checkEndPoint(right, y, line, &t, true)) { |
| 194 insert(t, !flipped, right, y); |
| 195 } |
| 196 for (int index = 0; index < 2; ++index) { |
| 197 if (!checkEndPointH(line[index], left, right, y, flipped, &t)) { |
| 198 continue; |
| 199 } |
| 200 insert(index, t, line[index]); |
| 201 } |
| 202 } |
| 203 if (used() > 0) { |
| 204 SkASSERT(fUsed <= 2); |
| 205 return used(); // coincident lines are returned here |
| 206 } |
188 int result = horizontal(line, y); | 207 int result = horizontal(line, y); |
189 switch (result) { | 208 if (!result) { |
190 case 0: | 209 return 0; |
191 break; | |
192 case 1: { | |
193 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX
); | |
194 if (!precisely_between(left, xIntercept, right)) { | |
195 return fUsed = 0; | |
196 } | |
197 fT[1][0] = (xIntercept - left) / (right - left); | |
198 break; | |
199 } | |
200 case 2: | |
201 double a0 = line[0].fX; | |
202 double a1 = line[1].fX; | |
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 fUsed = 0; | |
210 } | |
211 fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0); | |
212 fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0); | |
213 int bIn = (a0 - a1) * (b0 - b1) < 0; | |
214 fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1), 1.0), 0.0); | |
215 fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1), 1.0), 0.0); | |
216 bool second = fabs(fT[0][0] - fT[0][1]) > FLT_EPSILON; | |
217 SkASSERT((fabs(fT[1][0] - fT[1][1]) <= FLT_EPSILON) ^ second); | |
218 return computePoints(line, 1 + second); | |
219 } | 210 } |
| 211 SkASSERT(result == 1); |
| 212 double xIntercept = line[0].fX + fT[0][0] * (line[1].fX - line[0].fX); |
| 213 if (!precisely_between(left, xIntercept, right)) { |
| 214 return fUsed = 0; |
| 215 } |
| 216 fT[1][0] = (xIntercept - left) / (right - left); |
220 if (flipped) { | 217 if (flipped) { |
221 // OPTIMIZATION: instead of swapping, pass original line, use [1].fX - [
0].fX | 218 // OPTIMIZATION: ? instead of swapping, pass original line, use [1].fX -
[0].fX |
222 for (int index = 0; index < result; ++index) { | 219 for (int index = 0; index < result; ++index) { |
223 fT[1][index] = 1 - fT[1][index]; | 220 fT[1][index] = 1 - fT[1][index]; |
224 } | 221 } |
225 } | 222 } |
226 return computePoints(line, result); | 223 return computePoints(line, result); |
227 } | 224 } |
228 | 225 |
229 int SkIntersections::vertical(const SkDLine& line, double x) { | 226 int SkIntersections::vertical(const SkDLine& line, double x) { |
230 double min = line[0].fX; | 227 double min = line[0].fX; |
231 double max = line[1].fX; | 228 double max = line[1].fX; |
232 if (min > max) { | 229 if (min > max) { |
233 SkTSwap(min, max); | 230 SkTSwap(min, max); |
234 } | 231 } |
235 if (!precisely_between(min, x, max)) { | 232 if (!precisely_between(min, x, max)) { |
236 return fUsed = 0; | 233 return fUsed = 0; |
237 } | 234 } |
238 if (AlmostEqualUlps(min, max)) { | 235 if (AlmostEqualUlps(min, max)) { |
239 fT[0][0] = 0; | 236 fT[0][0] = 0; |
240 fT[0][1] = 1; | 237 fT[0][1] = 1; |
241 return fUsed = 2; | 238 return fUsed = 2; |
242 } | 239 } |
243 fT[0][0] = (x - line[0].fX) / (line[1].fX - line[0].fX); | 240 fT[0][0] = (x - line[0].fX) / (line[1].fX - line[0].fX); |
244 return fUsed = 1; | 241 return fUsed = 1; |
245 } | 242 } |
246 | 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 } |
| 254 |
247 int SkIntersections::vertical(const SkDLine& line, double top, double bottom, | 255 int SkIntersections::vertical(const SkDLine& line, double top, double bottom, |
248 double x, bool flipped) { | 256 double x, bool flipped) { |
| 257 // see if end points intersect the opposite line |
| 258 double t; |
| 259 if (checkEndPoint(x, top, line, &t, false)) { |
| 260 insert(t, flipped, x, top); |
| 261 } |
| 262 if (top != bottom) { |
| 263 if (checkEndPoint(x, bottom,line, &t, false)) { |
| 264 insert(t, !flipped, x, bottom); |
| 265 } |
| 266 for (int index = 0; index < 2; ++index) { |
| 267 if (!checkEndPointV(line[index], top, bottom, x, flipped, &t)) { |
| 268 continue; |
| 269 } |
| 270 insert( index, t, line[index]); |
| 271 } |
| 272 } |
| 273 if (used() > 0) { |
| 274 SkASSERT(fUsed <= 2); |
| 275 return used(); // coincident lines are returned here |
| 276 } |
249 int result = vertical(line, x); | 277 int result = vertical(line, x); |
250 switch (result) { | 278 if (!result) { |
251 case 0: | 279 return 0; |
252 break; | |
253 case 1: { | |
254 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY
); | |
255 if (!precisely_between(top, yIntercept, bottom)) { | |
256 return fUsed = 0; | |
257 } | |
258 fT[1][0] = (yIntercept - top) / (bottom - top); | |
259 break; | |
260 } | |
261 case 2: | |
262 double a0 = line[0].fY; | |
263 double a1 = line[1].fY; | |
264 double b0 = flipped ? bottom : top; | |
265 double b1 = flipped ? top : bottom; | |
266 // FIXME: share common code above | |
267 double at0 = (a0 - b0) / (a0 - a1); | |
268 double at1 = (a0 - b1) / (a0 - a1); | |
269 if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) { | |
270 return fUsed = 0; | |
271 } | |
272 fT[0][0] = SkTMax(SkTMin(at0, 1.0), 0.0); | |
273 fT[0][1] = SkTMax(SkTMin(at1, 1.0), 0.0); | |
274 int bIn = (a0 - a1) * (b0 - b1) < 0; | |
275 fT[1][bIn] = SkTMax(SkTMin((b0 - a0) / (b0 - b1), 1.0), 0.0); | |
276 fT[1][!bIn] = SkTMax(SkTMin((b0 - a1) / (b0 - b1), 1.0), 0.0); | |
277 bool second = fabs(fT[0][0] - fT[0][1]) > FLT_EPSILON; | |
278 SkASSERT((fabs(fT[1][0] - fT[1][1]) <= FLT_EPSILON) ^ second); | |
279 return computePoints(line, 1 + second); | |
280 } | 280 } |
| 281 SkASSERT(result == 1); |
| 282 double yIntercept = line[0].fY + fT[0][0] * (line[1].fY - line[0].fY); |
| 283 if (!precisely_between(top, yIntercept, bottom)) { |
| 284 return fUsed = 0; |
| 285 } |
| 286 fT[1][0] = (yIntercept - top) / (bottom - top); |
281 if (flipped) { | 287 if (flipped) { |
282 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [
0].fY | 288 // OPTIMIZATION: instead of swapping, pass original line, use [1].fY - [
0].fY |
283 for (int index = 0; index < result; ++index) { | 289 for (int index = 0; index < result; ++index) { |
284 fT[1][index] = 1 - fT[1][index]; | 290 fT[1][index] = 1 - fT[1][index]; |
285 } | 291 } |
286 } | 292 } |
287 return computePoints(line, result); | 293 return computePoints(line, result); |
288 } | 294 } |
289 | 295 |
290 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.p
y | 296 // from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.p
y |
291 // 4 subs, 2 muls, 1 cmp | 297 // 4 subs, 2 muls, 1 cmp |
292 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) { | 298 static bool ccw(const SkDPoint& A, const SkDPoint& B, const SkDPoint& C) { |
293 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX); | 299 return (C.fY - A.fY) * (B.fX - A.fX) > (B.fY - A.fY) * (C.fX - A.fX); |
294 } | 300 } |
295 | 301 |
296 // 16 subs, 8 muls, 6 cmps | 302 // 16 subs, 8 muls, 6 cmps |
297 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) { | 303 bool SkIntersections::Test(const SkDLine& a, const SkDLine& b) { |
298 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1]) | 304 return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1]) |
299 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]); | 305 && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]); |
300 } | 306 } |
OLD | NEW |