SkPathOpsDebug.cpp revision dac1d17027dcaa5596885a9f333979418b35001c
1/* 2 * Copyright 2013 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 8#include "SkPathOpsDebug.h" 9#include "SkPath.h" 10 11#if defined SK_DEBUG || !FORCE_RELEASE 12 13const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"}; 14 15#if defined(SK_DEBUG) || !FORCE_RELEASE 16int SkPathOpsDebug::gContourID = 0; 17int SkPathOpsDebug::gSegmentID = 0; 18#endif 19 20#if DEBUG_SORT || DEBUG_SWAP_TOP 21int SkPathOpsDebug::gSortCountDefault = SK_MaxS32; 22int SkPathOpsDebug::gSortCount; 23#endif 24 25#if DEBUG_ACTIVE_OP 26const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"}; 27#endif 28 29bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray, 30 const SkOpSpan* span) { 31 for (int index = 0; index < chaseArray.count(); ++index) { 32 const SkOpSpan* entry = chaseArray[index]; 33 if (entry == span) { 34 return true; 35 } 36 } 37 return false; 38} 39 40void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) { 41 size_t len = strlen(str); 42 bool num = false; 43 for (size_t idx = 0; idx < len; ++idx) { 44 if (num && str[idx] == 'e') { 45 if (len + 2 >= bufferLen) { 46 return; 47 } 48 memmove(&str[idx + 2], &str[idx + 1], len - idx); 49 str[idx] = '*'; 50 str[idx + 1] = '^'; 51 ++len; 52 } 53 num = str[idx] >= '0' && str[idx] <= '9'; 54 } 55} 56 57bool SkPathOpsDebug::ValidWind(int wind) { 58 return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF; 59} 60 61void SkPathOpsDebug::WindingPrintf(int wind) { 62 if (wind == SK_MinS32) { 63 SkDebugf("?"); 64 } else { 65 SkDebugf("%d", wind); 66 } 67} 68 69#if DEBUG_SHOW_TEST_NAME 70void* SkPathOpsDebug::CreateNameStr() { 71 return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH); 72} 73 74void SkPathOpsDebug::DeleteNameStr(void* v) { 75 SkDELETE_ARRAY(reinterpret_cast<char* >(v)); 76} 77 78void SkPathOpsDebug::BumpTestName(char* test) { 79 char* num = test + strlen(test); 80 while (num[-1] >= '0' && num[-1] <= '9') { 81 --num; 82 } 83 if (num[0] == '\0') { 84 return; 85 } 86 int dec = atoi(num); 87 if (dec == 0) { 88 return; 89 } 90 ++dec; 91 SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec); 92} 93#endif 94 95#if !DEBUG_SHOW_TEST_NAME // enable when building without extended test 96void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) { 97} 98#endif 99 100#endif // defined SK_DEBUG || !FORCE_RELEASE 101 102#include "SkOpAngle.h" 103#include "SkOpSegment.h" 104 105#if DEBUG_SORT 106void SkOpAngle::debugLoop() const { 107 dumpLoop(); 108} 109#endif 110 111#if DEBUG_ANGLE 112void SkOpAngle::debugSameAs(const SkOpAngle* compare) const { 113 SK_ALWAYSBREAK(fSegment == compare->fSegment); 114 const SkOpSpan& startSpan = fSegment->span(fStart); 115 const SkOpSpan& oStartSpan = fSegment->span(compare->fStart); 116 SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle); 117 SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle); 118 const SkOpSpan& endSpan = fSegment->span(fEnd); 119 const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd); 120 SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle); 121 SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle); 122} 123#endif 124 125#if DEBUG_VALIDATE 126void SkOpAngle::debugValidateNext() const { 127 const SkOpAngle* first = this; 128 const SkOpAngle* next = first; 129 SkTDArray<const SkOpAngle*>(angles); 130 do { 131// SK_ALWAYSBREAK(next->fSegment->debugContains(next)); 132 angles.push(next); 133 next = next->next(); 134 if (next == first) { 135 break; 136 } 137 SK_ALWAYSBREAK(!angles.contains(next)); 138 if (!next) { 139 return; 140 } 141 } while (true); 142} 143 144void SkOpAngle::debugValidateLoop() const { 145 const SkOpAngle* first = this; 146 const SkOpAngle* next = first; 147 SK_ALWAYSBREAK(first->next() != first); 148 int signSum = 0; 149 int oppSum = 0; 150 bool firstOperand = fSegment->operand(); 151 bool unorderable = false; 152 do { 153 unorderable |= next->fUnorderable; 154 const SkOpSegment* segment = next->fSegment; 155 bool operandsMatch = firstOperand == segment->operand(); 156 signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next); 157 oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next); 158 const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); 159 if (segment->_xor()) { 160// SK_ALWAYSBREAK(span.fWindValue == 1); 161// SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1); 162 } 163 if (segment->oppXor()) { 164 SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1); 165// SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1); 166 } 167 next = next->next(); 168 if (!next) { 169 return; 170 } 171 } while (next != first); 172 if (unorderable) { 173 return; 174 } 175 SK_ALWAYSBREAK(!signSum || fSegment->_xor()); 176 SK_ALWAYSBREAK(!oppSum || fSegment->oppXor()); 177 int lastWinding; 178 int lastOppWinding; 179 int winding; 180 int oppWinding; 181 do { 182 const SkOpSegment* segment = next->fSegment; 183 const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); 184 winding = span.fWindSum; 185 if (winding != SK_MinS32) { 186// SK_ALWAYSBREAK(winding != 0); 187 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); 188 lastWinding = winding; 189 int diffWinding = segment->spanSign(next); 190 if (!segment->_xor()) { 191 SK_ALWAYSBREAK(diffWinding != 0); 192 bool sameSign = (winding > 0) == (diffWinding > 0); 193 winding -= sameSign ? diffWinding : -diffWinding; 194 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); 195 SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding)); 196 if (!sameSign) { 197 SkTSwap(winding, lastWinding); 198 } 199 } 200 lastOppWinding = oppWinding = span.fOppSum; 201 if (oppWinding != SK_MinS32 && !segment->oppXor()) { 202 int oppDiffWinding = segment->oppSign(next); 203// SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor()); 204 if (oppDiffWinding) { 205 bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0); 206 oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding; 207 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); 208 SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding)); 209 if (!oppSameSign) { 210 SkTSwap(oppWinding, lastOppWinding); 211 } 212 } 213 } 214 firstOperand = segment->operand(); 215 break; 216 } 217 SK_ALWAYSBREAK(span.fOppSum == SK_MinS32); 218 next = next->next(); 219 } while (next != first); 220 if (winding == SK_MinS32) { 221 return; 222 } 223 SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding)); 224 first = next; 225 next = next->next(); 226 do { 227 const SkOpSegment* segment = next->fSegment; 228 lastWinding = winding; 229 lastOppWinding = oppWinding; 230 bool operandsMatch = firstOperand == segment->operand(); 231 if (operandsMatch) { 232 if (!segment->_xor()) { 233 winding -= segment->spanSign(next); 234 SK_ALWAYSBREAK(winding != lastWinding); 235 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); 236 } 237 if (!segment->oppXor()) { 238 int oppDiffWinding = segment->oppSign(next); 239 if (oppWinding != SK_MinS32) { 240 oppWinding -= oppDiffWinding; 241 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); 242 } else { 243 SK_ALWAYSBREAK(oppDiffWinding == 0); 244 } 245 } 246 } else { 247 if (!segment->oppXor()) { 248 winding -= segment->oppSign(next); 249 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding)); 250 } 251 if (!segment->_xor()) { 252 oppWinding -= segment->spanSign(next); 253 SK_ALWAYSBREAK(oppWinding != lastOppWinding); 254 SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding)); 255 } 256 } 257 bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding); 258 int sumWinding = useInner ? winding : lastWinding; 259 bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding); 260 int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding; 261 if (!operandsMatch) { 262 SkTSwap(useInner, oppUseInner); 263 SkTSwap(sumWinding, oppSumWinding); 264 } 265 const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd)); 266 if (winding == -lastWinding) { 267 if (span.fWindSum != SK_MinS32) { 268 SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n", 269 __FUNCTION__, 270 useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum); 271 } 272 } 273 if (oppWinding != SK_MinS32) { 274 if (span.fOppSum != SK_MinS32) { 275 SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor()); 276 } 277 } else { 278 SK_ALWAYSBREAK(!firstOperand); 279 SK_ALWAYSBREAK(!segment->operand()); 280 SK_ALWAYSBREAK(!span.fOppValue); 281 } 282 next = next->next(); 283 } while (next != first); 284} 285#endif 286 287#if DEBUG_SWAP_TOP 288bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const { 289 if (fVerb != SkPath::kCubic_Verb) { 290 return false; 291 } 292 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); 293 return dst.controlsContainedByEnds(); 294} 295#endif 296 297#if DEBUG_CONCIDENT 298// SK_ALWAYSBREAK if pair has not already been added 299void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const { 300 for (int i = 0; i < fTs.count(); ++i) { 301 if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) { 302 return; 303 } 304 } 305 SK_ALWAYSBREAK(0); 306} 307#endif 308 309#if DEBUG_ANGLE 310void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const { 311 const SkPoint& basePt = fTs[tStart].fPt; 312 while (++tStart < tEnd) { 313 const SkPoint& cmpPt = fTs[tStart].fPt; 314 SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt)); 315 } 316} 317#endif 318 319#if DEBUG_SWAP_TOP 320int SkOpSegment::debugInflections(int tStart, int tEnd) const { 321 if (fVerb != SkPath::kCubic_Verb) { 322 return false; 323 } 324 SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); 325 double inflections[2]; 326 return dst.findInflections(inflections); 327} 328#endif 329 330const SkOpAngle* SkOpSegment::debugLastAngle() const { 331 const SkOpAngle* result = NULL; 332 for (int index = 0; index < count(); ++index) { 333 const SkOpSpan& span = this->span(index); 334 if (span.fToAngle) { 335 SkASSERT(!result); 336 result = span.fToAngle; 337 } 338 } 339 SkASSERT(result); 340 return result; 341} 342 343void SkOpSegment::debugReset() { 344 fTs.reset(); 345 fAngles.reset(); 346} 347 348#if DEBUG_CONCIDENT 349void SkOpSegment::debugShowTs(const char* prefix) const { 350 SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID); 351 int lastWind = -1; 352 int lastOpp = -1; 353 double lastT = -1; 354 int i; 355 for (i = 0; i < fTs.count(); ++i) { 356 bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue 357 || lastOpp != fTs[i].fOppValue; 358 if (change && lastWind >= 0) { 359 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", 360 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); 361 } 362 if (change) { 363 SkDebugf(" [o=%d", fTs[i].fOther->fID); 364 lastWind = fTs[i].fWindValue; 365 lastOpp = fTs[i].fOppValue; 366 lastT = fTs[i].fT; 367 } else { 368 SkDebugf(",%d", fTs[i].fOther->fID); 369 } 370 } 371 if (i <= 0) { 372 return; 373 } 374 SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]", 375 lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp); 376 if (fOperand) { 377 SkDebugf(" operand"); 378 } 379 if (done()) { 380 SkDebugf(" done"); 381 } 382 SkDebugf("\n"); 383} 384#endif 385 386#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY 387void SkOpSegment::debugShowActiveSpans() const { 388 debugValidate(); 389 if (done()) { 390 return; 391 } 392#if DEBUG_ACTIVE_SPANS_SHORT_FORM 393 int lastId = -1; 394 double lastT = -1; 395#endif 396 for (int i = 0; i < fTs.count(); ++i) { 397 if (fTs[i].fDone) { 398 continue; 399 } 400 SK_ALWAYSBREAK(i < fTs.count() - 1); 401#if DEBUG_ACTIVE_SPANS_SHORT_FORM 402 if (lastId == fID && lastT == fTs[i].fT) { 403 continue; 404 } 405 lastId = fID; 406 lastT = fTs[i].fT; 407#endif 408 SkDebugf("%s id=%d", __FUNCTION__, fID); 409 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 410 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 411 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 412 } 413 const SkOpSpan* span = &fTs[i]; 414 SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span)); 415 int iEnd = i + 1; 416 while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) { 417 ++iEnd; 418 } 419 SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT); 420 const SkOpSegment* other = fTs[i].fOther; 421 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=", 422 other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex); 423 if (fTs[i].fWindSum == SK_MinS32) { 424 SkDebugf("?"); 425 } else { 426 SkDebugf("%d", fTs[i].fWindSum); 427 } 428 SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue); 429 } 430} 431#endif 432 433#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE 434void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) { 435 const SkPoint& pt = xyAtT(&span); 436 SkDebugf("%s id=%d", fun, fID); 437 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 438 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 439 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 440 } 441 SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> 442 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); 443 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=", 444 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, 445 (&span)[1].fT, winding); 446 if (span.fWindSum == SK_MinS32) { 447 SkDebugf("?"); 448 } else { 449 SkDebugf("%d", span.fWindSum); 450 } 451 SkDebugf(" windValue=%d\n", span.fWindValue); 452} 453 454void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding, 455 int oppWinding) { 456 const SkPoint& pt = xyAtT(&span); 457 SkDebugf("%s id=%d", fun, fID); 458 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY); 459 for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) { 460 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY); 461 } 462 SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther-> 463 fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]); 464 SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=", 465 span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY, 466 (&span)[1].fT, winding, oppWinding); 467 if (span.fOppSum == SK_MinS32) { 468 SkDebugf("?"); 469 } else { 470 SkDebugf("%d", span.fOppSum); 471 } 472 SkDebugf(" windSum="); 473 if (span.fWindSum == SK_MinS32) { 474 SkDebugf("?"); 475 } else { 476 SkDebugf("%d", span.fWindSum); 477 } 478 SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue); 479} 480#endif 481 482#if DEBUG_SHOW_WINDING 483int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const { 484 if (!(1 << fID & ofInterest)) { 485 return 0; 486 } 487 int sum = 0; 488 SkTArray<char, true> slots(slotCount * 2); 489 memset(slots.begin(), ' ', slotCount * 2); 490 for (int i = 0; i < fTs.count(); ++i) { 491 // if (!(1 << fTs[i].fOther->fID & ofInterest)) { 492 // continue; 493 // } 494 sum += fTs[i].fWindValue; 495 slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue); 496 sum += fTs[i].fOppValue; 497 slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue); 498 } 499 SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount, 500 slots.begin() + slotCount); 501 return sum; 502} 503#endif 504 505void SkOpSegment::debugValidate() const { 506#if DEBUG_VALIDATE 507 int count = fTs.count(); 508 SK_ALWAYSBREAK(count >= 2); 509 SK_ALWAYSBREAK(fTs[0].fT == 0); 510 SK_ALWAYSBREAK(fTs[count - 1].fT == 1); 511 int done = 0; 512 double t = -1; 513 const SkOpSpan* last = NULL; 514 bool tinyTFound = false; 515 bool hasLoop = false; 516 for (int i = 0; i < count; ++i) { 517 const SkOpSpan& span = fTs[i]; 518 SK_ALWAYSBREAK(t <= span.fT); 519 t = span.fT; 520 int otherIndex = span.fOtherIndex; 521 const SkOpSegment* other = span.fOther; 522 SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb); 523 const SkOpSpan& otherSpan = other->fTs[otherIndex]; 524 SK_ALWAYSBREAK(otherSpan.fPt == span.fPt); 525 SK_ALWAYSBREAK(otherSpan.fOtherT == t); 526 SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); 527 done += span.fDone; 528 if (last) { 529 SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther); 530 bool tsEqual = last->fT == span.fT; 531 bool tsPreciselyEqual = precisely_equal(last->fT, span.fT); 532 SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual); 533 bool pointsEqual = last->fPt == span.fPt; 534 bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt); 535#if 0 // bufferOverflow test triggers this 536 SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual); 537#endif 538// SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound); 539 SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop); 540 SK_ALWAYSBREAK(!last->fTiny || pointsEqual); 541 SK_ALWAYSBREAK(!last->fTiny || last->fDone); 542 SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual); 543 SK_ALWAYSBREAK(!last->fSmall || last->fDone); 544// SK_ALWAYSBREAK(!last->fSmall || last->fTiny); 545// SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone); 546 if (last->fTiny) { 547 tinyTFound |= !tsPreciselyEqual; 548 } else { 549 tinyTFound = false; 550 } 551 } 552 last = &span; 553 hasLoop |= last->fLoop; 554 } 555 SK_ALWAYSBREAK(done == fDoneSpans); 556// if (fAngles.count() ) { 557// fAngles.begin()->debugValidateLoop(); 558// } 559#endif 560} 561