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    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
118void 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
132void 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
150void 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
294bool 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
305void 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
316void 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
326int 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
336const 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
349void SkOpSegment::debugReset() {
350    fTs.reset();
351    fAngles.reset();
352}
353
354#if DEBUG_CONCIDENT
355void 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
393void 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
440void 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
460void 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
489int 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
511void 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