// 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); return2;
} bool linear; if (fPart->hullIntersects(*opp->fPart, &linear)) { if (!linear) { // check set true if linear return1;
}
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)) { return0;
} 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; return true;
}
// 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)); return true;
}
bool SkTSect::coincidentHasT(double t) {
SkTSpan* test = fCoincident; while (test) { if (between(test->fStartT, t, test->fEndT)) { return true;
}
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)) { return true;
}
} 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;
}
} return true;
}
bool SkTSect::extractCoincident(
SkTSect* sect2,
SkTSpan* first, SkTSpan* last,
SkTSpan** result) {
first = findCoincidentRun(first, &last); if (!first || !last) {
*result = nullptr; return true;
} // 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; return true;
}
SkOPASSERT(oppStartT == oppFirst->fStartT); if (!oppLast) {
*result = nullptr; return true;
}
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; return true;
}
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) { return0;
} 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)) { return0;
} if (!oppRayI.intersectRay(this->fCurve, oppLine)) { return0;
} // 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)) { return2;
}
} 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)) { return2;
}
} 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) { return0;
}
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) { return0;
}
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) { return0;
}
SkTCoincident coinW; double workT = tStart; double tStep = tEnd - tStart;
SkDPoint workPt; do {
tStep *= 0.5; if (precisely_zero(tStep)) { return0;
}
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)) { return0;
}
i->setMax(1);
i->insert(workT, oppTTest, workPt); return1;
}
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(); return true;
}
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] getsstuckinaloop.Itaddsanextensiontoallowacoincidentend perpendiculartotrackitsintersectionintheoppositecurve.However, theboundingboxoftheextensiondoesnotintersecttheoriginalcurve,
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(); if (!tail1) { return;
} if (approximately_greater_than_one(tail1->fEndT)) { const SkDPoint& end1 = sect1->pointLast(); if (tail1->isBounded()) { double t = tail1->closestBoundedT(end1); if (sect2->fCurve.ptAtT(t).approximatelyEqual(end1)) {
intersections->insert(1, t, end1);
}
}
}
} if (!(zeroOneSet & kOneS2Set)) { const SkTSpan* tail2 = sect2->tail(); if (!tail2) { return;
} if (approximately_greater_than_one(tail2->fEndT)) { const SkDPoint& end2 = sect2->pointLast(); if (tail2->isBounded()) { double t = tail2->closestBoundedT(end2); if (sect1->fCurve.ptAtT(t).approximatelyEqual(end2)) {
intersections->insert(t, 1, end2);
}
}
}
}
SkClosestSect closest; do { while (result1 && result1->fCoinStart.isMatch() && result1->fCoinEnd.isMatch()) {
result1 = result1->fNext;
} if (!result1) { break;
}
SkTSpan* result2 = sect2->fHead; while (result2) {
closest.find(result1, result2 SkDEBUGPARAMS(intersections));
result2 = result2->fNext;
}
} while ((result1 = result1->fNext));
closest.finish(intersections); // if there is more than one intersection and it isn't already coincident, check int last = intersections->used() - 1; for (int index = 0; index < last; ) { if (intersections->isCoincident(index) && intersections->isCoincident(index + 1)) {
++index; continue;
} double midT = ((*intersections)[0][index] + (*intersections)[0][index + 1]) / 2;
SkDPoint midPt = sect1->fCurve.ptAtT(midT); // intersect perpendicular with opposite curve
SkTCoincident perp;
perp.setPerp(sect1->fCurve, midT, midPt, sect2->fCurve); if (!perp.isMatch()) {
++index; continue;
} if (intersections->isCoincident(index)) {
intersections->removeOne(index);
--last;
} elseif (intersections->isCoincident(index + 1)) {
intersections->removeOne(index + 1);
--last;
} else {
intersections->setCoincident(index++);
}
intersections->setCoincident(index);
}
SkOPOBJASSERT(intersections, intersections->used() <= sect1->fCurve.maxIntersections());
}
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.