| OLD | NEW |
| (Empty) |
| 1 /* | |
| 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 */ | |
| 7 #include "CurveIntersection.h" | |
| 8 #include "Intersections.h" | |
| 9 #include "IntersectionUtilities.h" | |
| 10 #include "LineIntersection.h" | |
| 11 #include "LineUtilities.h" | |
| 12 #include "QuadraticLineSegments.h" | |
| 13 #include "QuadraticUtilities.h" | |
| 14 #include <algorithm> // for swap | |
| 15 | |
| 16 static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezc
lip.pdf see Multiple intersections | |
| 17 | |
| 18 class QuadraticIntersections { | |
| 19 public: | |
| 20 | |
| 21 QuadraticIntersections(const Quadratic& q1, const Quadratic& q2, Intersections&
i) | |
| 22 : quad1(q1) | |
| 23 , quad2(q2) | |
| 24 , intersections(i) | |
| 25 , depth(0) | |
| 26 , splits(0) | |
| 27 , coinMinT1(-1) { | |
| 28 } | |
| 29 | |
| 30 bool intersect() { | |
| 31 double minT1, minT2, maxT1, maxT2; | |
| 32 if (!bezier_clip(quad2, quad1, minT1, maxT1)) { | |
| 33 return false; | |
| 34 } | |
| 35 if (!bezier_clip(quad1, quad2, minT2, maxT2)) { | |
| 36 return false; | |
| 37 } | |
| 38 quad1Divisions = 1 / subDivisions(quad1); | |
| 39 quad2Divisions = 1 / subDivisions(quad2); | |
| 40 int split; | |
| 41 if (maxT1 - minT1 < maxT2 - minT2) { | |
| 42 intersections.swap(); | |
| 43 minT2 = 0; | |
| 44 maxT2 = 1; | |
| 45 split = maxT1 - minT1 > tClipLimit; | |
| 46 } else { | |
| 47 minT1 = 0; | |
| 48 maxT1 = 1; | |
| 49 split = (maxT2 - minT2 > tClipLimit) << 1; | |
| 50 } | |
| 51 return chop(minT1, maxT1, minT2, maxT2, split); | |
| 52 } | |
| 53 | |
| 54 protected: | |
| 55 | |
| 56 bool intersect(double minT1, double maxT1, double minT2, double maxT2) { | |
| 57 bool t1IsLine = maxT1 - minT1 <= quad1Divisions; | |
| 58 bool t2IsLine = maxT2 - minT2 <= quad2Divisions; | |
| 59 if (t1IsLine | t2IsLine) { | |
| 60 return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine); | |
| 61 } | |
| 62 Quadratic smaller, larger; | |
| 63 // FIXME: carry last subdivide and reduceOrder result with quad | |
| 64 sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller); | |
| 65 sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger); | |
| 66 double minT, maxT; | |
| 67 if (!bezier_clip(smaller, larger, minT, maxT)) { | |
| 68 if (approximately_equal(minT, maxT)) { | |
| 69 double smallT, largeT; | |
| 70 _Point q2pt, q1pt; | |
| 71 if (intersections.swapped()) { | |
| 72 largeT = interp(minT2, maxT2, minT); | |
| 73 xy_at_t(quad2, largeT, q2pt.x, q2pt.y); | |
| 74 xy_at_t(quad1, minT1, q1pt.x, q1pt.y); | |
| 75 if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q
1pt.y)) { | |
| 76 smallT = minT1; | |
| 77 } else { | |
| 78 xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code | |
| 79 SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(
q2pt.y, q1pt.y)); | |
| 80 smallT = maxT1; | |
| 81 } | |
| 82 } else { | |
| 83 smallT = interp(minT1, maxT1, minT); | |
| 84 xy_at_t(quad1, smallT, q1pt.x, q1pt.y); | |
| 85 xy_at_t(quad2, minT2, q2pt.x, q2pt.y); | |
| 86 if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q
1pt.y)) { | |
| 87 largeT = minT2; | |
| 88 } else { | |
| 89 xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code | |
| 90 SkASSERT(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(
q2pt.y, q1pt.y)); | |
| 91 largeT = maxT2; | |
| 92 } | |
| 93 } | |
| 94 intersections.add(smallT, largeT); | |
| 95 return true; | |
| 96 } | |
| 97 return false; | |
| 98 } | |
| 99 int split; | |
| 100 if (intersections.swapped()) { | |
| 101 double newMinT1 = interp(minT1, maxT1, minT); | |
| 102 double newMaxT1 = interp(minT1, maxT1, maxT); | |
| 103 split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1; | |
| 104 #define VERBOSE 0 | |
| 105 #if VERBOSE | |
| 106 printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__
, depth, | |
| 107 splits, newMinT1, newMaxT1, minT1, maxT1, split); | |
| 108 #endif | |
| 109 minT1 = newMinT1; | |
| 110 maxT1 = newMaxT1; | |
| 111 } else { | |
| 112 double newMinT2 = interp(minT2, maxT2, minT); | |
| 113 double newMaxT2 = interp(minT2, maxT2, maxT); | |
| 114 split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit; | |
| 115 #if VERBOSE | |
| 116 printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__
, depth, | |
| 117 splits, newMinT2, newMaxT2, minT2, maxT2, split); | |
| 118 #endif | |
| 119 minT2 = newMinT2; | |
| 120 maxT2 = newMaxT2; | |
| 121 } | |
| 122 return chop(minT1, maxT1, minT2, maxT2, split); | |
| 123 } | |
| 124 | |
| 125 bool intersectAsLine(double minT1, double maxT1, double minT2, double maxT2, | |
| 126 bool treat1AsLine, bool treat2AsLine) | |
| 127 { | |
| 128 _Line line1, line2; | |
| 129 if (intersections.swapped()) { | |
| 130 SkTSwap(treat1AsLine, treat2AsLine); | |
| 131 SkTSwap(minT1, minT2); | |
| 132 SkTSwap(maxT1, maxT2); | |
| 133 } | |
| 134 if (coinMinT1 >= 0) { | |
| 135 bool earlyExit; | |
| 136 if ((earlyExit = coinMaxT1 == minT1)) { | |
| 137 coinMaxT1 = maxT1; | |
| 138 } | |
| 139 if (coinMaxT2 == minT2) { | |
| 140 coinMaxT2 = maxT2; | |
| 141 return true; | |
| 142 } | |
| 143 if (earlyExit) { | |
| 144 return true; | |
| 145 } | |
| 146 coinMinT1 = -1; | |
| 147 } | |
| 148 // do line/quadratic or even line/line intersection instead | |
| 149 if (treat1AsLine) { | |
| 150 xy_at_t(quad1, minT1, line1[0].x, line1[0].y); | |
| 151 xy_at_t(quad1, maxT1, line1[1].x, line1[1].y); | |
| 152 } | |
| 153 if (treat2AsLine) { | |
| 154 xy_at_t(quad2, minT2, line2[0].x, line2[0].y); | |
| 155 xy_at_t(quad2, maxT2, line2[1].x, line2[1].y); | |
| 156 } | |
| 157 int pts; | |
| 158 double smallT1, largeT1, smallT2, largeT2; | |
| 159 if (treat1AsLine & treat2AsLine) { | |
| 160 double t1[2], t2[2]; | |
| 161 pts = ::intersect(line1, line2, t1, t2); | |
| 162 if (pts == 2) { | |
| 163 smallT1 = interp(minT1, maxT1, t1[0]); | |
| 164 largeT1 = interp(minT2, maxT2, t2[0]); | |
| 165 smallT2 = interp(minT1, maxT1, t1[1]); | |
| 166 largeT2 = interp(minT2, maxT2, t2[1]); | |
| 167 intersections.addCoincident(smallT1, smallT2, largeT1, largeT2); | |
| 168 } else { | |
| 169 smallT1 = interp(minT1, maxT1, t1[0]); | |
| 170 largeT1 = interp(minT2, maxT2, t2[0]); | |
| 171 intersections.add(smallT1, largeT1); | |
| 172 } | |
| 173 } else { | |
| 174 Intersections lq; | |
| 175 pts = ::intersect(treat1AsLine ? quad2 : quad1, | |
| 176 treat1AsLine ? line1 : line2, lq); | |
| 177 if (pts == 2) { // if the line and edge are coincident treat differently | |
| 178 _Point midQuad, midLine; | |
| 179 double midQuadT = (lq.fT[0][0] + lq.fT[0][1]) / 2; | |
| 180 xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y
); | |
| 181 double lineT = t_at(treat1AsLine ? line1 : line2, midQuad); | |
| 182 xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y); | |
| 183 if (AlmostEqualUlps(midQuad.x, midLine.x) | |
| 184 && AlmostEqualUlps(midQuad.y, midLine.y)) { | |
| 185 smallT1 = lq.fT[0][0]; | |
| 186 largeT1 = lq.fT[1][0]; | |
| 187 smallT2 = lq.fT[0][1]; | |
| 188 largeT2 = lq.fT[1][1]; | |
| 189 if (treat2AsLine) { | |
| 190 smallT1 = interp(minT1, maxT1, smallT1); | |
| 191 smallT2 = interp(minT1, maxT1, smallT2); | |
| 192 } else { | |
| 193 largeT1 = interp(minT2, maxT2, largeT1); | |
| 194 largeT2 = interp(minT2, maxT2, largeT2); | |
| 195 } | |
| 196 intersections.addCoincident(smallT1, smallT2, largeT1, largeT2); | |
| 197 goto setCoinMinMax; | |
| 198 } | |
| 199 } | |
| 200 for (int index = 0; index < pts; ++index) { | |
| 201 smallT1 = lq.fT[0][index]; | |
| 202 largeT1 = lq.fT[1][index]; | |
| 203 if (treat2AsLine) { | |
| 204 smallT1 = interp(minT1, maxT1, smallT1); | |
| 205 } else { | |
| 206 largeT1 = interp(minT2, maxT2, largeT1); | |
| 207 } | |
| 208 intersections.add(smallT1, largeT1); | |
| 209 } | |
| 210 } | |
| 211 if (pts > 0) { | |
| 212 setCoinMinMax: | |
| 213 coinMinT1 = minT1; | |
| 214 coinMaxT1 = maxT1; | |
| 215 coinMinT2 = minT2; | |
| 216 coinMaxT2 = maxT2; | |
| 217 } | |
| 218 return pts > 0; | |
| 219 } | |
| 220 | |
| 221 bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) { | |
| 222 ++depth; | |
| 223 intersections.swap(); | |
| 224 if (split) { | |
| 225 ++splits; | |
| 226 if (split & 2) { | |
| 227 double middle1 = (maxT1 + minT1) / 2; | |
| 228 intersect(minT1, middle1, minT2, maxT2); | |
| 229 intersect(middle1, maxT1, minT2, maxT2); | |
| 230 } else { | |
| 231 double middle2 = (maxT2 + minT2) / 2; | |
| 232 intersect(minT1, maxT1, minT2, middle2); | |
| 233 intersect(minT1, maxT1, middle2, maxT2); | |
| 234 } | |
| 235 --splits; | |
| 236 intersections.swap(); | |
| 237 --depth; | |
| 238 return intersections.intersected(); | |
| 239 } | |
| 240 bool result = intersect(minT1, maxT1, minT2, maxT2); | |
| 241 intersections.swap(); | |
| 242 --depth; | |
| 243 return result; | |
| 244 } | |
| 245 | |
| 246 private: | |
| 247 | |
| 248 const Quadratic& quad1; | |
| 249 const Quadratic& quad2; | |
| 250 Intersections& intersections; | |
| 251 int depth; | |
| 252 int splits; | |
| 253 double quad1Divisions; // line segments to approximate original within error | |
| 254 double quad2Divisions; | |
| 255 double coinMinT1; // range of Ts where approximate line intersected curve | |
| 256 double coinMaxT1; | |
| 257 double coinMinT2; | |
| 258 double coinMaxT2; | |
| 259 }; | |
| 260 | |
| 261 #include "LineParameters.h" | |
| 262 | |
| 263 static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2
, Intersections& i) { | |
| 264 // look to see if non-coincident data basically has unsortable tangents | |
| 265 | |
| 266 // look to see if a point between non-coincident data is on the curve | |
| 267 int cIndex; | |
| 268 for (int uIndex = 0; uIndex < i.fUsed; ) { | |
| 269 double bestDist1 = 1; | |
| 270 double bestDist2 = 1; | |
| 271 int closest1 = -1; | |
| 272 int closest2 = -1; | |
| 273 for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) { | |
| 274 double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]); | |
| 275 if (bestDist1 > dist) { | |
| 276 bestDist1 = dist; | |
| 277 closest1 = cIndex; | |
| 278 } | |
| 279 dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]); | |
| 280 if (bestDist2 > dist) { | |
| 281 bestDist2 = dist; | |
| 282 closest2 = cIndex; | |
| 283 } | |
| 284 } | |
| 285 _Line ends; | |
| 286 _Point mid; | |
| 287 double t1 = i.fT[0][uIndex]; | |
| 288 xy_at_t(q1, t1, ends[0].x, ends[0].y); | |
| 289 xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y); | |
| 290 double midT = (t1 + i.fCoincidentT[0][closest1]) / 2; | |
| 291 xy_at_t(q1, midT, mid.x, mid.y); | |
| 292 LineParameters params; | |
| 293 params.lineEndPoints(ends); | |
| 294 double midDist = params.pointDistance(mid); | |
| 295 // Note that we prefer to always measure t error, which does not scale, | |
| 296 // instead of point error, which is scale dependent. FIXME | |
| 297 if (!approximately_zero(midDist)) { | |
| 298 ++uIndex; | |
| 299 continue; | |
| 300 } | |
| 301 double t2 = i.fT[1][uIndex]; | |
| 302 xy_at_t(q2, t2, ends[0].x, ends[0].y); | |
| 303 xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y); | |
| 304 midT = (t2 + i.fCoincidentT[1][closest2]) / 2; | |
| 305 xy_at_t(q2, midT, mid.x, mid.y); | |
| 306 params.lineEndPoints(ends); | |
| 307 midDist = params.pointDistance(mid); | |
| 308 if (!approximately_zero(midDist)) { | |
| 309 ++uIndex; | |
| 310 continue; | |
| 311 } | |
| 312 // if both midpoints are close to the line, lengthen coincident span | |
| 313 int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs | |
| 314 if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1]))
{ | |
| 315 i.fCoincidentT[0][closest1] = t1; | |
| 316 } | |
| 317 cEnd = closest2 ^ 1; | |
| 318 if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2]))
{ | |
| 319 i.fCoincidentT[0][closest2] = t2; | |
| 320 } | |
| 321 int remaining = --i.fUsed - uIndex; | |
| 322 if (remaining > 0) { | |
| 323 memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) *
remaining); | |
| 324 memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) *
remaining); | |
| 325 } | |
| 326 } | |
| 327 // if coincident data is subjectively a tiny span, replace it with a single
point | |
| 328 for (cIndex = 0; cIndex < i.fCoincidentUsed; ) { | |
| 329 double start1 = i.fCoincidentT[0][cIndex]; | |
| 330 double end1 = i.fCoincidentT[0][cIndex + 1]; | |
| 331 _Line ends1; | |
| 332 xy_at_t(q1, start1, ends1[0].x, ends1[0].y); | |
| 333 xy_at_t(q1, end1, ends1[1].x, ends1[1].y); | |
| 334 if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0]
.y, ends1[1].y)) { | |
| 335 cIndex += 2; | |
| 336 continue; | |
| 337 } | |
| 338 double start2 = i.fCoincidentT[1][cIndex]; | |
| 339 double end2 = i.fCoincidentT[1][cIndex + 1]; | |
| 340 _Line ends2; | |
| 341 xy_at_t(q2, start2, ends2[0].x, ends2[0].y); | |
| 342 xy_at_t(q2, end2, ends2[1].x, ends2[1].y); | |
| 343 // again, approximately should be used with T values, not points FIXME | |
| 344 if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0]
.y, ends2[1].y)) { | |
| 345 cIndex += 2; | |
| 346 continue; | |
| 347 } | |
| 348 if (approximately_less_than_zero(start1) || approximately_less_than_zero
(end1)) { | |
| 349 start1 = 0; | |
| 350 } else if (approximately_greater_than_one(start1) || approximately_great
er_than_one(end1)) { | |
| 351 start1 = 1; | |
| 352 } else { | |
| 353 start1 = (start1 + end1) / 2; | |
| 354 } | |
| 355 if (approximately_less_than_zero(start2) || approximately_less_than_zero
(end2)) { | |
| 356 start2 = 0; | |
| 357 } else if (approximately_greater_than_one(start2) || approximately_great
er_than_one(end2)) { | |
| 358 start2 = 1; | |
| 359 } else { | |
| 360 start2 = (start2 + end2) / 2; | |
| 361 } | |
| 362 i.insert(start1, start2); | |
| 363 i.fCoincidentUsed -= 2; | |
| 364 int remaining = i.fCoincidentUsed - cIndex; | |
| 365 if (remaining > 0) { | |
| 366 memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2],
sizeof(i.fCoincidentT[0][0]) * remaining); | |
| 367 memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2],
sizeof(i.fCoincidentT[1][0]) * remaining); | |
| 368 } | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) { | |
| 373 if (implicit_matches(q1, q2)) { | |
| 374 // FIXME: compute T values | |
| 375 // compute the intersections of the ends to find the coincident span | |
| 376 bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y); | |
| 377 double t; | |
| 378 if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) { | |
| 379 i.addCoincident(t, 0); | |
| 380 } | |
| 381 if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) { | |
| 382 i.addCoincident(t, 1); | |
| 383 } | |
| 384 useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y); | |
| 385 if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) { | |
| 386 i.addCoincident(0, t); | |
| 387 } | |
| 388 if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) { | |
| 389 i.addCoincident(1, t); | |
| 390 } | |
| 391 SkASSERT(i.fCoincidentUsed <= 2); | |
| 392 return i.fCoincidentUsed > 0; | |
| 393 } | |
| 394 QuadraticIntersections q(q1, q2, i); | |
| 395 bool result = q.intersect(); | |
| 396 // FIXME: partial coincidence detection is currently poor. For now, try | |
| 397 // to fix up the data after the fact. In the future, revisit the error | |
| 398 // term to try to avoid this kind of result in the first place. | |
| 399 if (i.fUsed && i.fCoincidentUsed) { | |
| 400 hackToFixPartialCoincidence(q1, q2, i); | |
| 401 } | |
| 402 return result; | |
| 403 } | |
| OLD | NEW |