// first pass, add missing T values // second pass, determine winding values of overlaps void SkOpContour::addCoincidentPoints() { int count = fCoincidences.count(); for (int index = 0; index < count; ++index) { SkCoincidence& coincidence = fCoincidences[index]; int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) { // OPTIMIZATION: remove from array continue; } #if DEBUG_CONCIDENT thisOne.debugShowTs(); other.debugShowTs(); #endif double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; bool startSwapped, oStartSwapped, cancelers; if ((cancelers = startSwapped = startT > endT)) { SkTSwap(startT, endT); } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if ((oStartSwapped = oStartT > oEndT)) { SkTSwap(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); if (cancelers) { // make sure startT and endT have t entries if (startT > 0 || oEndT < 1 || thisOne.isMissing(startT) || other.isMissing(oEndT)) { thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[startSwapped]); } if (oStartT > 0 || endT < 1 || thisOne.isMissing(endT) || other.isMissing(oStartT)) { other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[oStartSwapped]); } } else { if (startT > 0 || oStartT > 0 || thisOne.isMissing(startT) || other.isMissing(oStartT)) { thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[startSwapped]); } if (endT < 1 || oEndT < 1 || thisOne.isMissing(endT) || other.isMissing(oEndT)) { other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oStartSwapped]); } } #if DEBUG_CONCIDENT thisOne.debugShowTs(); other.debugShowTs(); #endif } }
void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) { if (coincidence.fNearly[0] && coincidence.fNearly[1]) { return; } int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; if (thisOne.done()) { return; } SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if (other.done()) { return; } double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; const SkPoint* startPt = &coincidence.fPts[0][0]; const SkPoint* endPt = &coincidence.fPts[0][1]; bool cancelers; if ((cancelers = startT > endT)) { SkTSwap<double>(startT, endT); SkTSwap<const SkPoint*>(startPt, endPt); } if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing if (endT <= 1 - FLT_EPSILON) { endT += FLT_EPSILON; SkASSERT(endT <= 1); } else { startT -= FLT_EPSILON; SkASSERT(startT >= 0); } } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if (oStartT > oEndT) { SkTSwap<double>(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); if (cancelers) { thisOne.addTCancel(*startPt, *endPt, &other); } else { thisOne.addTCoincident(*startPt, *endPt, endT, &other); } #if DEBUG_CONCIDENT thisOne.debugShowTs("p"); other.debugShowTs("o"); #endif }
void SkOpContour::resolveNearCoincidence() { int count = fCoincidences.count(); for (int index = 0; index < count; ++index) { SkCoincidence& coincidence = fCoincidences[index]; if (!coincidence.fNearly[0] || !coincidence.fNearly[1]) { continue; } int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) { // OPTIMIZATION: remove from coincidence array continue; } #if DEBUG_CONCIDENT thisOne.debugShowTs("-"); other.debugShowTs("o"); #endif double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; bool cancelers; if ((cancelers = startT > endT)) { SkTSwap<double>(startT, endT); } if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing if (endT <= 1 - FLT_EPSILON) { endT += FLT_EPSILON; SkASSERT(endT <= 1); } else { startT -= FLT_EPSILON; SkASSERT(startT >= 0); } } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if (oStartT > oEndT) { SkTSwap<double>(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); if (cancelers) { thisOne.blindCancel(coincidence, &other); } else { thisOne.blindCoincident(coincidence, &other); } } }
// note: caller expects multiple results to be sorted smaller first // note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting // analysis of the quadratic equation, suggesting why the following looks at // the sign of B -- and further suggesting that the greatest loss of precision // is in b squared less two a c int quadraticRootsValidT(double A, double B, double C, double t[2]) { #if 0 B *= 2; double square = B * B - 4 * A * C; if (approximately_negative(square)) { if (!approximately_positive(square)) { return 0; } square = 0; } double squareRt = sqrt(square); double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2; int foundRoots = 0; double ratio = Q / A; if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) { if (approximately_less_than_zero(ratio)) { ratio = 0; } else if (approximately_greater_than_one(ratio)) { ratio = 1; } t[0] = ratio; ++foundRoots; } ratio = C / Q; if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) { if (approximately_less_than_zero(ratio)) { ratio = 0; } else if (approximately_greater_than_one(ratio)) { ratio = 1; } if (foundRoots == 0 || !approximately_negative(ratio - t[0])) { t[foundRoots++] = ratio; } else if (!approximately_negative(t[0] - ratio)) { t[foundRoots++] = t[0]; t[0] = ratio; } } #else double s[2]; int realRoots = quadraticRootsReal(A, B, C, s); int foundRoots = add_valid_ts(s, realRoots, t); #endif return foundRoots; }
void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) { int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; if (thisOne.done()) { return; } SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if (other.done()) { return; } double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; const SkPoint* startPt = &coincidence.fPts[0]; const SkPoint* endPt = &coincidence.fPts[1]; bool cancelers; if ((cancelers = startT > endT)) { SkTSwap<double>(startT, endT); SkTSwap<const SkPoint*>(startPt, endPt); } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if (oStartT > oEndT) { SkTSwap<double>(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); if (cancelers) { thisOne.addTCancel(*startPt, *endPt, &other); } else { thisOne.addTCoincident(*startPt, *endPt, &other); } #if DEBUG_CONCIDENT thisOne.debugShowTs(); other.debugShowTs(); #endif }
static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** current, int* index, int* endIndex, double* tHit, SkScalar* hitDx, bool* tryAgain, bool opp) { double test = 0.9; int contourWinding; do { contourWinding = contourRangeCheckY(contourList, current, index, endIndex, tHit, hitDx, tryAgain, &test, opp); if (contourWinding != SK_MinS32 || *tryAgain) { return contourWinding; } test /= 2; } while (!approximately_negative(test)); SkASSERT(0); // should be OK to comment out, but interested when this hits return contourWinding; }
static int rightAngleWinding(const SkTArray<SkOpContour*, true>& contourList, SkOpSegment** currentPtr, int* indexPtr, int* endIndexPtr, double* tHit, SkScalar* hitDx, bool* tryAgain, bool* onlyVertical, bool opp) { double test = 0.9; int contourWinding; do { contourWinding = contourRangeCheckY(contourList, currentPtr, indexPtr, endIndexPtr, tHit, hitDx, tryAgain, &test, opp); if (contourWinding != SK_MinS32 || *tryAgain) { return contourWinding; } if (*currentPtr && (*currentPtr)->isVertical()) { *onlyVertical = true; return contourWinding; } test /= 2; } while (!approximately_negative(test)); SkASSERT(0); // FIXME: incomplete functionality return contourWinding; }
static bool is_linear_inner(const SkDQuad& q1, double t1s, double t1e, const SkDQuad& q2, double t2s, double t2e, SkIntersections* i, bool* subDivide) { SkDQuad hull = q1.subDivide(t1s, t1e); SkDLine line = {{hull[2], hull[0]}}; const SkDLine* testLines[] = { &line, (const SkDLine*) &hull[0], (const SkDLine*) &hull[1] }; const size_t kTestCount = SK_ARRAY_COUNT(testLines); SkSTArray<kTestCount * 2, double, true> tsFound; for (size_t index = 0; index < kTestCount; ++index) { SkIntersections rootTs; rootTs.allowNear(false); int roots = rootTs.intersect(q2, *testLines[index]); for (int idx2 = 0; idx2 < roots; ++idx2) { double t = rootTs[0][idx2]; #ifdef SK_DEBUG SkDPoint qPt = q2.ptAtT(t); SkDPoint lPt = testLines[index]->ptAtT(rootTs[1][idx2]); SkASSERT(qPt.approximatelyEqual(lPt)); #endif if (approximately_negative(t - t2s) || approximately_positive(t - t2e)) { continue; } tsFound.push_back(rootTs[0][idx2]); } } int tCount = tsFound.count(); if (tCount <= 0) { return true; } double tMin, tMax; if (tCount == 1) { tMin = tMax = tsFound[0]; } else { SkASSERT(tCount > 1); SkTQSort<double>(tsFound.begin(), tsFound.end() - 1); tMin = tsFound[0]; tMax = tsFound[tsFound.count() - 1]; } SkDPoint end = q2.ptAtT(t2s); bool startInTriangle = hull.pointInHull(end); if (startInTriangle) { tMin = t2s; } end = q2.ptAtT(t2e); bool endInTriangle = hull.pointInHull(end); if (endInTriangle) { tMax = t2e; } int split = 0; SkDVector dxy1, dxy2; if (tMin != tMax || tCount > 2) { dxy2 = q2.dxdyAtT(tMin); for (int index = 1; index < tCount; ++index) { dxy1 = dxy2; dxy2 = q2.dxdyAtT(tsFound[index]); double dot = dxy1.dot(dxy2); if (dot < 0) { split = index - 1; break; } } } if (split == 0) { // there's one point if (add_intercept(q1, q2, tMin, tMax, i, subDivide)) { return true; } i->swap(); return is_linear_inner(q2, tMin, tMax, q1, t1s, t1e, i, subDivide); } // At this point, we have two ranges of t values -- treat each separately at the split bool result; if (add_intercept(q1, q2, tMin, tsFound[split - 1], i, subDivide)) { result = true; } else { i->swap(); result = is_linear_inner(q2, tMin, tsFound[split - 1], q1, t1s, t1e, i, subDivide); } if (add_intercept(q1, q2, tsFound[split], tMax, i, subDivide)) { result = true; } else { i->swap(); result |= is_linear_inner(q2, tsFound[split], tMax, q1, t1s, t1e, i, subDivide); } return result; }
// first pass, add missing T values // second pass, determine winding values of overlaps void SkOpContour::addCoincidentPoints() { int count = fCoincidences.count(); for (int index = 0; index < count; ++index) { SkCoincidence& coincidence = fCoincidences[index]; int thisIndex = coincidence.fSegments[0]; SkOpSegment& thisOne = fSegments[thisIndex]; SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; SkOpSegment& other = otherContour->fSegments[otherIndex]; if ((thisOne.done() || other.done()) && thisOne.complete() && other.complete()) { // OPTIMIZATION: remove from array continue; } #if DEBUG_CONCIDENT thisOne.debugShowTs("-"); other.debugShowTs("o"); #endif double startT = coincidence.fTs[0][0]; double endT = coincidence.fTs[0][1]; bool startSwapped, oStartSwapped, cancelers; if ((cancelers = startSwapped = startT > endT)) { SkTSwap(startT, endT); } if (startT == endT) { // if one is very large the smaller may have collapsed to nothing if (endT <= 1 - FLT_EPSILON) { endT += FLT_EPSILON; SkASSERT(endT <= 1); } else { startT -= FLT_EPSILON; SkASSERT(startT >= 0); } } SkASSERT(!approximately_negative(endT - startT)); double oStartT = coincidence.fTs[1][0]; double oEndT = coincidence.fTs[1][1]; if ((oStartSwapped = oStartT > oEndT)) { SkTSwap(oStartT, oEndT); cancelers ^= true; } SkASSERT(!approximately_negative(oEndT - oStartT)); const SkPoint& startPt = coincidence.fPts[0][startSwapped]; if (cancelers) { // make sure startT and endT have t entries if (startT > 0 || oEndT < 1 || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) { thisOne.addTPair(startT, &other, oEndT, true, startPt, coincidence.fPts[1][startSwapped]); } const SkPoint& oStartPt = coincidence.fPts[1][oStartSwapped]; if (oStartT > 0 || endT < 1 || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) { other.addTPair(oStartT, &thisOne, endT, true, oStartPt, coincidence.fPts[0][oStartSwapped]); } } else { if (startT > 0 || oStartT > 0 || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) { thisOne.addTPair(startT, &other, oStartT, true, startPt, coincidence.fPts[1][startSwapped]); } const SkPoint& oEndPt = coincidence.fPts[1][!oStartSwapped]; if (endT < 1 || oEndT < 1 || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) { other.addTPair(oEndT, &thisOne, endT, true, oEndPt, coincidence.fPts[0][!oStartSwapped]); } } #if DEBUG_CONCIDENT thisOne.debugShowTs("+"); other.debugShowTs("o"); #endif } // if there are multiple pairs of coincidence that share an edge, see if the opposite // are also coincident for (int index = 0; index < count - 1; ++index) { const SkCoincidence& coincidence = fCoincidences[index]; int thisIndex = coincidence.fSegments[0]; SkOpContour* otherContour = coincidence.fOther; int otherIndex = coincidence.fSegments[1]; for (int idx2 = 1; idx2 < count; ++idx2) { const SkCoincidence& innerCoin = fCoincidences[idx2]; int innerThisIndex = innerCoin.fSegments[0]; if (thisIndex == innerThisIndex) { checkCoincidentPair(coincidence, 1, innerCoin, 1, false); } if (this == otherContour && otherIndex == innerThisIndex) { checkCoincidentPair(coincidence, 0, innerCoin, 1, false); } SkOpContour* innerOtherContour = innerCoin.fOther; innerThisIndex = innerCoin.fSegments[1]; if (this == innerOtherContour && thisIndex == innerThisIndex) { checkCoincidentPair(coincidence, 1, innerCoin, 0, false); } if (otherContour == innerOtherContour && otherIndex == innerThisIndex) { checkCoincidentPair(coincidence, 0, innerCoin, 0, false); } } } }