// 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; }
/*( for quads and cubics, set up a parameterized line (e.g. LineParameters ) for points [0] to [1]. See if point [2] is on that line, or on one side or the other. If it both quads' end points are on the same side, choose the shorter tangent. If the tangents are equal, choose the better second tangent angle maybe I could set up LineParameters lazily */ bool SkOpAngle::operator<(const SkOpAngle& rh) const { double y = dy(); double ry = rh.dy(); if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? return y < 0; } double x = dx(); double rx = rh.dx(); if (y == 0 && ry == 0 && x * rx < 0) { return x < rx; } double x_ry = x * ry; double rx_y = rx * y; double cmp = x_ry - rx_y; if (!approximately_zero(cmp)) { return cmp < 0; } if (approximately_zero(x_ry) && approximately_zero(rx_y) && !approximately_zero_squared(cmp)) { return cmp < 0; } // at this point, the initial tangent line is coincident // see if edges curl away from each other if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) { // FIXME: running demo will trigger this assertion // (don't know if commenting out will trigger further assertion or not) // commenting it out allows demo to run in release, though return fSide < rh.fSide; } // see if either curve can be lengthened and try the tangent compare again if (/* cmp && */ (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting SkOpAngle longer = *this; SkOpAngle rhLonger = rh; if (longer.lengthen() | rhLonger.lengthen()) { return longer < rhLonger; } } if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_zero(y)) || (rh.fVerb == SkPath::kLine_Verb && approximately_zero(rx) && approximately_zero(ry))) { // See general unsortable comment below. This case can happen when // one line has a non-zero change in t but no change in x and y. fUnsortable = true; rh.fUnsortable = true; return this < &rh; // even with no solution, return a stable sort } if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { fUnsortable = true; rh.fUnsortable = true; return this < &rh; // even with no solution, return a stable sort } SkASSERT(fVerb >= SkPath::kQuad_Verb); SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); // FIXME: until I can think of something better, project a ray from the // end of the shorter tangent to midway between the end points // through both curves and use the resulting angle to sort // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive double len = fTangent1.normalSquared(); double rlen = rh.fTangent1.normalSquared(); SkDLine ray; SkIntersections i, ri; int roots, rroots; bool flip = false; do { bool useThis = (len < rlen) ^ flip; const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ? part[2] : part[1]; ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; SkASSERT(ray[0] != ray[1]); roots = (i.*CurveRay[fVerb])(fPts, ray); rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray); } while ((roots == 0 || rroots == 0) && (flip ^= true)); if (roots == 0 || rroots == 0) { // FIXME: we don't have a solution in this case. The interim solution // is to mark the edges as unsortable, exclude them from this and // future computations, and allow the returned path to be fragmented fUnsortable = true; rh.fUnsortable = true; return this < &rh; // even with no solution, return a stable sort } SkDPoint loc; double best = SK_ScalarInfinity; SkDVector dxy; double dist; int index; for (index = 0; index < roots; ++index) { loc = (*CurveDPointAtT[fVerb])(fPts, i[0][index]); dxy = loc - ray[0]; dist = dxy.lengthSquared(); if (best > dist) { best = dist; } } for (index = 0; index < rroots; ++index) { loc = (*CurveDPointAtT[rh.fVerb])(rh.fPts, ri[0][index]); dxy = loc - ray[0]; dist = dxy.lengthSquared(); if (best > dist) { return fSide < 0; } } return fSide > 0; }
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; (*CurveIntersectRay[index ? rPts : lPts])(segment.pts(), rays[index], &i); // 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); } }