| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #include "SkIntersections.h" |
| #include "SkOpAngle.h" |
| #include "SkOpSegment.h" |
| #include "SkPathOpsCurve.h" |
| #include "SkTSort.h" |
| |
| #if DEBUG_ANGLE |
| #include "SkString.h" |
| #endif |
| |
| /* Angles are sorted counterclockwise. The smallest angle has a positive x and the smallest |
| positive y. The largest angle has a positive x and a zero y. */ |
| |
| #if DEBUG_ANGLE |
| static bool CompareResult(SkString* bugOut, int append, bool compare) { |
| SkDebugf("%s %c %d\n", bugOut->c_str(), compare ? 'T' : 'F', append); |
| return compare; |
| } |
| |
| #define COMPARE_RESULT(append, compare) CompareResult(&bugOut, append, compare) |
| #else |
| #define COMPARE_RESULT(append, compare) compare |
| #endif |
| |
| /* quarter angle values for sector |
| |
| 31 x > 0, y == 0 horizontal line (to the right) |
| 0 x > 0, y == epsilon quad/cubic horizontal tangent eventually going +y |
| 1 x > 0, y > 0, x > y nearer horizontal angle |
| 2 x + e == y quad/cubic 45 going horiz |
| 3 x > 0, y > 0, x == y 45 angle |
| 4 x == y + e quad/cubic 45 going vert |
| 5 x > 0, y > 0, x < y nearer vertical angle |
| 6 x == epsilon, y > 0 quad/cubic vertical tangent eventually going +x |
| 7 x == 0, y > 0 vertical line (to the top) |
| |
| 8 7 6 |
| 9 | 5 |
| 10 | 4 |
| 11 | 3 |
| 12 \ | / 2 |
| 13 | 1 |
| 14 | 0 |
| 15 --------------+------------- 31 |
| 16 | 30 |
| 17 | 29 |
| 18 / | \ 28 |
| 19 | 27 |
| 20 | 26 |
| 21 | 25 |
| 22 23 24 |
| */ |
| |
| // return true if lh < this < rh |
| bool SkOpAngle::after(const SkOpAngle* test) const { |
| const SkOpAngle& lh = *test; |
| const SkOpAngle& rh = *lh.fNext; |
| SkASSERT(&lh != &rh); |
| #if DEBUG_ANGLE |
| SkString bugOut; |
| bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__, |
| lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd, |
| lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd), |
| fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart), |
| fSegment->t(fEnd), |
| rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd, |
| rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd)); |
| #endif |
| if (lh.fComputeSector && !const_cast<SkOpAngle&>(lh).computeSector()) { |
| return COMPARE_RESULT(1, true); |
| } |
| if (fComputeSector && !const_cast<SkOpAngle*>(this)->computeSector()) { |
| return COMPARE_RESULT(2, true); |
| } |
| if (rh.fComputeSector && !const_cast<SkOpAngle&>(rh).computeSector()) { |
| return COMPARE_RESULT(3, true); |
| } |
| #if DEBUG_ANGLE // reset bugOut with computed sectors |
| bugOut.printf("%s [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g" |
| " < [%d/%d] %d/%d tStart=%1.9g tEnd=%1.9g ", __FUNCTION__, |
| lh.fSegment->debugID(), lh.debugID(), lh.fSectorStart, lh.fSectorEnd, |
| lh.fSegment->t(lh.fStart), lh.fSegment->t(lh.fEnd), |
| fSegment->debugID(), debugID(), fSectorStart, fSectorEnd, fSegment->t(fStart), |
| fSegment->t(fEnd), |
| rh.fSegment->debugID(), rh.debugID(), rh.fSectorStart, rh.fSectorEnd, |
| rh.fSegment->t(rh.fStart), rh.fSegment->t(rh.fEnd)); |
| #endif |
| bool ltrOverlap = (lh.fSectorMask | rh.fSectorMask) & fSectorMask; |
| bool lrOverlap = lh.fSectorMask & rh.fSectorMask; |
| int lrOrder; // set to -1 if either order works |
| if (!lrOverlap) { // no lh/rh sector overlap |
| if (!ltrOverlap) { // no lh/this/rh sector overlap |
| return COMPARE_RESULT(4, (lh.fSectorEnd > rh.fSectorStart) |
| ^ (fSectorStart > lh.fSectorEnd) ^ (fSectorStart > rh.fSectorStart)); |
| } |
| int lrGap = (rh.fSectorStart - lh.fSectorStart + 32) & 0x1f; |
| /* A tiny change can move the start +/- 4. The order can only be determined if |
| lr gap is not 12 to 20 or -12 to -20. |
| -31 ..-21 1 |
| -20 ..-12 -1 |
| -11 .. -1 0 |
| 0 shouldn't get here |
| 11 .. 1 1 |
| 12 .. 20 -1 |
| 21 .. 31 0 |
| */ |
| lrOrder = lrGap > 20 ? 0 : lrGap > 11 ? -1 : 1; |
| } else { |
| lrOrder = (int) lh.orderable(rh); |
| if (!ltrOverlap) { |
| return COMPARE_RESULT(5, !lrOrder); |
| } |
| } |
| int ltOrder; |
| SkASSERT((lh.fSectorMask & fSectorMask) || (rh.fSectorMask & fSectorMask)); |
| if (lh.fSectorMask & fSectorMask) { |
| ltOrder = (int) lh.orderable(*this); |
| } else { |
| int ltGap = (fSectorStart - lh.fSectorStart + 32) & 0x1f; |
| ltOrder = ltGap > 20 ? 0 : ltGap > 11 ? -1 : 1; |
| } |
| int trOrder; |
| if (rh.fSectorMask & fSectorMask) { |
| trOrder = (int) orderable(rh); |
| } else { |
| int trGap = (rh.fSectorStart - fSectorStart + 32) & 0x1f; |
| trOrder = trGap > 20 ? 0 : trGap > 11 ? -1 : 1; |
| } |
| if (lrOrder >= 0 && ltOrder >= 0 && trOrder >= 0) { |
| return COMPARE_RESULT(7, lrOrder ? (ltOrder & trOrder) : (ltOrder | trOrder)); |
| } |
| SkASSERT(lrOrder >= 0 || ltOrder >= 0 || trOrder >= 0); |
| // There's not enough information to sort. Get the pairs of angles in opposite planes. |
| // If an order is < 0, the pair is already in an opposite plane. Check the remaining pairs. |
| // FIXME : once all variants are understood, rewrite this more simply |
| if (ltOrder == 0 && lrOrder == 0) { |
| SkASSERT(trOrder < 0); |
| // FIXME : once this is verified to work, remove one opposite angle call |
| SkDEBUGCODE(bool lrOpposite = lh.oppositePlanes(rh)); |
| bool ltOpposite = lh.oppositePlanes(*this); |
| SkASSERT(lrOpposite != ltOpposite); |
| return COMPARE_RESULT(8, ltOpposite); |
| } else if (ltOrder == 1 && trOrder == 0) { |
| SkASSERT(lrOrder < 0); |
| SkDEBUGCODE(bool ltOpposite = lh.oppositePlanes(*this)); |
| bool trOpposite = oppositePlanes(rh); |
| SkASSERT(ltOpposite != trOpposite); |
| return COMPARE_RESULT(9, trOpposite); |
| } else if (lrOrder == 1 && trOrder == 1) { |
| SkASSERT(ltOrder < 0); |
| SkDEBUGCODE(bool trOpposite = oppositePlanes(rh)); |
| bool lrOpposite = lh.oppositePlanes(rh); |
| SkASSERT(lrOpposite != trOpposite); |
| return COMPARE_RESULT(10, lrOpposite); |
| } |
| if (lrOrder < 0) { |
| if (ltOrder < 0) { |
| return COMPARE_RESULT(11, trOrder); |
| } |
| return COMPARE_RESULT(12, ltOrder); |
| } |
| return COMPARE_RESULT(13, !lrOrder); |
| } |
| |
| // given a line, see if the opposite curve's convex hull is all on one side |
| // returns -1=not on one side 0=this CW of test 1=this CCW of test |
| int SkOpAngle::allOnOneSide(const SkOpAngle& test) const { |
| SkASSERT(!fIsCurve); |
| SkASSERT(test.fIsCurve); |
| const SkDPoint& origin = test.fCurvePart[0]; |
| SkVector line; |
| if (fSegment->verb() == SkPath::kLine_Verb) { |
| const SkPoint* linePts = fSegment->pts(); |
| int lineStart = fStart < fEnd ? 0 : 1; |
| line = linePts[lineStart ^ 1] - linePts[lineStart]; |
| } else { |
| SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() }; |
| line = shortPts[1] - shortPts[0]; |
| } |
| float crosses[3]; |
| SkPath::Verb testVerb = test.fSegment->verb(); |
| int iMax = SkPathOpsVerbToPoints(testVerb); |
| // SkASSERT(origin == test.fCurveHalf[0]); |
| const SkDCubic& testCurve = test.fCurvePart; |
| // do { |
| for (int index = 1; index <= iMax; ++index) { |
| float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY)); |
| float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX)); |
| crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2; |
| } |
| if (crosses[0] * crosses[1] < 0) { |
| return -1; |
| } |
| if (SkPath::kCubic_Verb == testVerb) { |
| if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) { |
| return -1; |
| } |
| } |
| if (crosses[0]) { |
| return crosses[0] < 0; |
| } |
| if (crosses[1]) { |
| return crosses[1] < 0; |
| } |
| if (SkPath::kCubic_Verb == testVerb && crosses[2]) { |
| return crosses[2] < 0; |
| } |
| fUnorderable = true; |
| return -1; |
| } |
| |
| bool SkOpAngle::calcSlop(double x, double y, double rx, double ry, bool* result) const { |
| double absX = fabs(x); |
| double absY = fabs(y); |
| double length = absX < absY ? absX / 2 + absY : absX + absY / 2; |
| int exponent; |
| (void) frexp(length, &exponent); |
| double epsilon = ldexp(FLT_EPSILON, exponent); |
| SkPath::Verb verb = fSegment->verb(); |
| SkASSERT(verb == SkPath::kQuad_Verb || verb == SkPath::kCubic_Verb); |
| // FIXME: the quad and cubic factors are made up ; determine actual values |
| double slop = verb == SkPath::kQuad_Verb ? 4 * epsilon : 512 * epsilon; |
| double xSlop = slop; |
| double ySlop = x * y < 0 ? -xSlop : xSlop; // OPTIMIZATION: use copysign / _copysign ? |
| double x1 = x - xSlop; |
| double y1 = y + ySlop; |
| double x_ry1 = x1 * ry; |
| double rx_y1 = rx * y1; |
| *result = x_ry1 < rx_y1; |
| double x2 = x + xSlop; |
| double y2 = y - ySlop; |
| double x_ry2 = x2 * ry; |
| double rx_y2 = rx * y2; |
| bool less2 = x_ry2 < rx_y2; |
| return *result == less2; |
| } |
| |
| bool SkOpAngle::checkCrossesZero() const { |
| int start = SkTMin(fSectorStart, fSectorEnd); |
| int end = SkTMax(fSectorStart, fSectorEnd); |
| bool crossesZero = end - start > 16; |
| return crossesZero; |
| } |
| |
| bool SkOpAngle::checkParallel(const SkOpAngle& rh) const { |
| SkDVector scratch[2]; |
| const SkDVector* sweep, * tweep; |
| if (!fUnorderedSweep) { |
| sweep = fSweep; |
| } else { |
| scratch[0] = fCurvePart[1] - fCurvePart[0]; |
| sweep = &scratch[0]; |
| } |
| if (!rh.fUnorderedSweep) { |
| tweep = rh.fSweep; |
| } else { |
| scratch[1] = rh.fCurvePart[1] - rh.fCurvePart[0]; |
| tweep = &scratch[1]; |
| } |
| double s0xt0 = sweep->crossCheck(*tweep); |
| if (tangentsDiverge(rh, s0xt0)) { |
| return s0xt0 < 0; |
| } |
| SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0]; |
| SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0]; |
| double m0xm1 = m0.crossCheck(m1); |
| if (m0xm1 == 0) { |
| fUnorderable = true; |
| rh.fUnorderable = true; |
| return true; |
| } |
| return m0xm1 < 0; |
| } |
| |
| // the original angle is too short to get meaningful sector information |
| // lengthen it until it is long enough to be meaningful or leave it unset if lengthening it |
| // would cause it to intersect one of the adjacent angles |
| bool SkOpAngle::computeSector() { |
| if (fComputedSector) { |
| return !fUnorderable; |
| } |
| // SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small()); |
| fComputedSector = true; |
| int step = fStart < fEnd ? 1 : -1; |
| int limit = step > 0 ? fSegment->count() : -1; |
| int checkEnd = fEnd; |
| do { |
| // advance end |
| const SkOpSpan& span = fSegment->span(checkEnd); |
| const SkOpSegment* other = span.fOther; |
| int oCount = other->count(); |
| for (int oIndex = 0; oIndex < oCount; ++oIndex) { |
| const SkOpSpan& oSpan = other->span(oIndex); |
| if (oSpan.fOther != fSegment) { |
| continue; |
| } |
| if (oSpan.fOtherIndex == checkEnd) { |
| continue; |
| } |
| if (!approximately_equal(oSpan.fOtherT, span.fT)) { |
| continue; |
| } |
| goto recomputeSector; |
| } |
| checkEnd += step; |
| } while (checkEnd != limit); |
| recomputeSector: |
| if (checkEnd == fEnd || checkEnd - step == fEnd) { |
| fUnorderable = true; |
| return false; |
| } |
| int saveEnd = fEnd; |
| fComputedEnd = fEnd = checkEnd - step; |
| setSpans(); |
| setSector(); |
| fEnd = saveEnd; |
| return !fUnorderable; |
| } |
| |
| // returns -1 if overlaps 0 if no overlap cw 1 if no overlap ccw |
| int SkOpAngle::convexHullOverlaps(const SkOpAngle& rh) const { |
| const SkDVector* sweep = fSweep; |
| const SkDVector* tweep = rh.fSweep; |
| double s0xs1 = sweep[0].crossCheck(sweep[1]); |
| double s0xt0 = sweep[0].crossCheck(tweep[0]); |
| double s1xt0 = sweep[1].crossCheck(tweep[0]); |
| bool tBetweenS = s0xs1 > 0 ? s0xt0 > 0 && s1xt0 < 0 : s0xt0 < 0 && s1xt0 > 0; |
| double s0xt1 = sweep[0].crossCheck(tweep[1]); |
| double s1xt1 = sweep[1].crossCheck(tweep[1]); |
| tBetweenS |= s0xs1 > 0 ? s0xt1 > 0 && s1xt1 < 0 : s0xt1 < 0 && s1xt1 > 0; |
| double t0xt1 = tweep[0].crossCheck(tweep[1]); |
| if (tBetweenS) { |
| return -1; |
| } |
| if ((s0xt0 == 0 && s1xt1 == 0) || (s1xt0 == 0 && s0xt1 == 0)) { // s0 to s1 equals t0 to t1 |
| return -1; |
| } |
| bool sBetweenT = t0xt1 > 0 ? s0xt0 < 0 && s0xt1 > 0 : s0xt0 > 0 && s0xt1 < 0; |
| sBetweenT |= t0xt1 > 0 ? s1xt0 < 0 && s1xt1 > 0 : s1xt0 > 0 && s1xt1 < 0; |
| if (sBetweenT) { |
| return -1; |
| } |
| // if all of the sweeps are in the same half plane, then the order of any pair is enough |
| if (s0xt0 >= 0 && s0xt1 >= 0 && s1xt0 >= 0 && s1xt1 >= 0) { |
| return 0; |
| } |
| if (s0xt0 <= 0 && s0xt1 <= 0 && s1xt0 <= 0 && s1xt1 <= 0) { |
| return 1; |
| } |
| // if the outside sweeps are greater than 180 degress: |
| // first assume the inital tangents are the ordering |
| // if the midpoint direction matches the inital order, that is enough |
| SkDVector m0 = fSegment->dPtAtT(midT()) - fCurvePart[0]; |
| SkDVector m1 = rh.fSegment->dPtAtT(rh.midT()) - rh.fCurvePart[0]; |
| double m0xm1 = m0.crossCheck(m1); |
| if (s0xt0 > 0 && m0xm1 > 0) { |
| return 0; |
| } |
| if (s0xt0 < 0 && m0xm1 < 0) { |
| return 1; |
| } |
| if (tangentsDiverge(rh, s0xt0)) { |
| return s0xt0 < 0; |
| } |
| return m0xm1 < 0; |
| } |
| |
| // OPTIMIZATION: longest can all be either lazily computed here or precomputed in setup |
| double SkOpAngle::distEndRatio(double dist) const { |
| double longest = 0; |
| const SkOpSegment& segment = *this->segment(); |
| int ptCount = SkPathOpsVerbToPoints(segment.verb()); |
| const SkPoint* pts = segment.pts(); |
| for (int idx1 = 0; idx1 <= ptCount - 1; ++idx1) { |
| for (int idx2 = idx1 + 1; idx2 <= ptCount; ++idx2) { |
| if (idx1 == idx2) { |
| continue; |
| } |
| SkDVector v; |
| v.set(pts[idx2] - pts[idx1]); |
| double lenSq = v.lengthSquared(); |
| longest = SkTMax(longest, lenSq); |
| } |
| } |
| return sqrt(longest) / dist; |
| } |
| |
| bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const { |
| SkPath::Verb lVerb = fSegment->verb(); |
| SkPath::Verb rVerb = rh.fSegment->verb(); |
| int lPts = SkPathOpsVerbToPoints(lVerb); |
| int rPts = SkPathOpsVerbToPoints(rVerb); |
| SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}}, |
| {{fCurvePart[0], fCurvePart[lPts]}}}; |
| if (rays[0][1] == rays[1][1]) { |
| return checkParallel(rh); |
| } |
| double smallTs[2] = {-1, -1}; |
| bool limited[2] = {false, false}; |
| for (int index = 0; index < 2; ++index) { |
| const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; |
| SkIntersections i; |
| int cPts = index ? rPts : lPts; |
| (*CurveIntersectRay[cPts])(segment.pts(), rays[index], &i); |
| // if the curve is a line, then the line and the ray intersect only at their crossing |
| if (cPts == 1) { // line |
| continue; |
| } |
| // SkASSERT(i.used() >= 1); |
| // if (i.used() <= 1) { |
| // continue; |
| // } |
| double tStart = segment.t(index ? rh.fStart : fStart); |
| double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd); |
| bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd; |
| double t = testAscends ? 0 : 1; |
| for (int idx2 = 0; idx2 < i.used(); ++idx2) { |
| double testT = i[0][idx2]; |
| if (!approximately_between_orderable(tStart, testT, tEnd)) { |
| continue; |
| } |
| if (approximately_equal_orderable(tStart, testT)) { |
| continue; |
| } |
| smallTs[index] = t = testAscends ? SkTMax(t, testT) : SkTMin(t, testT); |
| limited[index] = approximately_equal_orderable(t, tEnd); |
| } |
| } |
| #if 0 |
| if (smallTs[0] < 0 && smallTs[1] < 0) { // if neither ray intersects, do endpoint sort |
| double m0xm1 = 0; |
| if (lVerb == SkPath::kLine_Verb) { |
| SkASSERT(rVerb != SkPath::kLine_Verb); |
| SkDVector m0 = rays[1][1] - fCurvePart[0]; |
| SkDPoint endPt; |
| endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]); |
| SkDVector m1 = endPt - fCurvePart[0]; |
| m0xm1 = m0.crossCheck(m1); |
| } |
| if (rVerb == SkPath::kLine_Verb) { |
| SkDPoint endPt; |
| endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]); |
| SkDVector m0 = endPt - fCurvePart[0]; |
| SkDVector m1 = rays[0][1] - fCurvePart[0]; |
| m0xm1 = m0.crossCheck(m1); |
| } |
| if (m0xm1 != 0) { |
| return m0xm1 < 0; |
| } |
| } |
| #endif |
| bool sRayLonger = false; |
| SkDVector sCept = {0, 0}; |
| double sCeptT = -1; |
| int sIndex = -1; |
| bool useIntersect = false; |
| for (int index = 0; index < 2; ++index) { |
| if (smallTs[index] < 0) { |
| continue; |
| } |
| const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; |
| const SkDPoint& dPt = segment.dPtAtT(smallTs[index]); |
| SkDVector cept = dPt - rays[index][0]; |
| // If this point is on the curve, it should have been detected earlier by ordinary |
| // curve intersection. This may be hard to determine in general, but for lines, |
| // the point could be close to or equal to its end, but shouldn't be near the start. |
| if ((index ? lPts : rPts) == 1) { |
| SkDVector total = rays[index][1] - rays[index][0]; |
| if (cept.lengthSquared() * 2 < total.lengthSquared()) { |
| continue; |
| } |
| } |
| SkDVector end = rays[index][1] - rays[index][0]; |
| if (cept.fX * end.fX < 0 || cept.fY * end.fY < 0) { |
| continue; |
| } |
| double rayDist = cept.length(); |
| double endDist = end.length(); |
| bool rayLonger = rayDist > endDist; |
| if (limited[0] && limited[1] && rayLonger) { |
| useIntersect = true; |
| sRayLonger = rayLonger; |
| sCept = cept; |
| sCeptT = smallTs[index]; |
| sIndex = index; |
| break; |
| } |
| double delta = fabs(rayDist - endDist); |
| double minX, minY, maxX, maxY; |
| minX = minY = SK_ScalarInfinity; |
| maxX = maxY = -SK_ScalarInfinity; |
| const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart; |
| int ptCount = index ? rPts : lPts; |
| for (int idx2 = 0; idx2 <= ptCount; ++idx2) { |
| minX = SkTMin(minX, curve[idx2].fX); |
| minY = SkTMin(minY, curve[idx2].fY); |
| maxX = SkTMax(maxX, curve[idx2].fX); |
| maxY = SkTMax(maxY, curve[idx2].fY); |
| } |
| double maxWidth = SkTMax(maxX - minX, maxY - minY); |
| delta /= maxWidth; |
| if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic number |
| sRayLonger = rayLonger; |
| sCept = cept; |
| sCeptT = smallTs[index]; |
| sIndex = index; |
| } |
| } |
| if (useIntersect) { |
| const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart; |
| const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment; |
| double tStart = segment.t(sIndex ? rh.fStart : fStart); |
| SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0]; |
| double septDir = mid.crossCheck(sCept); |
| if (!septDir) { |
| return checkParallel(rh); |
| } |
| return sRayLonger ^ (sIndex == 0) ^ (septDir < 0); |
| } else { |
| return checkParallel(rh); |
| } |
| } |
| |
| // Most of the time, the first one can be found trivially by detecting the smallest sector value. |
| // If all angles have the same sector value, actual sorting is required. |
| const SkOpAngle* SkOpAngle::findFirst() const { |
| const SkOpAngle* best = this; |
| int bestStart = SkTMin(fSectorStart, fSectorEnd); |
| const SkOpAngle* angle = this; |
| while ((angle = angle->fNext) != this) { |
| int angleEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| if (angleEnd < bestStart) { |
| return angle; // we wrapped around |
| } |
| int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| if (bestStart > angleStart) { |
| best = angle; |
| bestStart = angleStart; |
| } |
| } |
| // back up to the first possible angle |
| const SkOpAngle* firstBest = best; |
| angle = best; |
| int bestEnd = SkTMax(best->fSectorStart, best->fSectorEnd); |
| while ((angle = angle->previous()) != firstBest) { |
| if (angle->fStop) { |
| break; |
| } |
| int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| // angles that are smaller by one aren't necessary better, since the larger may be a line |
| // and the smaller may be a curve that curls to the other side of the line. |
| if (bestEnd + 1 < angleStart) { |
| return best; |
| } |
| best = angle; |
| bestEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| } |
| // in the case where all angles are nearly in the same sector, check the order to find the best |
| firstBest = best; |
| angle = best; |
| do { |
| angle = angle->fNext; |
| if (angle->fStop) { |
| return firstBest; |
| } |
| bool orderable = best->orderable(*angle); // note: may return an unorderable angle |
| if (orderable == 0) { |
| return angle; |
| } |
| best = angle; |
| } while (angle != firstBest); |
| // if the angles are equally ordered, fall back on the initial tangent |
| bool foundBelow = false; |
| while ((angle = angle->fNext)) { |
| SkDVector scratch[2]; |
| const SkDVector* sweep; |
| if (!angle->fUnorderedSweep) { |
| sweep = angle->fSweep; |
| } else { |
| scratch[0] = angle->fCurvePart[1] - angle->fCurvePart[0]; |
| sweep = &scratch[0]; |
| } |
| bool isAbove = sweep->fY <= 0; |
| if (isAbove && foundBelow) { |
| return angle; |
| } |
| foundBelow |= !isAbove; |
| if (angle == firstBest) { |
| return NULL; // should not loop around |
| } |
| } |
| SkASSERT(0); // should never get here |
| return NULL; |
| } |
| |
| /* y<0 y==0 y>0 x<0 x==0 x>0 xy<0 xy==0 xy>0 |
| 0 x x x |
| 1 x x x |
| 2 x x x |
| 3 x x x |
| 4 x x x |
| 5 x x x |
| 6 x x x |
| 7 x x x |
| 8 x x x |
| 9 x x x |
| 10 x x x |
| 11 x x x |
| 12 x x x |
| 13 x x x |
| 14 x x x |
| 15 x x x |
| */ |
| int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const { |
| double absX = fabs(x); |
| double absY = fabs(y); |
| double xy = SkPath::kLine_Verb == verb || !AlmostEqualUlps(absX, absY) ? absX - absY : 0; |
| // If there are four quadrants and eight octants, and since the Latin for sixteen is sedecim, |
| // one could coin the term sedecimant for a space divided into 16 sections. |
| // http://english.stackexchange.com/questions/133688/word-for-something-partitioned-into-16-parts |
| static const int sedecimant[3][3][3] = { |
| // y<0 y==0 y>0 |
| // x<0 x==0 x>0 x<0 x==0 x>0 x<0 x==0 x>0 |
| {{ 4, 3, 2}, { 7, -1, 15}, {10, 11, 12}}, // abs(x) < abs(y) |
| {{ 5, -1, 1}, {-1, -1, -1}, { 9, -1, 13}}, // abs(x) == abs(y) |
| {{ 6, 3, 0}, { 7, -1, 15}, { 8, 11, 14}}, // abs(x) > abs(y) |
| }; |
| int sector = sedecimant[(xy >= 0) + (xy > 0)][(y >= 0) + (y > 0)][(x >= 0) + (x > 0)] * 2 + 1; |
| // SkASSERT(SkPath::kLine_Verb == verb || sector >= 0); |
| return sector; |
| } |
| |
| // OPTIMIZE: if this loops to only one other angle, after first compare fails, insert on other side |
| // OPTIMIZE: return where insertion succeeded. Then, start next insertion on opposite side |
| void SkOpAngle::insert(SkOpAngle* angle) { |
| if (angle->fNext) { |
| if (loopCount() >= angle->loopCount()) { |
| if (!merge(angle)) { |
| return; |
| } |
| } else if (fNext) { |
| if (!angle->merge(this)) { |
| return; |
| } |
| } else { |
| angle->insert(this); |
| } |
| return; |
| } |
| bool singleton = NULL == fNext; |
| if (singleton) { |
| fNext = this; |
| } |
| SkOpAngle* next = fNext; |
| if (next->fNext == this) { |
| if (angle->overlap(*this)) { // angles are essentially coincident |
| return; |
| } |
| if (singleton || angle->after(this)) { |
| this->fNext = angle; |
| angle->fNext = next; |
| } else { |
| next->fNext = angle; |
| angle->fNext = this; |
| } |
| debugValidateNext(); |
| return; |
| } |
| SkOpAngle* last = this; |
| do { |
| SkASSERT(last->fNext == next); |
| if (angle->overlap(*last) || angle->overlap(*next)) { |
| return; |
| } |
| if (angle->after(last)) { |
| last->fNext = angle; |
| angle->fNext = next; |
| debugValidateNext(); |
| return; |
| } |
| last = next; |
| next = next->fNext; |
| if (last == this && next->fUnorderable) { |
| fUnorderable = true; |
| return; |
| } |
| SkASSERT(last != this); |
| } while (true); |
| } |
| |
| bool SkOpAngle::isHorizontal() const { |
| return !fIsCurve && fSweep[0].fY == 0; |
| } |
| |
| SkOpSpan* SkOpAngle::lastMarked() const { |
| if (fLastMarked) { |
| if (fLastMarked->fChased) { |
| return NULL; |
| } |
| fLastMarked->fChased = true; |
| } |
| return fLastMarked; |
| } |
| |
| bool SkOpAngle::loopContains(const SkOpAngle& test) const { |
| if (!fNext) { |
| return false; |
| } |
| const SkOpAngle* first = this; |
| const SkOpAngle* loop = this; |
| const SkOpSegment* tSegment = test.fSegment; |
| double tStart = tSegment->span(test.fStart).fT; |
| double tEnd = tSegment->span(test.fEnd).fT; |
| do { |
| const SkOpSegment* lSegment = loop->fSegment; |
| // FIXME : use precisely_equal ? or compare points exactly ? |
| if (lSegment != tSegment) { |
| continue; |
| } |
| double lStart = lSegment->span(loop->fStart).fT; |
| if (lStart != tEnd) { |
| continue; |
| } |
| double lEnd = lSegment->span(loop->fEnd).fT; |
| if (lEnd == tStart) { |
| return true; |
| } |
| } while ((loop = loop->fNext) != first); |
| return false; |
| } |
| |
| int SkOpAngle::loopCount() const { |
| int count = 0; |
| const SkOpAngle* first = this; |
| const SkOpAngle* next = this; |
| do { |
| next = next->fNext; |
| ++count; |
| } while (next && next != first); |
| return count; |
| } |
| |
| // OPTIMIZATION: can this be done better in after when angles are sorted? |
| void SkOpAngle::markStops() { |
| SkOpAngle* angle = this; |
| int lastEnd = SkTMax(fSectorStart, fSectorEnd); |
| do { |
| angle = angle->fNext; |
| int angleStart = SkTMin(angle->fSectorStart, angle->fSectorEnd); |
| // angles that are smaller by one aren't necessary better, since the larger may be a line |
| // and the smaller may be a curve that curls to the other side of the line. |
| if (lastEnd + 1 < angleStart) { |
| angle->fStop = true; |
| } |
| lastEnd = SkTMax(angle->fSectorStart, angle->fSectorEnd); |
| } while (angle != this); |
| } |
| |
| bool SkOpAngle::merge(SkOpAngle* angle) { |
| SkASSERT(fNext); |
| SkASSERT(angle->fNext); |
| SkOpAngle* working = angle; |
| do { |
| if (this == working) { |
| return false; |
| } |
| working = working->fNext; |
| } while (working != angle); |
| do { |
| SkOpAngle* next = working->fNext; |
| working->fNext = NULL; |
| insert(working); |
| working = next; |
| } while (working != angle); |
| // it's likely that a pair of the angles are unorderable |
| #if 0 && DEBUG_ANGLE |
| SkOpAngle* last = angle; |
| working = angle->fNext; |
| do { |
| SkASSERT(last->fNext == working); |
| last->fNext = working->fNext; |
| SkASSERT(working->after(last)); |
| last->fNext = working; |
| last = working; |
| working = working->fNext; |
| } while (last != angle); |
| #endif |
| debugValidateNext(); |
| return true; |
| } |
| |
| double SkOpAngle::midT() const { |
| return (fSegment->t(fStart) + fSegment->t(fEnd)) / 2; |
| } |
| |
| bool SkOpAngle::oppositePlanes(const SkOpAngle& rh) const { |
| int startSpan = abs(rh.fSectorStart - fSectorStart); |
| return startSpan >= 8; |
| } |
| |
| bool SkOpAngle::orderable(const SkOpAngle& rh) const { |
| int result; |
| if (!fIsCurve) { |
| if (!rh.fIsCurve) { |
| double leftX = fTangentHalf.dx(); |
| double leftY = fTangentHalf.dy(); |
| double rightX = rh.fTangentHalf.dx(); |
| double rightY = rh.fTangentHalf.dy(); |
| double x_ry = leftX * rightY; |
| double rx_y = rightX * leftY; |
| if (x_ry == rx_y) { |
| if (leftX * rightX < 0 || leftY * rightY < 0) { |
| return true; // exactly 180 degrees apart |
| } |
| goto unorderable; |
| } |
| SkASSERT(x_ry != rx_y); // indicates an undetected coincidence -- worth finding earlier |
| return x_ry < rx_y; |
| } |
| if ((result = allOnOneSide(rh)) >= 0) { |
| return result; |
| } |
| if (fUnorderable || approximately_zero(rh.fSide)) { |
| goto unorderable; |
| } |
| } else if (!rh.fIsCurve) { |
| if ((result = rh.allOnOneSide(*this)) >= 0) { |
| return !result; |
| } |
| if (rh.fUnorderable || approximately_zero(fSide)) { |
| goto unorderable; |
| } |
| } |
| if ((result = convexHullOverlaps(rh)) >= 0) { |
| return result; |
| } |
| return endsIntersect(rh); |
| unorderable: |
| fUnorderable = true; |
| rh.fUnorderable = true; |
| return true; |
| } |
| |
| bool SkOpAngle::overlap(const SkOpAngle& other) const { |
| int min = SkTMin(fStart, fEnd); |
| const SkOpSpan& span = fSegment->span(min); |
| const SkOpSegment* oSeg = other.fSegment; |
| int oMin = SkTMin(other.fStart, other.fEnd); |
| const SkOpSpan& oSpan = oSeg->span(oMin); |
| if (!span.fSmall && !oSpan.fSmall) { |
| return false; |
| } |
| if (fSegment->span(fStart).fPt != oSeg->span(other.fStart).fPt) { |
| return false; |
| } |
| // see if small span is contained by opposite span |
| return span.fSmall ? oSeg->containsPt(fSegment->span(fEnd).fPt, other.fEnd, other.fStart) |
| : fSegment->containsPt(oSeg->span(other.fEnd).fPt, fEnd, fStart); |
| } |
| |
| // OPTIMIZE: if this shows up in a profile, add a previous pointer |
| // as is, this should be rarely called |
| SkOpAngle* SkOpAngle::previous() const { |
| SkOpAngle* last = fNext; |
| do { |
| SkOpAngle* next = last->fNext; |
| if (next == this) { |
| return last; |
| } |
| last = next; |
| } while (true); |
| } |
| |
| void SkOpAngle::set(const SkOpSegment* segment, int start, int end) { |
| fSegment = segment; |
| fStart = start; |
| fComputedEnd = fEnd = end; |
| fNext = NULL; |
| fComputeSector = fComputedSector = false; |
| fStop = false; |
| setSpans(); |
| setSector(); |
| } |
| |
| void SkOpAngle::setCurveHullSweep() { |
| fUnorderedSweep = false; |
| fSweep[0] = fCurvePart[1] - fCurvePart[0]; |
| if (SkPath::kLine_Verb == fSegment->verb()) { |
| fSweep[1] = fSweep[0]; |
| return; |
| } |
| fSweep[1] = fCurvePart[2] - fCurvePart[0]; |
| if (SkPath::kCubic_Verb != fSegment->verb()) { |
| if (!fSweep[0].fX && !fSweep[0].fY) { |
| fSweep[0] = fSweep[1]; |
| } |
| return; |
| } |
| SkDVector thirdSweep = fCurvePart[3] - fCurvePart[0]; |
| if (fSweep[0].fX == 0 && fSweep[0].fY == 0) { |
| fSweep[0] = fSweep[1]; |
| fSweep[1] = thirdSweep; |
| if (fSweep[0].fX == 0 && fSweep[0].fY == 0) { |
| fSweep[0] = fSweep[1]; |
| fCurvePart[1] = fCurvePart[3]; |
| fIsCurve = false; |
| } |
| return; |
| } |
| double s1x3 = fSweep[0].crossCheck(thirdSweep); |
| double s3x2 = thirdSweep.crossCheck(fSweep[1]); |
| if (s1x3 * s3x2 >= 0) { // if third vector is on or between first two vectors |
| return; |
| } |
| double s2x1 = fSweep[1].crossCheck(fSweep[0]); |
| // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble |
| // probably such wide sweeps should be artificially subdivided earlier so that never happens |
| SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0); |
| if (s3x2 * s2x1 < 0) { |
| SkASSERT(s2x1 * s1x3 > 0); |
| fSweep[0] = fSweep[1]; |
| fUnorderedSweep = true; |
| } |
| fSweep[1] = thirdSweep; |
| } |
| |
| void SkOpAngle::setSector() { |
| SkPath::Verb verb = fSegment->verb(); |
| if (SkPath::kLine_Verb != verb && small()) { |
| goto deferTilLater; |
| } |
| fSectorStart = findSector(verb, fSweep[0].fX, fSweep[0].fY); |
| if (fSectorStart < 0) { |
| goto deferTilLater; |
| } |
| if (!fIsCurve) { // if it's a line or line-like, note that both sectors are the same |
| SkASSERT(fSectorStart >= 0); |
| fSectorEnd = fSectorStart; |
| fSectorMask = 1 << fSectorStart; |
| return; |
| } |
| SkASSERT(SkPath::kLine_Verb != verb); |
| fSectorEnd = findSector(verb, fSweep[1].fX, fSweep[1].fY); |
| if (fSectorEnd < 0) { |
| deferTilLater: |
| fSectorStart = fSectorEnd = -1; |
| fSectorMask = 0; |
| fComputeSector = true; // can't determine sector until segment length can be found |
| return; |
| } |
| if (fSectorEnd == fSectorStart) { |
| SkASSERT((fSectorStart & 3) != 3); // if the sector has no span, it can't be an exact angle |
| fSectorMask = 1 << fSectorStart; |
| return; |
| } |
| bool crossesZero = checkCrossesZero(); |
| int start = SkTMin(fSectorStart, fSectorEnd); |
| bool curveBendsCCW = (fSectorStart == start) ^ crossesZero; |
| // bump the start and end of the sector span if they are on exact compass points |
| if ((fSectorStart & 3) == 3) { |
| fSectorStart = (fSectorStart + (curveBendsCCW ? 1 : 31)) & 0x1f; |
| } |
| if ((fSectorEnd & 3) == 3) { |
| fSectorEnd = (fSectorEnd + (curveBendsCCW ? 31 : 1)) & 0x1f; |
| } |
| crossesZero = checkCrossesZero(); |
| start = SkTMin(fSectorStart, fSectorEnd); |
| int end = SkTMax(fSectorStart, fSectorEnd); |
| if (!crossesZero) { |
| fSectorMask = (unsigned) -1 >> (31 - end + start) << start; |
| } else { |
| fSectorMask = (unsigned) -1 >> (31 - start) | (-1 << end); |
| } |
| } |
| |
| void SkOpAngle::setSpans() { |
| fUnorderable = fSegment->isTiny(this); |
| fLastMarked = NULL; |
| const SkPoint* pts = fSegment->pts(); |
| SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY |
| = SK_ScalarNaN); |
| fSegment->subDivide(fStart, fEnd, &fCurvePart); |
| setCurveHullSweep(); |
| const SkPath::Verb verb = fSegment->verb(); |
| if (verb != SkPath::kLine_Verb |
| && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) { |
| SkDLine lineHalf; |
| lineHalf[0].set(fCurvePart[0].asSkPoint()); |
| lineHalf[1].set(fCurvePart[SkPathOpsVerbToPoints(verb)].asSkPoint()); |
| fTangentHalf.lineEndPoints(lineHalf); |
| fSide = 0; |
| } |
| switch (verb) { |
| case SkPath::kLine_Verb: { |
| SkASSERT(fStart != fEnd); |
| const SkPoint& cP1 = pts[fStart < fEnd]; |
| SkDLine lineHalf; |
| lineHalf[0].set(fSegment->span(fStart).fPt); |
| lineHalf[1].set(cP1); |
| fTangentHalf.lineEndPoints(lineHalf); |
| fSide = 0; |
| fIsCurve = false; |
| } return; |
| case SkPath::kQuad_Verb: { |
| SkLineParameters tangentPart; |
| SkDQuad& quad2 = *SkTCast<SkDQuad*>(&fCurvePart); |
| (void) tangentPart.quadEndPoints(quad2); |
| fSide = -tangentPart.pointDistance(fCurvePart[2]); // not normalized -- compare sign only |
| } break; |
| case SkPath::kCubic_Verb: { |
| SkLineParameters tangentPart; |
| (void) tangentPart.cubicPart(fCurvePart); |
| fSide = -tangentPart.pointDistance(fCurvePart[3]); |
| double testTs[4]; |
| // OPTIMIZATION: keep inflections precomputed with cubic segment? |
| int testCount = SkDCubic::FindInflections(pts, testTs); |
| double startT = fSegment->t(fStart); |
| double endT = fSegment->t(fEnd); |
| double limitT = endT; |
| int index; |
| for (index = 0; index < testCount; ++index) { |
| if (!::between(startT, testTs[index], limitT)) { |
| testTs[index] = -1; |
| } |
| } |
| testTs[testCount++] = startT; |
| testTs[testCount++] = endT; |
| SkTQSort<double>(testTs, &testTs[testCount - 1]); |
| double bestSide = 0; |
| int testCases = (testCount << 1) - 1; |
| index = 0; |
| while (testTs[index] < 0) { |
| ++index; |
| } |
| index <<= 1; |
| for (; index < testCases; ++index) { |
| int testIndex = index >> 1; |
| double testT = testTs[testIndex]; |
| if (index & 1) { |
| testT = (testT + testTs[testIndex + 1]) / 2; |
| } |
| // OPTIMIZE: could avoid call for t == startT, endT |
| SkDPoint pt = dcubic_xy_at_t(pts, testT); |
| SkLineParameters tangentPart; |
| tangentPart.cubicEndPoints(fCurvePart); |
| double testSide = tangentPart.pointDistance(pt); |
| if (fabs(bestSide) < fabs(testSide)) { |
| bestSide = testSide; |
| } |
| } |
| fSide = -bestSide; // compare sign only |
| } break; |
| default: |
| SkASSERT(0); |
| } |
| } |
| |
| bool SkOpAngle::small() const { |
| int min = SkMin32(fStart, fEnd); |
| int max = SkMax32(fStart, fEnd); |
| for (int index = min; index < max; ++index) { |
| const SkOpSpan& mSpan = fSegment->span(index); |
| if (!mSpan.fSmall) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool SkOpAngle::tangentsDiverge(const SkOpAngle& rh, double s0xt0) const { |
| if (s0xt0 == 0) { |
| return false; |
| } |
| // if the ctrl tangents are not nearly parallel, use them |
| // solve for opposite direction displacement scale factor == m |
| // initial dir = v1.cross(v2) == v2.x * v1.y - v2.y * v1.x |
| // displacement of q1[1] : dq1 = { -m * v1.y, m * v1.x } + q1[1] |
| // straight angle when : v2.x * (dq1.y - q1[0].y) == v2.y * (dq1.x - q1[0].x) |
| // v2.x * (m * v1.x + v1.y) == v2.y * (-m * v1.y + v1.x) |
| // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x |
| // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y) |
| // m = v1.cross(v2) / v1.dot(v2) |
| const SkDVector* sweep = fSweep; |
| const SkDVector* tweep = rh.fSweep; |
| double s0dt0 = sweep[0].dot(tweep[0]); |
| if (!s0dt0) { |
| return true; |
| } |
| SkASSERT(s0dt0 != 0); |
| double m = s0xt0 / s0dt0; |
| double sDist = sweep[0].length() * m; |
| double tDist = tweep[0].length() * m; |
| bool useS = fabs(sDist) < fabs(tDist); |
| double mFactor = fabs(useS ? distEndRatio(sDist) : rh.distEndRatio(tDist)); |
| return mFactor < 5000; // empirically found limit |
| } |
| |
| SkOpAngleSet::SkOpAngleSet() |
| : fAngles(NULL) |
| #if DEBUG_ANGLE |
| , fCount(0) |
| #endif |
| { |
| } |
| |
| SkOpAngleSet::~SkOpAngleSet() { |
| SkDELETE(fAngles); |
| } |
| |
| SkOpAngle& SkOpAngleSet::push_back() { |
| if (!fAngles) { |
| fAngles = SkNEW_ARGS(SkChunkAlloc, (2)); |
| } |
| void* ptr = fAngles->allocThrow(sizeof(SkOpAngle)); |
| SkOpAngle* angle = (SkOpAngle*) ptr; |
| #if DEBUG_ANGLE |
| angle->setID(++fCount); |
| #endif |
| return *angle; |
| } |
| |
| void SkOpAngleSet::reset() { |
| if (fAngles) { |
| fAngles->reset(); |
| } |
| } |