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 "SkOpAngle.h" | 8 #include "SkOpAngle.h" |
9 #include "SkOpSegment.h" | 9 #include "SkOpSegment.h" |
10 #include "SkPathOpsCurve.h" | 10 #include "SkPathOpsCurve.h" |
11 #include "SkTSort.h" | 11 #include "SkTSort.h" |
12 | 12 |
13 #if DEBUG_ANGLE | 13 #if DEBUG_ANGLE |
14 #include "SkString.h" | 14 #include "SkString.h" |
15 | |
16 static const char funcName[] = "SkOpSegment::operator<"; | |
17 static const int bugChar = strlen(funcName) + 1; | |
18 #endif | 15 #endif |
19 | 16 |
20 /* Angles are sorted counterclockwise. The smallest angle has a positive x and t
he smallest | 17 /* Angles are sorted counterclockwise. The smallest angle has a positive x and t
he smallest |
21 positive y. The largest angle has a positive x and a zero y. */ | 18 positive y. The largest angle has a positive x and a zero y. */ |
22 | 19 |
23 #if DEBUG_ANGLE | 20 #if DEBUG_ANGLE |
24 static bool CompareResult(SkString* bugOut, const char* append, bool compare
) { | 21 static bool CompareResult(SkString* bugOut, int append, bool compare) { |
25 bugOut->appendf("%s", append); | 22 SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append); |
26 bugOut->writable_str()[bugChar] = "><"[compare]; | |
27 SkDebugf("%s\n", bugOut->c_str()); | |
28 return compare; | 23 return compare; |
29 } | 24 } |
30 | 25 |
31 #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compa
re) | 26 #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compa
re) |
32 #else | 27 #else |
33 #define COMPARE_RESULT(append, compare) compare | 28 #define COMPARE_RESULT(append, compare) compare |
34 #endif | 29 #endif |
35 | 30 |
36 bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result)
const{ | 31 /* quarter angle values for sector |
| 32 |
| 33 31 x > 0, y == 0 horizontal line (to the right) |
| 34 0 x > 0, y == epsilon quad/cubic horizontal tangent eventually going +
y |
| 35 1 x > 0, y > 0, x > y nearer horizontal angle |
| 36 2 x + e == y quad/cubic 45 going horiz |
| 37 3 x > 0, y > 0, x == y 45 angle |
| 38 4 x == y + e quad/cubic 45 going vert |
| 39 5 x > 0, y > 0, x < y nearer vertical angle |
| 40 6 x == epsilon, y > 0 quad/cubic vertical tangent eventually going +x |
| 41 7 x == 0, y > 0 vertical line (to the top) |
| 42 |
| 43 8 7 6 |
| 44 9 | 5 |
| 45 10 | 4 |
| 46 11 | 3 |
| 47 12 \ | / 2 |
| 48 13 | 1 |
| 49 14 | 0 |
| 50 15 --------------+------------- 31 |
| 51 16 | 30 |
| 52 17 | 29 |
| 53 18 / | \ 28 |
| 54 19 | 27 |
| 55 20 | 26 |
| 56 21 | 25 |
| 57 22 23 24 |
| 58 */ |
| 59 |
| 60 // return true if lh < this < rh |
| 61 bool SkOpAngle::after(const SkOpAngle* test) const { |
| 62 const SkOpAngle& lh = *test; |
| 63 const SkOpAngle& rh = *lh.fNext; |
| 64 SkASSERT(&lh != &rh); |
| 65 #if DEBUG_ANGLE |
| 66 SkString bugOut; |
| 67 bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| 68 " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| 69 " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__, |
| 70 lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd
, |
| 71 lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd), |
| 72 fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->
t(fStart), |
| 73 fSegment->t(fEnd), |
| 74 rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd
, |
| 75 rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd)); |
| 76 #endif |
| 77 if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) { |
| 78 return COMPARE_RESULT(1, true); |
| 79 } |
| 80 if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) { |
| 81 return COMPARE_RESULT(2, true); |
| 82 } |
| 83 if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) { |
| 84 return COMPARE_RESULT(3, true); |
| 85 } |
| 86 #if DEBUG_ANGLE // reset bugOut with computed sectors |
| 87 bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| 88 " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| 89 " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__, |
| 90 lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd
, |
| 91 lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd), |
| 92 fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->
t(fStart), |
| 93 fSegment->t(fEnd), |
| 94 rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd
, |
| 95 rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd)); |
| 96 #endif |
| 97 bool ltrOverlap = (lh.fSectorMask | rh.fSectorMask) & fSectorMask; |
| 98 bool lrOverlap = lh.fSectorMask & rh.fSectorMask; |
| 99 int lrOrder; // set to -1 if either order works |
| 100 if (!lrOverlap) { // no lh/rh sector overlap |
| 101 if (!ltrOverlap) { // no lh/this/rh sector overlap |
| 102 return COMPARE_RESULT(4, (lh.fSectorEnd > rh.fSectorStart) |
| 103 ^ (fSectorStart > lh.fSectorEnd) ^ (fSectorStart > rh.fSecto
rStart)); |
| 104 } |
| 105 int lrGap = (rh.fSectorStart - lh.fSectorStart + 32) & 0x1f; |
| 106 /* A tiny change can move the start +/- 4. The order can only be determi
ned if |
| 107 lr gap is not 12 to 20 or -12 to -20. |
| 108 -31 ..-21 1 |
| 109 -20 ..-12 -1 |
| 110 -11 .. -1 0 |
| 111 0 shouldn't get here |
| 112 11 .. 1 1 |
| 113 12 .. 20 -1 |
| 114 21 .. 31 0 |
| 115 */ |
| 116 lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1; |
| 117 } else { |
| 118 lrOrder = (int) lh.orderable(rh); |
| 119 if (!ltrOverlap) { |
| 120 return COMPARE_RESULT(5, !lrOrder); |
| 121 } |
| 122 } |
| 123 int ltOrder; |
| 124 SkASSERT((lh.fSectorMask & fSectorMask) || (rh.fSectorMask & fSectorMask)); |
| 125 if (lh.fSectorMask & fSectorMask) { |
| 126 ltOrder = (int) lh.orderable(*this); |
| 127 } else { |
| 128 int ltGap = (fSectorStart - lh.fSectorStart + 32) & 0x1f; |
| 129 ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1; |
| 130 } |
| 131 int trOrder; |
| 132 if (rh.fSectorMask & fSectorMask) { |
| 133 trOrder = (int) orderable(rh); |
| 134 } else { |
| 135 int trGap = (rh.fSectorStart - fSectorStart + 32) & 0x1f; |
| 136 trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1; |
| 137 } |
| 138 if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) { |
| 139 return COMPARE_RESULT(7, lrOrder ? (ltOrder & trOrder) : (ltOrder | trOr
der)); |
| 140 } |
| 141 SkASSERT(lrOrder >= 0 || ltOrder >= 0 || trOrder >= 0); |
| 142 // There's not enough information to sort. Get the pairs of angles in opposite p
lanes. |
| 143 // If an order is < 0, the pair is already in an opposite plane. Check the remai
ning pairs. |
| 144 // FIXME : once all variants are understood, rewrite this more simply |
| 145 if (ltOrder == 0 && lrOrder == 0) { |
| 146 SkASSERT(trOrder < 0); |
| 147 // FIXME : once this is verified to work, remove one opposite angle call |
| 148 SkDEBUGCODE(bool lrOpposite = lh.oppositePlanes(rh)); |
| 149 bool ltOpposite = lh.oppositePlanes(*this); |
| 150 SkASSERT(lrOpposite != ltOpposite); |
| 151 return COMPARE_RESULT(8, ltOpposite); |
| 152 } else if (ltOrder == 1 && trOrder == 0) { |
| 153 SkASSERT(lrOrder < 0); |
| 154 SkDEBUGCODE(bool ltOpposite = lh.oppositePlanes(*this)); |
| 155 bool trOpposite = oppositePlanes(rh); |
| 156 SkASSERT(ltOpposite != trOpposite); |
| 157 return COMPARE_RESULT(9, trOpposite); |
| 158 } else if (lrOrder == 1 && trOrder == 1) { |
| 159 SkASSERT(ltOrder < 0); |
| 160 SkDEBUGCODE(bool trOpposite = oppositePlanes(rh)); |
| 161 bool lrOpposite = lh.oppositePlanes(rh); |
| 162 SkASSERT(lrOpposite != trOpposite); |
| 163 return COMPARE_RESULT(10, lrOpposite); |
| 164 } |
| 165 if (lrOrder < 0) { |
| 166 if (ltOrder < 0) { |
| 167 return COMPARE_RESULT(11, trOrder); |
| 168 } |
| 169 return COMPARE_RESULT(12, ltOrder); |
| 170 } |
| 171 return COMPARE_RESULT(13, !lrOrder); |
| 172 } |
| 173 |
| 174 // given a line, see if the opposite curve's convex hull is all on one side |
| 175 // returns -1=not on one side 0=this CW of test 1=this CCW of test |
| 176 int SkOpAngle::allOnOneSide(const SkOpAngle& test) const { |
| 177 SkASSERT(!fIsCurve); |
| 178 SkASSERT(test.fIsCurve); |
| 179 const SkDPoint& origin = test.fCurvePart[0]; |
| 180 SkVector line; |
| 181 if (fSegment->verb() == SkPath::kLine_Verb) { |
| 182 const SkPoint* linePts = fSegment->pts(); |
| 183 int lineStart = fStart < fEnd ? 0 : 1; |
| 184 line = linePts[lineStart ^ 1] - linePts[lineStart]; |
| 185 } else { |
| 186 SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoi
nt() }; |
| 187 line = shortPts[1] - shortPts[0]; |
| 188 } |
| 189 float crosses[3]; |
| 190 SkPath::Verb testVerb = test.fSegment->verb(); |
| 191 int iMax = SkPathOpsVerbToPoints(testVerb); |
| 192 // SkASSERT(origin == test.fCurveHalf[0]); |
| 193 const SkDCubic& testCurve = test.fCurvePart; |
| 194 // do { |
| 195 for (int index = 1; index <= iMax; ++index) { |
| 196 float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY)); |
| 197 float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX)); |
| 198 crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2; |
| 199 } |
| 200 if (crosses[0] * crosses[1] < 0) { |
| 201 return -1; |
| 202 } |
| 203 if (SkPath::kCubic_Verb == testVerb) { |
| 204 if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) { |
| 205 return -1; |
| 206 } |
| 207 } |
| 208 if (crosses[0]) { |
| 209 return crosses[0] < 0; |
| 210 } |
| 211 if (crosses[1]) { |
| 212 return crosses[1] < 0; |
| 213 } |
| 214 if (SkPath::kCubic_Verb == testVerb && crosses[2]) { |
| 215 return crosses[2] < 0; |
| 216 } |
| 217 fUnorderable = true; |
| 218 return -1; |
| 219 } |
| 220 |
| 221 bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result)
const { |
37 double absX = fabs(x); | 222 double absX = fabs(x); |
38 double absY = fabs(y); | 223 double absY = fabs(y); |
39 double length = absX < absY ? absX / 2 + absY : absX + absY / 2; | 224 double length = absX < absY ? absX / 2 + absY : absX + absY / 2; |
40 int exponent; | 225 int exponent; |
41 (void) frexp(length, &exponent); | 226 (void) frexp(length, &exponent); |
42 double epsilon = ldexp(FLT_EPSILON, exponent); | 227 double epsilon = ldexp(FLT_EPSILON, exponent); |
43 SkPath::Verb verb = fSegment->verb(); | 228 SkPath::Verb verb = fSegment->verb(); |
44 SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb); | 229 SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb); |
45 // FIXME: the quad and cubic factors are made up ; determine actual values | 230 // FIXME: the quad and cubic factors are made up ; determine actual values |
46 double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon; | 231 double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon; |
47 double xSlop = slop; | 232 double xSlop = slop; |
48 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _
copysign ? | 233 double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _
copysign ? |
49 double x1 = x - xSlop; | 234 double x1 = x - xSlop; |
50 double y1 = y + ySlop; | 235 double y1 = y + ySlop; |
51 double x_ry1 = x1 * ry; | 236 double x_ry1 = x1 * ry; |
52 double rx_y1 = rx * y1; | 237 double rx_y1 = rx * y1; |
53 *result = x_ry1 < rx_y1; | 238 *result = x_ry1 < rx_y1; |
54 double x2 = x + xSlop; | 239 double x2 = x + xSlop; |
55 double y2 = y - ySlop; | 240 double y2 = y - ySlop; |
56 double x_ry2 = x2 * ry; | 241 double x_ry2 = x2 * ry; |
57 double rx_y2 = rx * y2; | 242 double rx_y2 = rx * y2; |
58 bool less2 = x_ry2 < rx_y2; | 243 bool less2 = x_ry2 < rx_y2; |
59 return *result == less2; | 244 return *result == less2; |
60 } | 245 } |
61 | 246 |
62 /* | 247 bool SkOpAngle::checkCrossesZero() const { |
63 for quads and cubics, set up a parameterized line (e.g. LineParameters ) | 248 int start = SkTMin(fSectorStart, fSectorEnd); |
64 for points [0] to [1]. See if point [2] is on that line, or on one side | 249 int end = SkTMax(fSectorStart, fSectorEnd); |
65 or the other. If it both quads' end points are on the same side, choose | 250 bool crossesZero = end - start > 16; |
66 the shorter tangent. If the tangents are equal, choose the better second | 251 return crossesZero; |
67 tangent angle | 252 } |
68 | 253 |
69 FIXME: maybe I could set up LineParameters lazily | 254 bool SkOpAngle::checkParallel(const SkOpAngle& rh) const { |
| 255 SkDVector scratch[2]; |
| 256 const SkDVector* sweep, * tweep; |
| 257 if (!fUnorderedSweep) { |
| 258 sweep = fSweep; |
| 259 } else { |
| 260 scratch[0] = fCurvePart[1] - fCurvePart[0]; |
| 261 sweep = &scratch[0]; |
| 262 } |
| 263 if (!rh.fUnorderedSweep) { |
| 264 tweep = rh.fSweep; |
| 265 } else { |
| 266 scratch[1] = rh.fCurvePart[1] - rh.fCurvePart[0]; |
| 267 tweep = &scratch[1]; |
| 268 } |
| 269 double s0xt0 = sweep->crossCheck(*tweep); |
| 270 if (tangentsDiverge(rh, s0xt0)) { |
| 271 return s0xt0 < 0; |
| 272 } |
| 273 SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0]; |
| 274 SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0]; |
| 275 double m0xm1 = m0.crossCheck(m1); |
| 276 if (m0xm1 == 0) { |
| 277 fUnorderable = true; |
| 278 rh.fUnorderable = true; |
| 279 return true; |
| 280 } |
| 281 return m0xm1 < 0; |
| 282 } |
| 283 |
| 284 // the original angle is too short to get meaningful sector information |
| 285 // lengthen it until it is long enough to be meaningful or leave it unset if len
gthening it |
| 286 // would cause it to intersect one of the adjacent angles |
| 287 bool SkOpAngle::computeSector() { |
| 288 if (fComputedSector) { |
| 289 // FIXME: logically, this should return !fUnorderable, but doing so brea
ks testQuadratic51 |
| 290 // -- but in general, this code may not work so this may be the least of
problems |
| 291 // adding the bang fixes testQuads46x in release, however |
| 292 return fUnorderable; |
| 293 } |
| 294 SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small()); |
| 295 fComputedSector = true; |
| 296 int step = fStart < fEnd ? 1 : -1; |
| 297 int limit = step > 0 ? fSegment->count() : -1; |
| 298 int checkEnd = fEnd; |
| 299 do { |
| 300 // advance end |
| 301 const SkOpSpan& span = fSegment->span(checkEnd); |
| 302 const SkOpSegment* other = span.fOther; |
| 303 int oCount = other->count(); |
| 304 for (int oIndex = 0; oIndex < oCount; ++oIndex) { |
| 305 const SkOpSpan& oSpan = other->span(oIndex); |
| 306 if (oSpan.fOther != fSegment) { |
| 307 continue; |
| 308 } |
| 309 if (oSpan.fOtherIndex == checkEnd) { |
| 310 continue; |
| 311 } |
| 312 if (!approximately_equal(oSpan.fOtherT, span.fT)) { |
| 313 continue; |
| 314 } |
| 315 goto recomputeSector; |
| 316 } |
| 317 checkEnd += step; |
| 318 } while (checkEnd != limit); |
| 319 recomputeSector: |
| 320 if (checkEnd == fEnd || checkEnd - step == fEnd) { |
| 321 fUnorderable = true; |
| 322 return false; |
| 323 } |
| 324 fEnd = checkEnd - step; |
| 325 setSpans(); |
| 326 setSector(); |
| 327 return !fUnorderable; |
| 328 } |
| 329 |
| 330 // returns -1 if overlaps 0 if no overlap cw 1 if no overlap ccw |
| 331 int SkOpAngle::convexHullOverlaps(const SkOpAngle& rh) const { |
| 332 const SkDVector* sweep = fSweep; |
| 333 const SkDVector* tweep = rh.fSweep; |
| 334 double s0xs1 = sweep[0].crossCheck(sweep[1]); |
| 335 double s0xt0 = sweep[0].crossCheck(tweep[0]); |
| 336 double s1xt0 = sweep[1].crossCheck(tweep[0]); |
| 337 bool tBetweenS = s0xs1 > 0 ? s0xt0 > 0 && s1xt0 < 0 : s0xt0 < 0 && s1xt0 > 0
; |
| 338 double s0xt1 = sweep[0].crossCheck(tweep[1]); |
| 339 double s1xt1 = sweep[1].crossCheck(tweep[1]); |
| 340 tBetweenS |= s0xs1 > 0 ? s0xt1 > 0 && s1xt1 < 0 : s0xt1 < 0 && s1xt1 > 0; |
| 341 double t0xt1 = tweep[0].crossCheck(tweep[1]); |
| 342 if (tBetweenS) { |
| 343 return -1; |
| 344 } |
| 345 if ((s0xt0 == 0 && s1xt1 == 0) || (s1xt0 == 0 && s0xt1 == 0)) { // s0 to s1
equals t0 to t1 |
| 346 return -1; |
| 347 } |
| 348 bool sBetweenT = t0xt1 > 0 ? s0xt0 < 0 && s0xt1 > 0 : s0xt0 > 0 && s0xt1 < 0
; |
| 349 sBetweenT |= t0xt1 > 0 ? s1xt0 < 0 && s1xt1 > 0 : s1xt0 > 0 && s1xt1 < 0; |
| 350 if (sBetweenT) { |
| 351 return -1; |
| 352 } |
| 353 // if all of the sweeps are in the same half plane, then the order of any pa
ir is enough |
| 354 if (s0xt0 >= 0 && s0xt1 >= 0 && s1xt0 >= 0 && s1xt1 >= 0) { |
| 355 return 0; |
| 356 } |
| 357 if (s0xt0 <= 0 && s0xt1 <= 0 && s1xt0 <= 0 && s1xt1 <= 0) { |
| 358 return 1; |
| 359 } |
| 360 // if the outside sweeps are greater than 180 degress: |
| 361 // first assume the inital tangents are the ordering |
| 362 // if the midpoint direction matches the inital order, that is enough |
| 363 SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0]; |
| 364 SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0]; |
| 365 double m0xm1 = m0.crossCheck(m1); |
| 366 if (s0xt0 > 0 && m0xm1 > 0) { |
| 367 return 0; |
| 368 } |
| 369 if (s0xt0 < 0 && m0xm1 < 0) { |
| 370 return 1; |
| 371 } |
| 372 if (tangentsDiverge(rh, s0xt0)) { |
| 373 return s0xt0 < 0; |
| 374 } |
| 375 return m0xm1 < 0; |
| 376 } |
| 377 |
| 378 // OPTIMIZATION: longest can all be either lazily computed here or precomputed i
n setup |
| 379 double SkOpAngle::distEndRatio(double dist) const { |
| 380 double longest = 0; |
| 381 const SkOpSegment& segment = *this->segment(); |
| 382 int ptCount = SkPathOpsVerbToPoints(segment.verb()); |
| 383 const SkPoint* pts = segment.pts(); |
| 384 for (int idx1 = 0; idx1 <= ptCount - 1; ++idx1) { |
| 385 for (int idx2 = idx1 + 1; idx2 <= ptCount; ++idx2) { |
| 386 if (idx1 == idx2) { |
| 387 continue; |
| 388 } |
| 389 SkDVector v; |
| 390 v.set(pts[idx2] - pts[idx1]); |
| 391 double lenSq = v.lengthSquared(); |
| 392 longest = SkTMax(longest, lenSq); |
| 393 } |
| 394 } |
| 395 return sqrt(longest) / dist; |
| 396 } |
| 397 |
| 398 bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const { |
| 399 SkPath::Verb lVerb = fSegment->verb(); |
| 400 SkPath::Verb rVerb = rh.fSegment->verb(); |
| 401 int lPts = SkPathOpsVerbToPoints(lVerb); |
| 402 int rPts = SkPathOpsVerbToPoints(rVerb); |
| 403 SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}}, |
| 404 {{fCurvePart[0], fCurvePart[lPts]}}}; |
| 405 if (rays[0][1] == rays[1][1]) { |
| 406 return checkParallel(rh); |
| 407 } |
| 408 double smallTs[2] = {-1, -1}; |
| 409 bool limited[2] = {false, false}; |
| 410 for (int index = 0; index < 2; ++index) { |
| 411 const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; |
| 412 SkIntersections i; |
| 413 (*CurveIntersectRay[index ? rPts : lPts])(segment.pts(), rays[index], &i
); |
| 414 // SkASSERT(i.used() >= 1); |
| 415 if (i.used() <= 1) { |
| 416 continue; |
| 417 } |
| 418 double tStart = segment.t(index ? rh.fStart : fStart); |
| 419 double tEnd = segment.t(index ? rh.fEnd : fEnd); |
| 420 bool testAscends = index ? rh.fStart < rh.fEnd : fStart < fEnd; |
| 421 double t = testAscends ? 0 : 1; |
| 422 for (int idx2 = 0; idx2 < i.used(); ++idx2) { |
| 423 double testT = i[0][idx2]; |
| 424 if (!approximately_between_orderable(tStart, testT, tEnd)) { |
| 425 continue; |
| 426 } |
| 427 if (approximately_equal_orderable(tStart, testT)) { |
| 428 continue; |
| 429 } |
| 430 smallTs[index] = t = testAscends ? SkTMax(t, testT) : SkTMin(t, test
T); |
| 431 limited[index] = approximately_equal_orderable(t, tEnd); |
| 432 } |
| 433 } |
| 434 #if 0 |
| 435 if (smallTs[0] < 0 && smallTs[1] < 0) { // if neither ray intersects, do en
dpoint sort |
| 436 double m0xm1 = 0; |
| 437 if (lVerb == SkPath::kLine_Verb) { |
| 438 SkASSERT(rVerb != SkPath::kLine_Verb); |
| 439 SkDVector m0 = rays[1][1] - fCurvePart[0]; |
| 440 SkDPoint endPt; |
| 441 endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]); |
| 442 SkDVector m1 = endPt - fCurvePart[0]; |
| 443 m0xm1 = m0.crossCheck(m1); |
| 444 } |
| 445 if (rVerb == SkPath::kLine_Verb) { |
| 446 SkDPoint endPt; |
| 447 endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]); |
| 448 SkDVector m0 = endPt - fCurvePart[0]; |
| 449 SkDVector m1 = rays[0][1] - fCurvePart[0]; |
| 450 m0xm1 = m0.crossCheck(m1); |
| 451 } |
| 452 if (m0xm1 != 0) { |
| 453 return m0xm1 < 0; |
| 454 } |
| 455 } |
| 456 #endif |
| 457 bool sRayLonger = false; |
| 458 SkDVector sCept = {0, 0}; |
| 459 double sCeptT = -1; |
| 460 int sIndex = -1; |
| 461 bool useIntersect = false; |
| 462 for (int index = 0; index < 2; ++index) { |
| 463 if (smallTs[index] < 0) { |
| 464 continue; |
| 465 } |
| 466 const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; |
| 467 const SkDPoint& dPt = segment.dPtAtT(smallTs[index]); |
| 468 SkDVector cept = dPt - rays[index][0]; |
| 469 // If this point is on the curve, it should have been detected earlier b
y ordinary |
| 470 // curve intersection. This may be hard to determine in general, but for
lines, |
| 471 // the point could be close to or equal to its end, but shouldn't be nea
r the start. |
| 472 if ((index ? lPts : rPts) == 1) { |
| 473 SkDVector total = rays[index][1] - rays[index][0]; |
| 474 if (cept.lengthSquared() * 2 < total.lengthSquared()) { |
| 475 continue; |
| 476 } |
| 477 } |
| 478 SkDVector end = rays[index][1] - rays[index][0]; |
| 479 if (cept.fX * end.fX < 0 || cept.fY * end.fY < 0) { |
| 480 continue; |
| 481 } |
| 482 double rayDist = cept.length(); |
| 483 double endDist = end.length(); |
| 484 bool rayLonger = rayDist > endDist; |
| 485 if (limited[0] && limited[1] && rayLonger) { |
| 486 useIntersect = true; |
| 487 sRayLonger = rayLonger; |
| 488 sCept = cept; |
| 489 sCeptT = smallTs[index]; |
| 490 sIndex = index; |
| 491 break; |
| 492 } |
| 493 double delta = fabs(rayDist - endDist); |
| 494 double minX, minY, maxX, maxY; |
| 495 minX = minY = SK_ScalarInfinity; |
| 496 maxX = maxY = -SK_ScalarInfinity; |
| 497 const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart; |
| 498 int ptCount = index ? rPts : lPts; |
| 499 for (int idx2 = 0; idx2 <= ptCount; ++idx2) { |
| 500 minX = SkTMin(minX, curve[idx2].fX); |
| 501 minY = SkTMin(minY, curve[idx2].fY); |
| 502 maxX = SkTMax(maxX, curve[idx2].fX); |
| 503 maxY = SkTMax(maxY, curve[idx2].fY); |
| 504 } |
| 505 double maxWidth = SkTMax(maxX - minX, maxY - minY); |
| 506 delta /= maxWidth; |
| 507 if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic
number |
| 508 sRayLonger = rayLonger; |
| 509 sCept = cept; |
| 510 sCeptT = smallTs[index]; |
| 511 sIndex = index; |
| 512 } |
| 513 } |
| 514 if (useIntersect) { |
| 515 const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart; |
| 516 const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment; |
| 517 double tStart = segment.t(sIndex ? rh.fStart : fStart); |
| 518 SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0
]; |
| 519 double septDir = mid.crossCheck(sCept); |
| 520 if (!septDir) { |
| 521 return checkParallel(rh); |
| 522 } |
| 523 return sRayLonger ^ (sIndex == 0) ^ (septDir < 0); |
| 524 } else { |
| 525 return checkParallel(rh); |
| 526 } |
| 527 } |
| 528 |
| 529 // Most of the time, the first one can be found trivially by detecting the small
est sector value. |
| 530 // If all angles have the same sector value, actual sorting is required. |
| 531 const SkOpAngle* SkOpAngle::findFirst() const { |
| 532 const SkOpAngle* best = this; |
| 533 int bestStart = SkTMin(fSectorStart, fSectorEnd); |
| 534 const SkOpAngle* angle = this; |
| 535 while ((angle = angle->fNext) != this) { |
| 536 int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| 537 if (angleEnd < bestStart) { |
| 538 return angle; // we wrapped around |
| 539 } |
| 540 int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| 541 if (bestStart > angleStart) { |
| 542 best = angle; |
| 543 bestStart = angleStart; |
| 544 } |
| 545 } |
| 546 // back up to the first possible angle |
| 547 const SkOpAngle* firstBest = best; |
| 548 angle = best; |
| 549 int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd); |
| 550 while ((angle = angle->previous()) != firstBest) { |
| 551 if (angle->fStop) { |
| 552 break; |
| 553 } |
| 554 int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| 555 // angles that are smaller by one aren't necessary better, since the lar
ger may be a line |
| 556 // and the smaller may be a curve that curls to the other side of the li
ne. |
| 557 if (bestEnd + 1 < angleStart) { |
| 558 return best; |
| 559 } |
| 560 best = angle; |
| 561 bestEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| 562 } |
| 563 // in the case where all angles are nearly in the same sector, check the ord
er to find the best |
| 564 firstBest = best; |
| 565 angle = best; |
| 566 do { |
| 567 angle = angle->fNext; |
| 568 if (angle->fStop) { |
| 569 return firstBest; |
| 570 } |
| 571 bool orderable = best->orderable(*angle); // note: may return an unorde
rable angle |
| 572 if (orderable == 0) { |
| 573 return angle; |
| 574 } |
| 575 best = angle; |
| 576 } while (angle != firstBest); |
| 577 // if the angles are equally ordered, fall back on the initial tangent |
| 578 bool foundBelow = false; |
| 579 while ((angle = angle->fNext)) { |
| 580 SkDVector scratch[2]; |
| 581 const SkDVector* sweep; |
| 582 if (!angle->fUnorderedSweep) { |
| 583 sweep = angle->fSweep; |
| 584 } else { |
| 585 scratch[0] = angle->fCurvePart[1] - angle->fCurvePart[0]; |
| 586 sweep = &scratch[0]; |
| 587 } |
| 588 bool isAbove = sweep->fY <= 0; |
| 589 if (isAbove && foundBelow) { |
| 590 return angle; |
| 591 } |
| 592 foundBelow |= !isAbove; |
| 593 if (angle == firstBest) { |
| 594 return NULL; // should not loop around |
| 595 } |
| 596 } |
| 597 SkASSERT(0); // should never get here |
| 598 return NULL; |
| 599 } |
| 600 |
| 601 /* y<0 y==0 y>0 x<0 x==0 x>0 xy<0 xy==0 xy>0 |
| 602 0 x x x |
| 603 1 x x x |
| 604 2 x x x |
| 605 3 x x x |
| 606 4 x x x |
| 607 5 x x x |
| 608 6 x x x |
| 609 7 x x x |
| 610 8 x x x |
| 611 9 x x x |
| 612 10 x x x |
| 613 11 x x x |
| 614 12 x x x |
| 615 13 x x x |
| 616 14 x x x |
| 617 15 x x x |
70 */ | 618 */ |
71 bool SkOpAngle::operator<(const SkOpAngle& rh) const { // this/lh: left-hand; r
h: right-hand | 619 int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const { |
72 double y = dy(); | 620 double absX = fabs(x); |
73 double ry = rh.dy(); | 621 double absY = fabs(y); |
| 622 double xy = SkPath::kLine_Verb == verb || !AlmostEqualUlps(absX, absY) ? abs
X - absY : 0; |
| 623 // If there are four quadrants and eight octants, and since the Latin for si
xteen is sedecim, |
| 624 // one could coin the term sedecimant for a space divided into 16 sections. |
| 625 // http://english.stackexchange.com/questions/133688/word-for-something-parti
tioned-into-16-parts |
| 626 static const int sedecimant[3][3][3] = { |
| 627 // y<0 y==0 y>0 |
| 628 // x<0 x==0 x>0 x<0 x==0 x>0 x<0 x==0 x>0 |
| 629 {{ 4, 3, 2}, { 7, -1, 15}, {10, 11, 12}}, // abs(x) < abs(y) |
| 630 {{ 5, -1, 1}, {-1, -1, -1}, { 9, -1, 13}}, // abs(x) == abs(y) |
| 631 {{ 6, 3, 0}, { 7, -1, 15}, { 8, 11, 14}}, // abs(x) > abs(y) |
| 632 }; |
| 633 int sector = sedecimant[(xy >= 0) + (xy > 0)][(y >= 0) + (y > 0)][(x >= 0) +
(x > 0)] * 2 + 1; |
| 634 SkASSERT(SkPath::kLine_Verb == verb || sector >= 0); |
| 635 return sector; |
| 636 } |
| 637 |
| 638 // OPTIMIZE: if this loops to only one other angle, after first compare fails, i
nsert on other side |
| 639 // OPTIMIZE: return where insertion succeeded. Then, start next insertion on opp
osite side |
| 640 void SkOpAngle::insert(SkOpAngle* angle) { |
| 641 if (angle->fNext) { |
| 642 if (loopCount() >= angle->loopCount()) { |
| 643 if (!merge(angle)) { |
| 644 return; |
| 645 } |
| 646 } else if (fNext) { |
| 647 if (!angle->merge(this)) { |
| 648 return; |
| 649 } |
| 650 } else { |
| 651 angle->insert(this); |
| 652 } |
| 653 return; |
| 654 } |
| 655 bool singleton = NULL == fNext; |
| 656 if (singleton) { |
| 657 fNext = this; |
| 658 } |
| 659 SkOpAngle* next = fNext; |
| 660 if (next->fNext == this) { |
| 661 if (singleton || angle->after(this)) { |
| 662 this->fNext = angle; |
| 663 angle->fNext = next; |
| 664 } else { |
| 665 next->fNext = angle; |
| 666 angle->fNext = this; |
| 667 } |
| 668 debugValidateNext(); |
| 669 return; |
| 670 } |
| 671 SkOpAngle* last = this; |
| 672 do { |
| 673 SkASSERT(last->fNext == next); |
| 674 if (angle->after(last)) { |
| 675 last->fNext = angle; |
| 676 angle->fNext = next; |
| 677 debugValidateNext(); |
| 678 return; |
| 679 } |
| 680 last = next; |
| 681 next = next->fNext; |
| 682 if (last == this && next->fUnorderable) { |
| 683 fUnorderable = true; |
| 684 return; |
| 685 } |
| 686 SkASSERT(last != this); |
| 687 } while (true); |
| 688 } |
| 689 |
| 690 bool SkOpAngle::isHorizontal() const { |
| 691 return !fIsCurve && fSweep[0].fY == 0; |
| 692 } |
| 693 |
| 694 SkOpSpan* SkOpAngle::lastMarked() const { |
| 695 if (fLastMarked) { |
| 696 if (fLastMarked->fChased) { |
| 697 return NULL; |
| 698 } |
| 699 fLastMarked->fChased = true; |
| 700 } |
| 701 return fLastMarked; |
| 702 } |
| 703 |
| 704 int SkOpAngle::loopCount() const { |
| 705 int count = 0; |
| 706 const SkOpAngle* first = this; |
| 707 const SkOpAngle* next = this; |
| 708 do { |
| 709 next = next->fNext; |
| 710 ++count; |
| 711 } while (next && next != first); |
| 712 return count; |
| 713 } |
| 714 |
| 715 // OPTIMIZATION: can this be done better in after when angles are sorted? |
| 716 void SkOpAngle::markStops() { |
| 717 SkOpAngle* angle = this; |
| 718 int lastEnd = SkTMax(fSectorStart, fSectorEnd); |
| 719 do { |
| 720 angle = angle->fNext; |
| 721 int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| 722 // angles that are smaller by one aren't necessary better, since the lar
ger may be a line |
| 723 // and the smaller may be a curve that curls to the other side of the li
ne. |
| 724 if (lastEnd + 1 < angleStart) { |
| 725 angle->fStop = true; |
| 726 } |
| 727 lastEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| 728 } while (angle != this); |
| 729 } |
| 730 |
| 731 bool SkOpAngle::merge(SkOpAngle* angle) { |
| 732 SkASSERT(fNext); |
| 733 SkASSERT(angle->fNext); |
| 734 SkOpAngle* working = angle; |
| 735 do { |
| 736 if (this == working) { |
| 737 return false; |
| 738 } |
| 739 working = working->fNext; |
| 740 } while (working != angle); |
| 741 do { |
| 742 SkOpAngle* next = working->fNext; |
| 743 working->fNext = NULL; |
| 744 insert(working); |
| 745 working = next; |
| 746 } while (working != angle); |
| 747 // it's likely that a pair of the angles are unorderable |
74 #if DEBUG_ANGLE | 748 #if DEBUG_ANGLE |
75 SkString bugOut; | 749 SkOpAngle* last = angle; |
76 bugOut.printf("%s _ id=%d segId=%d tStart=%1.9g tEnd=%1.9g" | 750 working = angle->fNext; |
77 " | id=%d segId=%d tStart=%1.9g tEnd=%1.9g ", funcName, | 751 do { |
78 fID, fSegment->debugID(), fSegment->t(fStart), fSegment->t(fEnd), | 752 SkASSERT(last->fNext == working); |
79 rh.fID, rh.fSegment->debugID(), rh.fSegment->t(rh.fStart), rh.fSegment->
t(rh.fEnd)); | 753 last->fNext = working->fNext; |
| 754 SkASSERT(working->after(last)); |
| 755 last->fNext = working; |
| 756 last = working; |
| 757 working = working->fNext; |
| 758 } while (last != angle); |
80 #endif | 759 #endif |
81 double y_ry = y * ry; | 760 debugValidateNext(); |
82 if (y_ry < 0) { // if y's are opposite signs, we can do a quick return | 761 return true; |
83 return COMPARE_RESULT("1 y * ry < 0", y < 0); | 762 } |
84 } | 763 |
85 // at this point, both y's must be the same sign, or one (or both) is zero | 764 double SkOpAngle::midT() const { |
86 double x = dx(); | 765 return (fSegment->t(fStart) + fSegment->t(fEnd)) / 2; |
87 double rx = rh.dx(); | 766 } |
88 if (x * rx < 0) { // if x's are opposite signs, use y to determine first or
second half | 767 |
89 if (y < 0 && ry < 0) { // if y's are negative, lh x is smaller if posit
ive | 768 bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const { |
90 return COMPARE_RESULT("2 x_rx < 0 && y < 0 ...", x > 0); | 769 int startSpan = abs(rh.fSectorStart - fSectorStart); |
91 } | 770 return startSpan >= 8; |
92 if (y >= 0 && ry >= 0) { // if y's are zero or positive, lh x is smalle
r if negative | 771 } |
93 return COMPARE_RESULT("3 x_rx < 0 && y >= 0 ...", x < 0); | 772 |
94 } | 773 bool SkOpAngle::orderable(const SkOpAngle& rh) const { |
95 SkASSERT((y == 0) ^ (ry == 0)); // if one y is zero and one is negative
, neg y is smaller | 774 int result; |
96 return COMPARE_RESULT("4 x_rx < 0 && y == 0 ...", y < 0); | 775 if (!fIsCurve) { |
97 } | 776 if (!rh.fIsCurve) { |
98 // at this point, both x's must be the same sign, or one (or both) is zero | 777 double leftX = fTangentHalf.dx(); |
99 if (y_ry == 0) { // if either y is zero | 778 double leftY = fTangentHalf.dy(); |
100 if (y + ry < 0) { // if the other y is less than zero, it must be smalle
r | 779 double rightX = rh.fTangentHalf.dx(); |
101 return COMPARE_RESULT("5 y_ry == 0 && y + ry < 0", y < 0); | 780 double rightY = rh.fTangentHalf.dy(); |
102 } | 781 double x_ry = leftX * rightY; |
103 if (y + ry > 0) { // if a y is greater than zero and an x is positive,
non zero is smaller | 782 double rx_y = rightX * leftY; |
104 return COMPARE_RESULT("6 y_ry == 0 && y + ry > 0", (x + rx > 0) ^ (y
== 0)); | 783 if (x_ry == rx_y) { |
105 } | 784 if (leftX * rightX < 0 || leftY * rightY < 0) { |
106 // at this point, both y's are zero, so lines are coincident or one is d
egenerate | 785 return true; // exactly 180 degrees apart |
107 SkASSERT(x * rx != 0); // and a degenerate line should haven't gotten t
his far | 786 } |
108 } | 787 goto unorderable; |
109 // see if either curve can be lengthened before trying the tangent | 788 } |
110 if (fSegment->other(fEnd) != rh.fSegment // tangents not absolutely identic
al | 789 SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- wo
rth finding earlier |
111 && rh.fSegment->other(rh.fEnd) != fSegment | 790 return x_ry < rx_y; |
112 && y != -DBL_EPSILON | 791 } |
113 && ry != -DBL_EPSILON) { // and not intersecting | 792 if ((result = allOnOneSide(rh)) >= 0) { |
114 SkOpAngle longer = *this; | 793 return result; |
115 SkOpAngle rhLonger = rh; | 794 } |
116 if ((longer.lengthen(rh) | rhLonger.lengthen(*this)) // lengthen both | 795 if (fUnorderable || approximately_zero(rh.fSide)) { |
117 && (fUnorderable || !longer.fUnorderable) | 796 goto unorderable; |
118 && (rh.fUnorderable || !rhLonger.fUnorderable)) { | 797 } |
| 798 } else if (!rh.fIsCurve) { |
| 799 if ((result = rh.allOnOneSide(*this)) >= 0) { |
| 800 return !result; |
| 801 } |
| 802 if (rh.fUnorderable || approximately_zero(fSide)) { |
| 803 goto unorderable; |
| 804 } |
| 805 } |
| 806 if ((result = convexHullOverlaps(rh)) >= 0) { |
| 807 return result; |
| 808 } |
| 809 return endsIntersect(rh); |
| 810 unorderable: |
| 811 fUnorderable = true; |
| 812 rh.fUnorderable = true; |
| 813 return true; |
| 814 } |
| 815 |
| 816 // OPTIMIZE: if this shows up in a profile, add a previous pointer |
| 817 // as is, this should be rarely called |
| 818 SkOpAngle* SkOpAngle::previous() const { |
| 819 SkOpAngle* last = fNext; |
| 820 do { |
| 821 SkOpAngle* next = last->fNext; |
| 822 if (next == this) { |
| 823 return last; |
| 824 } |
| 825 last = next; |
| 826 } while (true); |
| 827 } |
| 828 |
| 829 void SkOpAngle::set(const SkOpSegment* segment, int start, int end) { |
119 #if DEBUG_ANGLE | 830 #if DEBUG_ANGLE |
120 bugOut.prepend(" "); | 831 fID = 0; |
121 #endif | 832 #endif |
122 return COMPARE_RESULT("10 longer.lengthen(rh) ...", longer < rhLonge
r); | |
123 } | |
124 } | |
125 SkPath::Verb verb = fSegment->verb(); | |
126 SkPath::Verb rVerb = rh.fSegment->verb(); | |
127 if (y_ry != 0) { // if they aren't coincident, look for a stable cross produ
ct | |
128 // at this point, y's are the same sign, neither is zero | |
129 // and x's are the same sign, or one (or both) is zero | |
130 double x_ry = x * ry; | |
131 double rx_y = rx * y; | |
132 if (!fComputed && !rh.fComputed) { | |
133 if (!SkDLine::NearRay(x, y, rx, ry) && x_ry != rx_y) { | |
134 return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx
_y); | |
135 } | |
136 if (fSide2 == 0 && rh.fSide2 == 0) { | |
137 return COMPARE_RESULT("7a !fComputed && !rh.fComputed", x_ry < r
x_y); | |
138 } | |
139 } else { | |
140 // if the vector was a result of subdividing a curve, see if it is s
table | |
141 bool sloppy1 = x_ry < rx_y; | |
142 bool sloppy2 = !sloppy1; | |
143 if ((!fComputed || calcSlop(x, y, rx, ry, &sloppy1)) | |
144 && (!rh.fComputed || rh.calcSlop(rx, ry, x, y, &sloppy2)) | |
145 && sloppy1 != sloppy2) { | |
146 return COMPARE_RESULT("8 CalcSlop(x, y ...", sloppy1); | |
147 } | |
148 } | |
149 } | |
150 if (fSide2 * rh.fSide2 == 0) { // one is zero | |
151 #if DEBUG_ANGLE | |
152 if (fSide2 == rh.fSide2 && y_ry) { // both is zero; coincidence was und
etected | |
153 SkDebugf("%s coincidence!\n", __FUNCTION__); | |
154 } | |
155 #endif | |
156 return COMPARE_RESULT("9a fSide2 * rh.fSide2 == 0 ...", fSide2 < rh.fSid
e2); | |
157 } | |
158 // at this point, the initial tangent line is nearly coincident | |
159 // see if edges curl away from each other | |
160 if (fSide * rh.fSide < 0 && (!approximately_zero(fSide) || !approximately_ze
ro(rh.fSide))) { | |
161 return COMPARE_RESULT("9b fSide * rh.fSide < 0 ...", fSide < rh.fSide); | |
162 } | |
163 if (fUnsortable || rh.fUnsortable) { | |
164 // even with no solution, return a stable sort | |
165 return COMPARE_RESULT("11 fUnsortable || rh.fUnsortable", this < &rh); | |
166 } | |
167 if ((verb == SkPath::kLine_Verb && approximately_zero(y) && approximately_ze
ro(x)) | |
168 || (rVerb == SkPath::kLine_Verb | |
169 && approximately_zero(ry) && approximately_zero(rx))) { | |
170 // See general unsortable comment below. This case can happen when | |
171 // one line has a non-zero change in t but no change in x and y. | |
172 fUnsortable = true; | |
173 return COMPARE_RESULT("12 verb == SkPath::kLine_Verb ...", this < &rh); | |
174 } | |
175 if (fSegment->isTiny(this) || rh.fSegment->isTiny(&rh)) { | |
176 fUnsortable = true; | |
177 return COMPARE_RESULT("13 verb == fSegment->isTiny(this) ...", this < &r
h); | |
178 } | |
179 SkASSERT(verb >= SkPath::kQuad_Verb); | |
180 SkASSERT(rVerb >= SkPath::kQuad_Verb); | |
181 // FIXME: until I can think of something better, project a ray from the | |
182 // end of the shorter tangent to midway between the end points | |
183 // through both curves and use the resulting angle to sort | |
184 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive | |
185 double len = fTangentPart.normalSquared(); | |
186 double rlen = rh.fTangentPart.normalSquared(); | |
187 SkDLine ray; | |
188 SkIntersections i, ri; | |
189 int roots, rroots; | |
190 bool flip = false; | |
191 bool useThis; | |
192 bool leftLessThanRight = fSide > 0; | |
193 do { | |
194 useThis = (len < rlen) ^ flip; | |
195 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; | |
196 SkPath::Verb partVerb = useThis ? verb : rVerb; | |
197 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? | |
198 part[2] : part[1]; | |
199 ray[1] = SkDPoint::Mid(part[0], part[SkPathOpsVerbToPoints(partVerb)]); | |
200 SkASSERT(ray[0] != ray[1]); | |
201 roots = (i.*CurveRay[SkPathOpsVerbToPoints(verb)])(fSegment->pts(), ray)
; | |
202 rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rVerb)])(rh.fSegment->pts()
, ray); | |
203 } while ((roots == 0 || rroots == 0) && (flip ^= true)); | |
204 if (roots == 0 || rroots == 0) { | |
205 // FIXME: we don't have a solution in this case. The interim solution | |
206 // is to mark the edges as unsortable, exclude them from this and | |
207 // future computations, and allow the returned path to be fragmented | |
208 fUnsortable = true; | |
209 return COMPARE_RESULT("roots == 0 || rroots == 0", this < &rh); | |
210 } | |
211 SkASSERT(fSide != 0 && rh.fSide != 0); | |
212 if (fSide * rh.fSide < 0) { | |
213 fUnsortable = true; | |
214 return COMPARE_RESULT("14 fSide * rh.fSide < 0", this < &rh); | |
215 } | |
216 SkDPoint lLoc; | |
217 double best = SK_ScalarInfinity; | |
218 #if DEBUG_SORT | |
219 SkDebugf("lh=%d rh=%d use-lh=%d ray={{%1.9g,%1.9g}, {%1.9g,%1.9g}} %c\n", | |
220 fSegment->debugID(), rh.fSegment->debugID(), useThis, ray[0].fX, ray
[0].fY, | |
221 ray[1].fX, ray[1].fY, "-+"[fSide > 0]); | |
222 #endif | |
223 for (int index = 0; index < roots; ++index) { | |
224 SkDPoint loc = i.pt(index); | |
225 SkDVector dxy = loc - ray[0]; | |
226 double dist = dxy.lengthSquared(); | |
227 #if DEBUG_SORT | |
228 SkDebugf("best=%1.9g dist=%1.9g loc={%1.9g,%1.9g} dxy={%1.9g,%1.9g}\n", | |
229 best, dist, loc.fX, loc.fY, dxy.fX, dxy.fY); | |
230 #endif | |
231 if (best > dist) { | |
232 lLoc = loc; | |
233 best = dist; | |
234 } | |
235 } | |
236 flip = false; | |
237 SkDPoint rLoc; | |
238 for (int index = 0; index < rroots; ++index) { | |
239 rLoc = ri.pt(index); | |
240 SkDVector dxy = rLoc - ray[0]; | |
241 double dist = dxy.lengthSquared(); | |
242 #if DEBUG_SORT | |
243 SkDebugf("best=%1.9g dist=%1.9g %c=(fSide < 0) rLoc={%1.9g,%1.9g} dxy={%
1.9g,%1.9g}\n", | |
244 best, dist, "><"[fSide < 0], rLoc.fX, rLoc.fY, dxy.fX, dxy.fY); | |
245 #endif | |
246 if (best > dist) { | |
247 flip = true; | |
248 break; | |
249 } | |
250 } | |
251 if (flip) { | |
252 leftLessThanRight = !leftLessThanRight; | |
253 } | |
254 return COMPARE_RESULT("15 leftLessThanRight", leftLessThanRight); | |
255 } | |
256 | |
257 bool SkOpAngle::isHorizontal() const { | |
258 return dy() == 0 && fSegment->verb() == SkPath::kLine_Verb; | |
259 } | |
260 | |
261 // lengthen cannot cross opposite angle | |
262 bool SkOpAngle::lengthen(const SkOpAngle& opp) { | |
263 if (fSegment->other(fEnd) == opp.fSegment) { | |
264 return false; | |
265 } | |
266 // FIXME: make this a while loop instead and make it as large as possible? | |
267 int newEnd = fEnd; | |
268 if (fStart < fEnd ? ++newEnd < fSegment->count() : --newEnd >= 0) { | |
269 fEnd = newEnd; | |
270 setSpans(); | |
271 return true; | |
272 } | |
273 return false; | |
274 } | |
275 | |
276 void SkOpAngle::set(const SkOpSegment* segment, int start, int end) { | |
277 fSegment = segment; | 833 fSegment = segment; |
278 fStart = start; | 834 fStart = start; |
279 fEnd = end; | 835 fEnd = end; |
| 836 fNext = NULL; |
| 837 fComputeSector = fComputedSector = false; |
| 838 fStop = false; |
280 setSpans(); | 839 setSpans(); |
| 840 setSector(); |
| 841 } |
| 842 |
| 843 void SkOpAngle::setCurveHullSweep() { |
| 844 fUnorderedSweep = false; |
| 845 fSweep[0] = fCurvePart[1] - fCurvePart[0]; |
| 846 if (SkPath::kLine_Verb == fSegment->verb()) { |
| 847 fSweep[1] = fSweep[0]; |
| 848 return; |
| 849 } |
| 850 fSweep[1] = fCurvePart[2] - fCurvePart[0]; |
| 851 if (SkPath::kCubic_Verb != fSegment->verb()) { |
| 852 if (!fSweep[0].fX && !fSweep[0].fY) { |
| 853 fSweep[0] = fSweep[1]; |
| 854 } |
| 855 return; |
| 856 } |
| 857 SkDVector thirdSweep = fCurvePart[3] - fCurvePart[0]; |
| 858 if (fSweep[0].fX == 0 && fSweep[0].fY == 0) { |
| 859 fSweep[0] = fSweep[1]; |
| 860 fSweep[1] = thirdSweep; |
| 861 if (fSweep[0].fX == 0 && fSweep[0].fY == 0) { |
| 862 fSweep[0] = fSweep[1]; |
| 863 fCurvePart[1] = fCurvePart[3]; |
| 864 fIsCurve = false; |
| 865 } |
| 866 return; |
| 867 } |
| 868 double s1x3 = fSweep[0].crossCheck(thirdSweep); |
| 869 double s3x2 = thirdSweep.crossCheck(fSweep[1]); |
| 870 if (s1x3 * s3x2 >= 0) { // if third vector is on or between first two vecto
rs |
| 871 return; |
| 872 } |
| 873 double s2x1 = fSweep[1].crossCheck(fSweep[0]); |
| 874 // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in tr
ouble |
| 875 // probably such wide sweeps should be artificially subdivided earlier so th
at never happens |
| 876 SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0); |
| 877 if (s3x2 * s2x1 < 0) { |
| 878 SkASSERT(s2x1 * s1x3 > 0); |
| 879 fSweep[0] = fSweep[1]; |
| 880 fUnorderedSweep = true; |
| 881 } |
| 882 fSweep[1] = thirdSweep; |
| 883 } |
| 884 |
| 885 void SkOpAngle::setSector() { |
| 886 SkPath::Verb verb = fSegment->verb(); |
| 887 if (SkPath::kLine_Verb != verb && small()) { |
| 888 fSectorStart = fSectorEnd = -1; |
| 889 fSectorMask = 0; |
| 890 fComputeSector = true; // can't determine sector until segment length c
an be found |
| 891 return; |
| 892 } |
| 893 fSectorStart = findSector(verb, fSweep[0].fX, fSweep[0].fY); |
| 894 if (!fIsCurve) { // if it's a line or line-like, note that both sectors are
the same |
| 895 SkASSERT(fSectorStart >= 0); |
| 896 fSectorEnd = fSectorStart; |
| 897 fSectorMask = 1 << fSectorStart; |
| 898 return; |
| 899 } |
| 900 SkASSERT(SkPath::kLine_Verb != verb); |
| 901 fSectorEnd = findSector(verb, fSweep[1].fX, fSweep[1].fY); |
| 902 if (fSectorEnd == fSectorStart) { |
| 903 SkASSERT((fSectorStart & 3) != 3); // if the sector has no span, it can
't be an exact angle |
| 904 fSectorMask = 1 << fSectorStart; |
| 905 return; |
| 906 } |
| 907 bool crossesZero = checkCrossesZero(); |
| 908 int start = SkTMin(fSectorStart, fSectorEnd); |
| 909 bool curveBendsCCW = (fSectorStart == start) ^ crossesZero; |
| 910 // bump the start and end of the sector span if they are on exact compass po
ints |
| 911 if ((fSectorStart & 3) == 3) { |
| 912 fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f; |
| 913 } |
| 914 if ((fSectorEnd & 3) == 3) { |
| 915 fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f; |
| 916 } |
| 917 crossesZero = checkCrossesZero(); |
| 918 start = SkTMin(fSectorStart, fSectorEnd); |
| 919 int end = SkTMax(fSectorStart, fSectorEnd); |
| 920 if (!crossesZero) { |
| 921 fSectorMask = (unsigned) -1 >> (31 - end + start) << start; |
| 922 } else { |
| 923 fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end); |
| 924 } |
281 } | 925 } |
282 | 926 |
283 void SkOpAngle::setSpans() { | 927 void SkOpAngle::setSpans() { |
284 fUnorderable = fSegment->isTiny(this); | 928 fUnorderable = fSegment->isTiny(this); |
285 fLastMarked = NULL; | 929 fLastMarked = NULL; |
286 fUnsortable = false; | |
287 const SkPoint* pts = fSegment->pts(); | 930 const SkPoint* pts = fSegment->pts(); |
288 if (fSegment->verb() != SkPath::kLine_Verb) { | 931 SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurveP
art[3].fY |
289 fComputed = fSegment->subDivide(fStart, fEnd, &fCurvePart); | 932 = SK_ScalarNaN); |
290 fSegment->subDivide(fStart, fStart < fEnd ? fSegment->count() - 1 : 0, &
fCurveHalf); | 933 fSegment->subDivide(fStart, fEnd, &fCurvePart); |
291 } | 934 setCurveHullSweep(); |
292 // FIXME: slight errors in subdivision cause sort trouble later on. As an ex
periment, try | 935 const SkPath::Verb verb = fSegment->verb(); |
293 // rounding the curve part to float precision here | 936 if (verb != SkPath::kLine_Verb |
294 // fCurvePart.round(fSegment->verb()); | 937 && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) { |
295 switch (fSegment->verb()) { | 938 SkDLine lineHalf; |
| 939 lineHalf[0].set(fCurvePart[0].asSkPoint()); |
| 940 lineHalf[1].set(fCurvePart[SkPathOpsVerbToPoints(verb)].asSkPoint()); |
| 941 fTangentHalf.lineEndPoints(lineHalf); |
| 942 fSide = 0; |
| 943 } |
| 944 switch (verb) { |
296 case SkPath::kLine_Verb: { | 945 case SkPath::kLine_Verb: { |
297 SkASSERT(fStart != fEnd); | 946 SkASSERT(fStart != fEnd); |
298 fCurvePart[0].set(pts[fStart > fEnd]); | 947 const SkPoint& cP1 = pts[fStart < fEnd]; |
299 fCurvePart[1].set(pts[fStart < fEnd]); | 948 SkDLine lineHalf; |
300 fComputed = false; | 949 lineHalf[0].set(fSegment->span(fStart).fPt); |
301 // OPTIMIZATION: for pure line compares, we never need fTangentPart.c | 950 lineHalf[1].set(cP1); |
302 fTangentPart.lineEndPoints(*SkTCast<SkDLine*>(&fCurvePart)); | 951 fTangentHalf.lineEndPoints(lineHalf); |
303 fSide = 0; | 952 fSide = 0; |
304 fSide2 = 0; | 953 fIsCurve = false; |
305 } break; | 954 } return; |
306 case SkPath::kQuad_Verb: { | 955 case SkPath::kQuad_Verb: { |
307 fSide2 = -fTangentHalf.quadPart(*SkTCast<SkDQuad*>(&fCurveHalf)); | 956 SkLineParameters tangentPart; |
308 SkDQuad& quad = *SkTCast<SkDQuad*>(&fCurvePart); | 957 SkDQuad& quad2 = *SkTCast<SkDQuad*>(&fCurvePart); |
309 fTangentPart.quadEndPoints(quad); | 958 (void) tangentPart.quadEndPoints(quad2); |
310 fSide = -fTangentPart.pointDistance(fCurvePart[2]); // not normalized -
- compare sign only | 959 fSide = -tangentPart.pointDistance(fCurvePart[2]); // not normalized --
compare sign only |
311 if (fComputed && dx() > 0 && approximately_zero(dy())) { | |
312 SkDCubic origCurve; // can't use segment's curve in place since it m
ay be flipped | |
313 int last = fSegment->count() - 1; | |
314 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last :
0, &origCurve); | |
315 SkLineParameters origTan; | |
316 origTan.quadEndPoints(*SkTCast<SkDQuad*>(&origCurve)); | |
317 if (origTan.dx() <= 0 | |
318 || (dy() != origTan.dy() && dy() * origTan.dy() <= 0)) { //
signs match? | |
319 fUnorderable = true; | |
320 return; | |
321 } | |
322 } | |
323 } break; | 960 } break; |
324 case SkPath::kCubic_Verb: { | 961 case SkPath::kCubic_Verb: { |
325 double startT = fSegment->t(fStart); | 962 SkLineParameters tangentPart; |
326 fSide2 = -fTangentHalf.cubicPart(fCurveHalf); | 963 (void) tangentPart.cubicPart(fCurvePart); |
327 fTangentPart.cubicEndPoints(fCurvePart); | 964 fSide = -tangentPart.pointDistance(fCurvePart[3]); |
328 double testTs[4]; | 965 double testTs[4]; |
329 // OPTIMIZATION: keep inflections precomputed with cubic segment? | 966 // OPTIMIZATION: keep inflections precomputed with cubic segment? |
330 int testCount = SkDCubic::FindInflections(pts, testTs); | 967 int testCount = SkDCubic::FindInflections(pts, testTs); |
| 968 double startT = fSegment->t(fStart); |
331 double endT = fSegment->t(fEnd); | 969 double endT = fSegment->t(fEnd); |
332 double limitT = endT; | 970 double limitT = endT; |
333 int index; | 971 int index; |
334 for (index = 0; index < testCount; ++index) { | 972 for (index = 0; index < testCount; ++index) { |
335 if (!between(startT, testTs[index], limitT)) { | 973 if (!::between(startT, testTs[index], limitT)) { |
336 testTs[index] = -1; | 974 testTs[index] = -1; |
337 } | 975 } |
338 } | 976 } |
339 testTs[testCount++] = startT; | 977 testTs[testCount++] = startT; |
340 testTs[testCount++] = endT; | 978 testTs[testCount++] = endT; |
341 SkTQSort<double>(testTs, &testTs[testCount - 1]); | 979 SkTQSort<double>(testTs, &testTs[testCount - 1]); |
342 double bestSide = 0; | 980 double bestSide = 0; |
343 int testCases = (testCount << 1) - 1; | 981 int testCases = (testCount << 1) - 1; |
344 index = 0; | 982 index = 0; |
345 while (testTs[index] < 0) { | 983 while (testTs[index] < 0) { |
346 ++index; | 984 ++index; |
347 } | 985 } |
348 index <<= 1; | 986 index <<= 1; |
349 for (; index < testCases; ++index) { | 987 for (; index < testCases; ++index) { |
350 int testIndex = index >> 1; | 988 int testIndex = index >> 1; |
351 double testT = testTs[testIndex]; | 989 double testT = testTs[testIndex]; |
352 if (index & 1) { | 990 if (index & 1) { |
353 testT = (testT + testTs[testIndex + 1]) / 2; | 991 testT = (testT + testTs[testIndex + 1]) / 2; |
354 } | 992 } |
355 // OPTIMIZE: could avoid call for t == startT, endT | 993 // OPTIMIZE: could avoid call for t == startT, endT |
356 SkDPoint pt = dcubic_xy_at_t(pts, testT); | 994 SkDPoint pt = dcubic_xy_at_t(pts, testT); |
357 double testSide = fTangentPart.pointDistance(pt); | 995 SkLineParameters tangentPart; |
| 996 tangentPart.cubicEndPoints(fCurvePart); |
| 997 double testSide = tangentPart.pointDistance(pt); |
358 if (fabs(bestSide) < fabs(testSide)) { | 998 if (fabs(bestSide) < fabs(testSide)) { |
359 bestSide = testSide; | 999 bestSide = testSide; |
360 } | 1000 } |
361 } | 1001 } |
362 fSide = -bestSide; // compare sign only | 1002 fSide = -bestSide; // compare sign only |
363 SkASSERT(fSide == 0 || fSide2 != 0); | |
364 if (fComputed && dx() > 0 && approximately_zero(dy())) { | |
365 SkDCubic origCurve; // can't use segment's curve in place since it m
ay be flipped | |
366 int last = fSegment->count() - 1; | |
367 fSegment->subDivide(fStart < fEnd ? 0 : last, fStart < fEnd ? last :
0, &origCurve); | |
368 SkDCubicPair split = origCurve.chopAt(startT); | |
369 SkLineParameters splitTan; | |
370 splitTan.cubicEndPoints(fStart < fEnd ? split.second() : split.first
()); | |
371 if (splitTan.dx() <= 0) { | |
372 fUnorderable = true; | |
373 fUnsortable = fSegment->isTiny(this); | |
374 return; | |
375 } | |
376 // if one is < 0 and the other is >= 0 | |
377 if (dy() * splitTan.dy() < 0) { | |
378 fUnorderable = true; | |
379 fUnsortable = fSegment->isTiny(this); | |
380 return; | |
381 } | |
382 } | |
383 } break; | 1003 } break; |
384 default: | 1004 default: |
385 SkASSERT(0); | 1005 SkASSERT(0); |
386 } | 1006 } |
387 if ((fUnsortable = approximately_zero(dx()) && approximately_zero(dy()))) { | |
388 return; | |
389 } | |
390 if (fSegment->verb() == SkPath::kLine_Verb) { | |
391 return; | |
392 } | |
393 SkASSERT(fStart != fEnd); | |
394 int smaller = SkMin32(fStart, fEnd); | |
395 int larger = SkMax32(fStart, fEnd); | |
396 while (smaller < larger && fSegment->span(smaller).fTiny) { | |
397 ++smaller; | |
398 } | |
399 if (precisely_equal(fSegment->span(smaller).fT, fSegment->span(larger).fT))
{ | |
400 #if DEBUG_UNSORTABLE | |
401 SkPoint iPt = fSegment->xyAtT(fStart); | |
402 SkPoint ePt = fSegment->xyAtT(fEnd); | |
403 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n
", __FUNCTION__, | |
404 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | |
405 #endif | |
406 fUnsortable = true; | |
407 return; | |
408 } | |
409 fUnsortable = fStart < fEnd ? fSegment->span(smaller).fUnsortableStart | |
410 : fSegment->span(larger).fUnsortableEnd; | |
411 #if DEBUG_UNSORTABLE | |
412 if (fUnsortable) { | |
413 SkPoint iPt = fSegment->xyAtT(smaller); | |
414 SkPoint ePt = fSegment->xyAtT(larger); | |
415 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNC
TION__, | |
416 smaller, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); | |
417 } | |
418 #endif | |
419 return; | |
420 } | 1007 } |
421 | 1008 |
422 #ifdef SK_DEBUG | 1009 bool SkOpAngle::small() const { |
423 void SkOpAngle::dump() const { | 1010 int min = SkMin32(fStart, fEnd); |
424 const SkOpSpan& spanStart = fSegment->span(fStart); | 1011 int max = SkMax32(fStart, fEnd); |
425 const SkOpSpan& spanEnd = fSegment->span(fEnd); | 1012 for (int index = min; index < max; ++index) { |
426 const SkOpSpan& spanMin = fStart < fEnd ? spanStart : spanEnd; | 1013 const SkOpSpan& mSpan = fSegment->span(index); |
427 SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g) sumWind=", | 1014 if (!mSpan.fSmall) { |
428 fSegment->debugID(), fSegment->xAtT(fStart), fSegment->yAtT(fStart), | 1015 return false; |
429 fStart, spanStart.fT, fEnd, spanEnd.fT); | 1016 } |
430 SkPathOpsDebug::WindingPrintf(spanMin.fWindSum); | 1017 } |
431 SkDebugf(" oppWind="); | 1018 return true; |
432 SkPathOpsDebug::WindingPrintf(spanMin.fOppSum), | |
433 SkDebugf(" done=%d\n", spanMin.fDone); | |
434 } | 1019 } |
435 #endif | 1020 |
| 1021 bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const { |
| 1022 if (s0xt0 == 0) { |
| 1023 return false; |
| 1024 } |
| 1025 // if the ctrl tangents are not nearly parallel, use them |
| 1026 // solve for opposite direction displacement scale factor == m |
| 1027 // initial dir = v1.cross(v2) == v2.x * v1.y - v2.y * v1.x |
| 1028 // displacement of q1[1] : dq1 = { -m * v1.y, m * v1.x } + q1[1] |
| 1029 // straight angle when : v2.x * (dq1.y - q1[0].y) == v2.y * (dq1.x - q1[0].x
) |
| 1030 // v2.x * (m * v1.x + v1.y) == v2.y * (-m * v1.y + v1.
x) |
| 1031 // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x |
| 1032 // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y) |
| 1033 // m = v1.cross(v2) / v1.dot(v2) |
| 1034 const SkDVector* sweep = fSweep; |
| 1035 const SkDVector* tweep = rh.fSweep; |
| 1036 double s0dt0 = sweep[0].dot(tweep[0]); |
| 1037 if (!s0dt0) { |
| 1038 return true; |
| 1039 } |
| 1040 SkASSERT(s0dt0 != 0); |
| 1041 double m = s0xt0 / s0dt0; |
| 1042 double sDist = sweep[0].length() * m; |
| 1043 double tDist = tweep[0].length() * m; |
| 1044 bool useS = fabs(sDist) < fabs(tDist); |
| 1045 double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist)); |
| 1046 return mFactor < 5000; // empirically found limit |
| 1047 } |
OLD | NEW |