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