| 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 |