// returns 0 if no hull intersection // 1 if hulls intersect // 2 if hulls only share a common endpoint // -1 if linear and further checking is required
int SkTSpan::hullCheck(const SkTSpan* opp, bool* start, bool* oppStart) { if (fIsLinear) { return -1;
} bool ptsInCommon; if (onlyEndPointsInCommon(opp, start, oppStart, &ptsInCommon)) {
SkASSERT(ptsInCommon); return 2;
} bool linear; if (fPart->hullIntersects(*opp->fPart, &linear)) { if (!linear) { // check set true if linear return 1;
}
fIsLinear = true;
fIsLine = fPart->controlsInside(); return ptsInCommon ? 1 : -1;
} // hull is not linear; check set true if intersected at the end points return ((int) ptsInCommon) << 1; // 0 or 2
}
// OPTIMIZE ? If at_most_end_pts_in_common detects that one quad is near linear, // use line intersection to guess a better split than 0.5 // OPTIMIZE Once at_most_end_pts_in_common detects linear, mark span so all future splits are linear
int SkTSpan::hullsIntersect(SkTSpan* opp, bool* start, bool* oppStart) { if (!fBounds.intersects(opp->fBounds)) { return 0;
} int hullSect = this->hullCheck(opp, start, oppStart); if (hullSect >= 0) { return hullSect;
}
hullSect = opp->hullCheck(this, oppStart, start); if (hullSect >= 0) { return hullSect;
} return -1;
}
bool SkTSect::binarySearchCoin(SkTSect* sect2, double tStart, double tStep, double* resultT, double* oppT, SkTSpan** oppFirst) {
SkTSpan work(fCurve, fHeap); double result = work.fStartT = work.fEndT = tStart;
SkDEBUGCODE(work.fDebugSect = this);
SkDPoint last = fCurve.ptAtT(tStart);
SkDPoint oppPt; bool flip = false; bool contained = false; bool down = tStep < 0; const SkTCurve& opp = sect2->fCurve; do {
tStep *= 0.5;
work.fStartT += tStep; if (flip) {
tStep = -tStep;
flip = false;
}
work.initBounds(fCurve); if (work.fCollapsed) { returnfalse;
} if (last.approximatelyEqual(work.pointFirst())) { break;
}
last = work.pointFirst();
work.fCoinStart.setPerp(fCurve, work.fStartT, last, opp); if (work.fCoinStart.isMatch()) { #if DEBUG_T_SECT
work.validatePerpPt(work.fCoinStart.perpT(), work.fCoinStart.perpPt()); #endif double oppTTest = work.fCoinStart.perpT(); if (sect2->fHead->contains(oppTTest)) {
*oppT = oppTTest;
oppPt = work.fCoinStart.perpPt();
contained = true; if (down ? result <= work.fStartT : result >= work.fStartT) {
*oppFirst = nullptr; // signal caller to fail returnfalse;
}
result = work.fStartT; continue;
}
}
tStep = -tStep;
flip = true;
} while (true); if (!contained) { returnfalse;
} if (last.approximatelyEqual(fCurve[0])) {
result = 0;
} elseif (last.approximatelyEqual(this->pointLast())) {
result = 1;
} if (oppPt.approximatelyEqual(opp[0])) {
*oppT = 0;
} elseif (oppPt.approximatelyEqual(sect2->pointLast())) {
*oppT = 1;
}
*resultT = result; returntrue;
}
// OPTIMIZE ? keep a sorted list of sizes in the form of a doubly-linked list in quad span // so that each quad sect has a pointer to the largest, and can update it as spans // are split
SkTSpan* SkTSect::boundsMax() {
SkTSpan* test = fHead;
SkTSpan* largest = fHead; bool lCollapsed = largest->fCollapsed; int safetyNet = 10000; while ((test = test->fNext)) { if (!--safetyNet) {
fHung = true; return nullptr;
} bool tCollapsed = test->fCollapsed; if ((lCollapsed && !tCollapsed) || (lCollapsed == tCollapsed &&
largest->fBoundsMax < test->fBoundsMax)) {
largest = test;
lCollapsed = test->fCollapsed;
}
} return largest;
}
bool SkTSect::coincidentCheck(SkTSect* sect2) {
SkTSpan* first = fHead; if (!first) { returnfalse;
}
SkTSpan* last, * next; do { int consecutive = this->countConsecutiveSpans(first, &last);
next = last->fNext; if (consecutive < COINCIDENT_SPAN_COUNT) { continue;
}
this->validate();
sect2->validate();
this->computePerpendiculars(sect2, first, last);
this->validate();
sect2->validate(); // check to see if a range of points are on the curve
SkTSpan* coinStart = first; do { bool success = this->extractCoincident(sect2, coinStart, last, &coinStart); if (!success) { returnfalse;
}
} while (coinStart && !last->fDeleted); if (!fHead || !sect2->fHead) { break;
} if (!next || next->fDeleted) { break;
}
} while ((first = next)); returntrue;
}
bool SkTSect::coincidentHasT(double t) {
SkTSpan* test = fCoincident; while (test) { if (between(test->fStartT, t, test->fEndT)) { returntrue;
}
test = test->fNext;
} returnfalse;
}
int SkTSect::collapsed() const { int result = 0; const SkTSpan* test = fHead; while (test) { if (test->fCollapsed) {
++result;
}
test = test->next();
} return result;
}
void SkTSect::computePerpendiculars(SkTSect* sect2,
SkTSpan* first, SkTSpan* last) { if (!last) { return;
} const SkTCurve& opp = sect2->fCurve;
SkTSpan* work = first;
SkTSpan* prior = nullptr; do { if (!work->fHasPerp && !work->fCollapsed) { if (prior) {
work->fCoinStart = prior->fCoinEnd;
} else {
work->fCoinStart.setPerp(fCurve, work->fStartT, work->pointFirst(), opp);
} if (work->fCoinStart.isMatch()) { double perpT = work->fCoinStart.perpT(); if (sect2->coincidentHasT(perpT)) {
work->fCoinStart.init();
} else {
sect2->addForPerp(work, perpT);
}
}
work->fCoinEnd.setPerp(fCurve, work->fEndT, work->pointLast(), opp); if (work->fCoinEnd.isMatch()) { double perpT = work->fCoinEnd.perpT(); if (sect2->coincidentHasT(perpT)) {
work->fCoinEnd.init();
} else {
sect2->addForPerp(work, perpT);
}
}
work->fHasPerp = true;
} if (work == last) { break;
}
prior = work;
work = work->fNext;
SkASSERT(work);
} while (true);
}
int SkTSect::countConsecutiveSpans(SkTSpan* first,
SkTSpan** lastPtr) const { int consecutive = 1;
SkTSpan* last = first; do {
SkTSpan* next = last->fNext; if (!next) { break;
} if (next->fStartT > last->fEndT) { break;
}
++consecutive;
last = next;
} while (true);
*lastPtr = last; return consecutive;
}
bool SkTSect::hasBounded(const SkTSpan* span) const { const SkTSpan* test = fHead; if (!test) { returnfalse;
} do { if (test->findOppSpan(span)) { returntrue;
}
} while ((test = test->next())); returnfalse;
}
bool SkTSect::deleteEmptySpans() {
SkTSpan* test;
SkTSpan* next = fHead; int safetyHatch = 1000; while ((test = next)) {
next = test->fNext; if (!test->fBounded) { if (!this->removeSpan(test)) { returnfalse;
}
} if (--safetyHatch < 0) { returnfalse;
}
} returntrue;
}
bool SkTSect::extractCoincident(
SkTSect* sect2,
SkTSpan* first, SkTSpan* last,
SkTSpan** result) {
first = findCoincidentRun(first, &last); if (!first || !last) {
*result = nullptr; returntrue;
} // march outwards to find limit of coincidence from here to previous and next spans double startT = first->fStartT; double oppStartT SK_INIT_TO_AVOID_WARNING; double oppEndT SK_INIT_TO_AVOID_WARNING;
SkTSpan* prev = first->fPrev;
SkASSERT(first->fCoinStart.isMatch());
SkTSpan* oppFirst = first->findOppT(first->fCoinStart.perpT());
SkOPASSERT(last->fCoinEnd.isMatch()); bool oppMatched = first->fCoinStart.perpT() < first->fCoinEnd.perpT(); double coinStart;
SkDEBUGCODE(double coinEnd);
SkTSpan* cutFirst; if (prev && prev->fEndT == startT
&& this->binarySearchCoin(sect2, startT, prev->fStartT - startT, &coinStart,
&oppStartT, &oppFirst)
&& prev->fStartT < coinStart && coinStart < startT
&& (cutFirst = prev->oppT(oppStartT))) {
oppFirst = cutFirst;
first = this->addSplitAt(prev, coinStart);
first->markCoincident();
prev->fCoinEnd.markCoincident(); if (oppFirst->fStartT < oppStartT && oppStartT < oppFirst->fEndT) {
SkTSpan* oppHalf = sect2->addSplitAt(oppFirst, oppStartT); if (oppMatched) {
oppFirst->fCoinEnd.markCoincident();
oppHalf->markCoincident();
oppFirst = oppHalf;
} else {
oppFirst->markCoincident();
oppHalf->fCoinStart.markCoincident();
}
}
} else { if (!oppFirst) { returnfalse;
}
SkDEBUGCODE(coinStart = first->fStartT);
SkDEBUGCODE(oppStartT = oppMatched ? oppFirst->fStartT : oppFirst->fEndT);
} // FIXME: incomplete : if we're not at the end, find end of coin
SkTSpan* oppLast;
SkOPASSERT(last->fCoinEnd.isMatch());
oppLast = last->findOppT(last->fCoinEnd.perpT());
SkDEBUGCODE(coinEnd = last->fEndT); #ifdef SK_DEBUG if (!this->globalState() || !this->globalState()->debugSkipAssert()) {
oppEndT = oppMatched ? oppLast->fEndT : oppLast->fStartT;
} #endif if (!oppMatched) { using std::swap;
swap(oppFirst, oppLast);
swap(oppStartT, oppEndT);
}
SkOPASSERT(oppStartT < oppEndT);
SkASSERT(coinStart == first->fStartT);
SkASSERT(coinEnd == last->fEndT); if (!oppFirst) {
*result = nullptr; returntrue;
}
SkOPASSERT(oppStartT == oppFirst->fStartT); if (!oppLast) {
*result = nullptr; returntrue;
}
SkOPASSERT(oppEndT == oppLast->fEndT); // reduce coincident runs to single entries
this->validate();
sect2->validate(); bool deleteEmptySpans = this->updateBounded(first, last, oppFirst);
deleteEmptySpans |= sect2->updateBounded(oppFirst, oppLast, first);
this->removeSpanRange(first, last);
sect2->removeSpanRange(oppFirst, oppLast);
first->fEndT = last->fEndT;
first->resetBounds(this->fCurve);
first->fCoinStart.setPerp(fCurve, first->fStartT, first->pointFirst(), sect2->fCurve);
first->fCoinEnd.setPerp(fCurve, first->fEndT, first->pointLast(), sect2->fCurve);
oppStartT = first->fCoinStart.perpT();
oppEndT = first->fCoinEnd.perpT(); if (between(0, oppStartT, 1) && between(0, oppEndT, 1)) { if (!oppMatched) { using std::swap;
swap(oppStartT, oppEndT);
}
oppFirst->fStartT = oppStartT;
oppFirst->fEndT = oppEndT;
oppFirst->resetBounds(sect2->fCurve);
}
this->validateBounded();
sect2->validateBounded();
last = first->fNext; if (!this->removeCoincident(first, false)) { returnfalse;
} if (!sect2->removeCoincident(oppFirst, true)) { returnfalse;
} if (deleteEmptySpans) { if (!this->deleteEmptySpans() || !sect2->deleteEmptySpans()) {
*result = nullptr; returnfalse;
}
}
this->validate();
sect2->validate();
*result = last && !last->fDeleted && fHead && sect2->fHead ? last : nullptr; returntrue;
}
SkTSpan* SkTSect::findCoincidentRun(
SkTSpan* first, SkTSpan** lastPtr) {
SkTSpan* work = first;
SkTSpan* lastCandidate = nullptr;
first = nullptr; // find the first fully coincident span do { if (work->fCoinStart.isMatch()) { #if DEBUG_T_SECT
work->validatePerpT(work->fCoinStart.perpT());
work->validatePerpPt(work->fCoinStart.perpT(), work->fCoinStart.perpPt()); #endif
SkOPASSERT(work->hasOppT(work->fCoinStart.perpT())); if (!work->fCoinEnd.isMatch()) { break;
}
lastCandidate = work; if (!first) {
first = work;
}
} elseif (first && work->fCollapsed) {
*lastPtr = lastCandidate; return first;
} else {
lastCandidate = nullptr;
SkOPASSERT(!first);
} if (work == *lastPtr) { return first;
}
work = work->fNext; if (!work) { return nullptr;
}
} while (true); if (lastCandidate) {
*lastPtr = lastCandidate;
} return first;
}
int SkTSect::intersects(SkTSpan* span,
SkTSect* opp,
SkTSpan* oppSpan, int* oppResult) { bool spanStart, oppStart; int hullResult = span->hullsIntersect(oppSpan, &spanStart, &oppStart); if (hullResult >= 0) { if (hullResult == 2) { // hulls have one point in common if (!span->fBounded || !span->fBounded->fNext) {
SkASSERT(!span->fBounded || span->fBounded->fBounded == oppSpan); if (spanStart) {
span->fEndT = span->fStartT;
} else {
span->fStartT = span->fEndT;
}
} else {
hullResult = 1;
} if (!oppSpan->fBounded || !oppSpan->fBounded->fNext) { if (oppSpan->fBounded && oppSpan->fBounded->fBounded != span) { return 0;
} if (oppStart) {
oppSpan->fEndT = oppSpan->fStartT;
} else {
oppSpan->fStartT = oppSpan->fEndT;
}
*oppResult = 2;
} else {
*oppResult = 1;
}
} else {
*oppResult = 1;
} return hullResult;
} if (span->fIsLine && oppSpan->fIsLine) {
SkIntersections i; int sects = this->linesIntersect(span, opp, oppSpan, &i); if (sects == 2) { return *oppResult = 1;
} if (!sects) { return -1;
}
this->removedEndCheck(span);
span->fStartT = span->fEndT = i[0][0];
opp->removedEndCheck(oppSpan);
oppSpan->fStartT = oppSpan->fEndT = i[1][0]; return *oppResult = 2;
} if (span->fIsLinear || oppSpan->fIsLinear) { return *oppResult = (int) span->linearsIntersect(oppSpan);
} return *oppResult = 1;
}
// while the intersection points are sufficiently far apart: // construct the tangent lines from the intersections // find the point where the tangent line intersects the opposite curve
int SkTSect::linesIntersect(SkTSpan* span,
SkTSect* opp,
SkTSpan* oppSpan, SkIntersections* i) {
SkIntersections thisRayI SkDEBUGCODE((span->fDebugGlobalState));
SkIntersections oppRayI SkDEBUGCODE((span->fDebugGlobalState));
SkDLine thisLine = {{ span->pointFirst(), span->pointLast() }};
SkDLine oppLine = {{ oppSpan->pointFirst(), oppSpan->pointLast() }}; int loopCount = 0; double bestDistSq = DBL_MAX; if (!thisRayI.intersectRay(opp->fCurve, thisLine)) { return 0;
} if (!oppRayI.intersectRay(this->fCurve, oppLine)) { return 0;
} // if the ends of each line intersect the opposite curve, the lines are coincident if (thisRayI.used() > 1) { int ptMatches = 0; for (int tIndex = 0; tIndex < thisRayI.used(); ++tIndex) { for (int lIndex = 0; lIndex < (int) std::size(thisLine.fPts); ++lIndex) {
ptMatches += thisRayI.pt(tIndex).approximatelyEqual(thisLine.fPts[lIndex]);
}
} if (ptMatches == 2 || is_parallel(thisLine, opp->fCurve)) { return 2;
}
} if (oppRayI.used() > 1) { int ptMatches = 0; for (int oIndex = 0; oIndex < oppRayI.used(); ++oIndex) { for (int lIndex = 0; lIndex < (int) std::size(oppLine.fPts); ++lIndex) {
ptMatches += oppRayI.pt(oIndex).approximatelyEqual(oppLine.fPts[lIndex]);
}
} if (ptMatches == 2|| is_parallel(oppLine, this->fCurve)) { return 2;
}
} do { // pick the closest pair of points double closest = DBL_MAX; int closeIndex SK_INIT_TO_AVOID_WARNING; int oppCloseIndex SK_INIT_TO_AVOID_WARNING; for (int index = 0; index < oppRayI.used(); ++index) { if (!roughly_between(span->fStartT, oppRayI[0][index], span->fEndT)) { continue;
} for (int oIndex = 0; oIndex < thisRayI.used(); ++oIndex) { if (!roughly_between(oppSpan->fStartT, thisRayI[0][oIndex], oppSpan->fEndT)) { continue;
} double distSq = thisRayI.pt(index).distanceSquared(oppRayI.pt(oIndex)); if (closest > distSq) {
closest = distSq;
closeIndex = index;
oppCloseIndex = oIndex;
}
}
} if (closest == DBL_MAX) { break;
} const SkDPoint& oppIPt = thisRayI.pt(oppCloseIndex); const SkDPoint& iPt = oppRayI.pt(closeIndex); if (between(span->fStartT, oppRayI[0][closeIndex], span->fEndT)
&& between(oppSpan->fStartT, thisRayI[0][oppCloseIndex], oppSpan->fEndT)
&& oppIPt.approximatelyEqual(iPt)) {
i->merge(oppRayI, closeIndex, thisRayI, oppCloseIndex); return i->used();
} double distSq = oppIPt.distanceSquared(iPt); if (bestDistSq < distSq || ++loopCount > 5) { return 0;
}
bestDistSq = distSq; double oppStart = oppRayI[0][closeIndex];
thisLine[0] = fCurve.ptAtT(oppStart);
thisLine[1] = thisLine[0] + fCurve.dxdyAtT(oppStart); if (!thisRayI.intersectRay(opp->fCurve, thisLine)) { break;
} double start = thisRayI[0][oppCloseIndex];
oppLine[0] = opp->fCurve.ptAtT(start);
oppLine[1] = oppLine[0] + opp->fCurve.dxdyAtT(start); if (!oppRayI.intersectRay(this->fCurve, oppLine)) { break;
}
} while (true); // convergence may fail if the curves are nearly coincident
SkTCoincident oCoinS, oCoinE;
oCoinS.setPerp(opp->fCurve, oppSpan->fStartT, oppSpan->pointFirst(), fCurve);
oCoinE.setPerp(opp->fCurve, oppSpan->fEndT, oppSpan->pointLast(), fCurve); double tStart = oCoinS.perpT(); double tEnd = oCoinE.perpT(); bool swap = tStart > tEnd; if (swap) { using std::swap;
swap(tStart, tEnd);
}
tStart = std::max(tStart, span->fStartT);
tEnd = std::min(tEnd, span->fEndT); if (tStart > tEnd) { return 0;
}
SkDVector perpS, perpE; if (tStart == span->fStartT) {
SkTCoincident coinS;
coinS.setPerp(fCurve, span->fStartT, span->pointFirst(), opp->fCurve);
perpS = span->pointFirst() - coinS.perpPt();
} elseif (swap) {
perpS = oCoinE.perpPt() - oppSpan->pointLast();
} else {
perpS = oCoinS.perpPt() - oppSpan->pointFirst();
} if (tEnd == span->fEndT) {
SkTCoincident coinE;
coinE.setPerp(fCurve, span->fEndT, span->pointLast(), opp->fCurve);
perpE = span->pointLast() - coinE.perpPt();
} elseif (swap) {
perpE = oCoinS.perpPt() - oppSpan->pointFirst();
} else {
perpE = oCoinE.perpPt() - oppSpan->pointLast();
} if (perpS.dot(perpE) >= 0) { return 0;
}
SkTCoincident coinW; double workT = tStart; double tStep = tEnd - tStart;
SkDPoint workPt; do {
tStep *= 0.5; if (precisely_zero(tStep)) { return 0;
}
workT += tStep;
workPt = fCurve.ptAtT(workT);
coinW.setPerp(fCurve, workT, workPt, opp->fCurve); double perpT = coinW.perpT(); if (coinW.isMatch() ? !between(oppSpan->fStartT, perpT, oppSpan->fEndT) : perpT < 0) { continue;
}
SkDVector perpW = workPt - coinW.perpPt(); if ((perpS.dot(perpW) >= 0) == (tStep < 0)) {
tStep = -tStep;
} if (workPt.approximatelyEqual(coinW.perpPt())) { break;
}
} while (true); double oppTTest = coinW.perpT(); if (!opp->fHead->contains(oppTTest)) { return 0;
}
i->setMax(1);
i->insert(workT, oppTTest, workPt); return 1;
}
bool find(const SkTSpan* span1, const SkTSpan* span2
SkDEBUGPARAMS(SkIntersections* i)) {
SkClosestRecord* record = &fClosest[fUsed];
record->findEnd(span1, span2, 0, 0);
record->findEnd(span1, span2, 0, span2->part().pointLast());
record->findEnd(span1, span2, span1->part().pointLast(), 0);
record->findEnd(span1, span2, span1->part().pointLast(), span2->part().pointLast()); if (record->fClosest == FLT_MAX) { returnfalse;
} for (int index = 0; index < fUsed; ++index) {
SkClosestRecord* test = &fClosest[index]; if (test->matesWith(*record SkDEBUGPARAMS(i))) { if (test->fClosest > record->fClosest) {
test->merge(*record);
}
test->update(*record);
record->reset(); returnfalse;
}
}
++fUsed;
fClosest.push_back().reset(); returntrue;
}
void finish(SkIntersections* intersections) const {
STArray<SkDCubic::kMaxIntersections * 3, const SkClosestRecord*, true> closestPtrs; for (int index = 0; index < fUsed; ++index) {
closestPtrs.push_back(&fClosest[index]);
}
SkTQSort<const SkClosestRecord>(closestPtrs.begin(), closestPtrs.end()); for (int index = 0; index < fUsed; ++index) { const SkClosestRecord* test = closestPtrs[index];
test->addIntersection(intersections);
}
}
// this is oversized so that an extra records can merge into final one
STArray<SkDCubic::kMaxIntersections * 2, SkClosestRecord, true> fClosest; int fUsed;
};
// returns true if the rect is too small to consider
void SkTSect::BinarySearch(SkTSect* sect1,
SkTSect* sect2, SkIntersections* intersections) { #if DEBUG_T_SECT_DUMP > 1
gDumpTSectNum = 0; #endif
SkDEBUGCODE(sect1->fOppSect = sect2);
SkDEBUGCODE(sect2->fOppSect = sect1);
intersections->reset();
intersections->setMax(sect1->fCurve.maxIntersections() + 4); // give extra for slop
SkTSpan* span1 = sect1->fHead;
SkTSpan* span2 = sect2->fHead; int oppSect, sect = sect1->intersects(span1, sect2, span2, &oppSect); // SkASSERT(between(0, sect, 2)); if (!sect) { return;
} if (sect == 2 && oppSect == 2) {
(void) EndsEqual(sect1, sect2, intersections); return;
}
span1->addBounded(span2, §1->fHeap);
span2->addBounded(span1, §2->fHeap); constint kMaxCoinLoopCount = 8; int coinLoopCount = kMaxCoinLoopCount; double start1s SK_INIT_TO_AVOID_WARNING; double start1e SK_INIT_TO_AVOID_WARNING; do { // find the largest bounds
SkTSpan* largest1 = sect1->boundsMax(); if (!largest1) { if (sect1->fHung) { return;
} break;
}
SkTSpan* largest2 = sect2->boundsMax(); // split it if (!largest2 || (largest1 && (largest1->fBoundsMax > largest2->fBoundsMax
|| (!largest1->fCollapsed && largest2->fCollapsed)))) { if (sect2->fHung) { return;
} if (largest1->fCollapsed) { break;
}
sect1->resetRemovedEnds();
sect2->resetRemovedEnds(); // trim parts that don't intersect the opposite
SkTSpan* half1 = sect1->addOne();
SkDEBUGCODE(half1->debugSetGlobalState(sect1->globalState())); if (!half1->split(largest1, §1->fHeap)) { break;
} if (!sect1->trim(largest1, sect2)) {
SkOPOBJASSERT(intersections, 0); return;
} if (!sect1->trim(half1, sect2)) {
SkOPOBJASSERT(intersections, 0); return;
}
} else { if (largest2->fCollapsed) { break;
}
sect1->resetRemovedEnds();
sect2->resetRemovedEnds(); // trim parts that don't intersect the opposite
SkTSpan* half2 = sect2->addOne();
SkDEBUGCODE(half2->debugSetGlobalState(sect2->globalState())); if (!half2->split(largest2, §2->fHeap)) { break;
} if (!sect2->trim(largest2, sect1)) {
SkOPOBJASSERT(intersections, 0); return;
} if (!sect2->trim(half2, sect1)) {
SkOPOBJASSERT(intersections, 0); return;
}
}
sect1->validate();
sect2->validate(); #if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kIterations_DebugLoop); #endif // if there are 9 or more continuous spans on both sects, suspect coincidence if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
&& sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { if (coinLoopCount == kMaxCoinLoopCount) {
start1s = sect1->fHead->fStartT;
start1e = sect1->tail()->fEndT;
} if (!sect1->coincidentCheck(sect2)) { return;
}
sect1->validate();
sect2->validate(); #if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kCoinCheck_DebugLoop); #endif if (!--coinLoopCount && sect1->fHead && sect2->fHead) { /* All known working cases resolve in two tries. Sadly, cubicConicTests[0] gets stuck in a loop. It adds an extension to allow a coincident end perpendicular to track its intersection in the opposite curve. However, the bounding box of the extension does not intersect the original curve,
so the extension is discarded, only to be added again the next time around. */
sect1->coincidentForce(sect2, start1s, start1e);
sect1->validate();
sect2->validate();
}
} if (sect1->fActiveCount >= COINCIDENT_SPAN_COUNT
&& sect2->fActiveCount >= COINCIDENT_SPAN_COUNT) { if (!sect1->fHead) { return;
}
sect1->computePerpendiculars(sect2, sect1->fHead, sect1->tail()); if (!sect2->fHead) { return;
}
sect2->computePerpendiculars(sect1, sect2->fHead, sect2->tail()); if (!sect1->removeByPerpendicular(sect2)) { return;
}
sect1->validate();
sect2->validate(); #if DEBUG_T_SECT_LOOP_COUNT
intersections->debugBumpLoopCount(SkIntersections::kComputePerp_DebugLoop); #endif if (sect1->collapsed() > sect1->fCurve.maxIntersections()) { break;
}
} #if DEBUG_T_SECT_DUMP
sect1->dumpBoth(sect2); #endif if (!sect1->fHead || !sect2->fHead) { break;
}
} while (true);
SkTSpan* coincident = sect1->fCoincident; if (coincident) { // if there is more than one coincident span, check loosely to see if they should be joined if (coincident->fNext) {
sect1->mergeCoincidence(sect2);
coincident = sect1->fCoincident;
}
SkASSERT(sect2->fCoincident); // courtesy check : coincidence only looks at sect 1 do { if (!coincident) { return;
} if (!coincident->fCoinStart.isMatch()) { continue;
} if (!coincident->fCoinEnd.isMatch()) { continue;
} double perpT = coincident->fCoinStart.perpT(); if (perpT < 0) { return;
} int index = intersections->insertCoincident(coincident->fStartT,
perpT, coincident->pointFirst()); if ((intersections->insertCoincident(coincident->fEndT,
coincident->fCoinEnd.perpT(),
coincident->pointLast()) < 0) && index >= 0) {
intersections->clearCoincidence(index);
}
} while ((coincident = coincident->fNext));
} int zeroOneSet = EndsEqual(sect1, sect2, intersections); // if (!sect1->fHead || !sect2->fHead) { // if the final iteration contains an end (0 or 1), if (sect1->fRemovedStartT && !(zeroOneSet & kZeroS1Set)) {
SkTCoincident perp; // intersect perpendicular with opposite curve
perp.setPerp(sect1->fCurve, 0, sect1->fCurve[0], sect2->fCurve); if (perp.isMatch()) {
intersections->insert(0, perp.perpT(), perp.perpPt());
}
} if (sect1->fRemovedEndT && !(zeroOneSet & kOneS1Set)) {
SkTCoincident perp;
perp.setPerp(sect1->fCurve, 1, sect1->pointLast(), sect2->fCurve); if (perp.isMatch()) {
intersections->insert(1, perp.perpT(), perp.perpPt());
}
} if (sect2->fRemovedStartT && !(zeroOneSet & kZeroS2Set)) {
SkTCoincident perp;
perp.setPerp(sect2->fCurve, 0, sect2->fCurve[0], sect1->fCurve); if (perp.isMatch()) {
intersections->insert(perp.perpT(), 0, perp.perpPt());
}
} if (sect2->fRemovedEndT && !(zeroOneSet & kOneS2Set)) {
SkTCoincident perp;
perp.setPerp(sect2->fCurve, 1, sect2->pointLast(), sect1->fCurve); if (perp.isMatch()) {
intersections->insert(perp.perpT(), 1, perp.perpPt());
}
} // } if (!sect1->fHead || !sect2->fHead) { return;
}
sect1->recoverCollapsed();
sect2->recoverCollapsed();
SkTSpan* result1 = sect1->fHead; // check heads and tails for zero and ones and insert them if we haven't already done so const SkTSpan* head1 = result1; if (!(zeroOneSet & kZeroS1Set) && approximately_less_than_zero(head1->fStartT)) { const SkDPoint& start1 = sect1->fCurve[0]; if (head1->isBounded()) { double t = head1->closestBoundedT(start1); if (sect2->fCurve.ptAtT(t).approximatelyEqual(start1)) {
intersections->insert(0, t, start1);
}
}
} const SkTSpan* head2 = sect2->fHead; if (!(zeroOneSet & kZeroS2Set) && approximately_less_than_zero(head2->fStartT)) { const SkDPoint& start2 = sect2->fCurve[0]; if (head2->isBounded()) { double t = head2->closestBoundedT(start2); if (sect1->fCurve.ptAtT(t).approximatelyEqual(start2)) {
intersections->insert(t, 0, start2);
}
}
} if (!(zeroOneSet & kOneS1Set)) { const SkTSpan* tail1 = sect1->tail();
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.