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