void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) { SkTArray<double, true> ts; toQuadraticTs(&cubic, precision, &ts); if (ts.count() <= 0) { SkDQuad quad = cubic.toQuad(); quads.push_back(quad); return; } double tStart = 0; for (int i1 = 0; i1 <= ts.count(); ++i1) { const double tEnd = i1 < ts.count() ? ts[i1] : 1; SkDRect bounds; bounds.setBounds(cubic); SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); if (quad[1].fX < bounds.fLeft) { quad[1].fX = bounds.fLeft; } else if (quad[1].fX > bounds.fRight) { quad[1].fX = bounds.fRight; } if (quad[1].fY < bounds.fTop) { quad[1].fY = bounds.fTop; } else if (quad[1].fY > bounds.fBottom) { quad[1].fY = bounds.fBottom; } quads.push_back(quad); tStart = tEnd; } }
// if two ends intersect, check middle for coincidence bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) { if (fUsed < 2) { return false; } int last = fUsed - 1; double tRange1 = fT[0][last] - fT[0][0]; double tRange2 = fT[1][last] - fT[1][0]; for (int index = 1; index < 5; ++index) { double testT1 = fT[0][0] + tRange1 * index / 5; double testT2 = fT[1][0] + tRange2 * index / 5; SkDPoint testPt1 = c1.ptAtT(testT1); SkDPoint testPt2 = c2.ptAtT(testT2); if (!testPt1.approximatelyEqual(testPt2)) { return false; } } if (fUsed > 2) { fPt[1] = fPt[last]; fT[0][1] = fT[0][last]; fT[1][1] = fT[1][last]; fUsed = 2; } fIsCoincident[0] = fIsCoincident[1] = 0x03; return true; }
bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const { if (fVerb != SkPath::kCubic_Verb) { return false; } SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); return dst.controlsContainedByEnds(); }
// see parallel routine in line quadratic intersections int intersectRay(double roots[3]) { double adj = fLine[1].fX - fLine[0].fX; double opp = fLine[1].fY - fLine[0].fY; SkDCubic c; SkDEBUGCODE(c.fDebugGlobalState = fIntersections->globalState()); for (int n = 0; n < 4; ++n) { c[n].fX = (fCubic[n].fY - fLine[0].fY) * adj - (fCubic[n].fX - fLine[0].fX) * opp; } double A, B, C, D; SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D); int count = SkDCubic::RootsValidT(A, B, C, D, roots); for (int index = 0; index < count; ++index) { SkDPoint calcPt = c.ptAtT(roots[index]); if (!approximately_zero(calcPt.fX)) { for (int n = 0; n < 4; ++n) { c[n].fY = (fCubic[n].fY - fLine[0].fY) * opp + (fCubic[n].fX - fLine[0].fX) * adj; } double extremeTs[6]; int extrema = SkDCubic::FindExtrema(&c[0].fX, extremeTs); count = c.searchRoots(extremeTs, extrema, 0, SkDCubic::kXAxis, roots); break; } } return count; }
static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) { SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an // extremely shallow quadratic? int order = reducer->reduce(quad, SkReduceOrder::kFill_Style); #if DEBUG_QUAD_PART SkDebugf("%s cubic=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g)" " t=(%1.17g,%1.17g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, tStart, tEnd); SkDebugf("%s part=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g)" " quad=(%1.17g,%1.17g %1.17g,%1.17g %1.17g,%1.17g)\n", __FUNCTION__, part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY, part[3].fX, part[3].fY, quad[0].fX, quad[0].fY, quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY); SkDebugf("%s simple=(%1.17g,%1.17g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY); if (order > 1) { SkDebugf(" %1.17g,%1.17g", reducer->fQuad[1].fX, reducer->fQuad[1].fY); } if (order > 2) { SkDebugf(" %1.17g,%1.17g", reducer->fQuad[2].fX, reducer->fQuad[2].fY); } SkDebugf(")\n"); SkASSERT(order < 4 && order > 0); #endif return order; }
int SkOpSegment::debugInflections(int tStart, int tEnd) const { if (fVerb != SkPath::kCubic_Verb) { return false; } SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT); double inflections[2]; return dst.findInflections(inflections); }
DEF_TEST(PathOpsCubicHull, reporter) { for (size_t index = 0; index < hullTests_count; ++index) { const CubicPts& c = hullTests[index]; SkDCubic cubic; cubic.debugSet(c.fPts); char order[4]; cubic.convexHull(order); } }
static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2, bool coin) { SkASSERT(ValidCubic(cubic1)); SkASSERT(ValidCubic(cubic2)); #if ONE_OFF_DEBUG SkDebugf("computed quadratics given\n"); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY, cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY, cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY); #endif SkTArray<SkDQuad, true> quads1; CubicToQuads(cubic1, cubic1.calcPrecision(), quads1); #if ONE_OFF_DEBUG SkDebugf("computed quadratics set 1\n"); for (int index = 0; index < quads1.count(); ++index) { const SkDQuad& q = quads1[index]; SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); } #endif SkTArray<SkDQuad, true> quads2; CubicToQuads(cubic2, cubic2.calcPrecision(), quads2); #if ONE_OFF_DEBUG SkDebugf("computed quadratics set 2\n"); for (int index = 0; index < quads2.count(); ++index) { const SkDQuad& q = quads2[index]; SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); } #endif SkIntersections intersections; intersections.intersect(cubic1, cubic2); REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; xy1 = cubic1.ptAtT(tt1); tt2 = intersections[1][pt3]; xy2 = cubic2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX, iPt.fY, xy2.fX, xy2.fY, tt2); #endif REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) { SkDCubic cubic; cubic.set(a); SkReduceOrder reducer; int order = reducer.reduce(cubic, kAllow_Quadratics); if (order == 2 || order == 3) { // cubic became line or quad for (int index = 0; index < order; ++index) { *reducePts++ = reducer.fQuad[index].asSkPoint(); } } return SkPathOpsPointsToVerb(order - 1); }
bool SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) { SkScalar d[3]; SkCubicType cubicType = SkClassifyCubic(pointsPtr, d); if (cubicType == kLoop_SkCubicType) { // crib code from gpu path utils that finds t values where loop self-intersects // use it to find mid of t values which should be a friendly place to chop SkScalar tempSqrt = SkScalarSqrt(4.f * d[0] * d[2] - 3.f * d[1] * d[1]); SkScalar ls = d[1] - tempSqrt; SkScalar lt = 2.f * d[0]; SkScalar ms = d[1] + tempSqrt; SkScalar mt = 2.f * d[0]; if (roughly_between(0, ls, lt) && roughly_between(0, ms, mt)) { ls = ls / lt; ms = ms / mt; SkASSERT(roughly_between(0, ls, 1) && roughly_between(0, ms, 1)); *t = (ls + ms) / 2; SkASSERT(roughly_between(0, *t, 1)); return *t > 0 && *t < 1; } } else if (kSerpentine_SkCubicType == cubicType || kCusp_SkCubicType == cubicType) { SkDCubic cubic; cubic.set(pointsPtr); double inflectionTs[2]; int infTCount = cubic.findInflections(inflectionTs); if (infTCount == 2) { double maxCurvature[3]; int roots = cubic.findMaxCurvature(maxCurvature); #if DEBUG_CUBIC_SPLIT SkDebugf("%s\n", __FUNCTION__); cubic.dump(); for (int index = 0; index < infTCount; ++index) { SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]); SkDPoint pt = cubic.ptAtT(inflectionTs[index]); SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]); SkDLine perp = {{pt - dPt, pt + dPt}}; perp.dump(); } for (int index = 0; index < roots; ++index) { SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]); SkDPoint pt = cubic.ptAtT(maxCurvature[index]); SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]); SkDLine perp = {{pt - dPt, pt + dPt}}; perp.dump(); } #endif for (int index = 0; index < roots; ++index) { if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) { *t = maxCurvature[index]; return *t > 0 && *t < 1; } } } else if (infTCount == 1) { *t = inflectionTs[0]; return *t > 0 && *t < 1; } } return false; }
void SkGlyphCache::AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis, SkGlyph::Intercept* intercept) { SkDCubic cubic; cubic.set(pts); double roots[3]; int count = yAxis ? cubic.verticalIntersect(axis, roots) : cubic.horizontalIntersect(axis, roots); while (--count >= 0) { SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint(); AddInterval(*(&pt.fX + yAxis), intercept); } }
static void test(skiatest::Reporter* reporter, const SkDQuad* quadTests, const char* name, int firstTest, size_t testCount) { for (size_t index = firstTest; index < testCount; ++index) { const SkDQuad& quad = quadTests[index]; SkDCubic cubic = quad.toCubic(); double precision = cubic.calcPrecision(); SkTDArray<SkDQuad> quads; CubicToQuads(cubic, precision, quads); if (quads.count() != 1) { SkDebugf("%s [%d] cubic to quadratics failed count=%d\n", name, static_cast<int>(index), quads.count()); } REPORTER_ASSERT(reporter, quads.count() == 1); } }
static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) { double A, B, C, D; SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D); D -= axisIntercept; int count = SkDCubic::RootsValidT(A, B, C, D, roots); for (int index = 0; index < count; ++index) { SkDPoint calcPt = c.ptAtT(roots[index]); if (!approximately_equal(calcPt.fX, axisIntercept)) { double extremeTs[6]; int extrema = SkDCubic::FindExtrema(&c[0].fX, extremeTs); count = c.searchRoots(extremeTs, extrema, axisIntercept, SkDCubic::kXAxis, roots); break; } } return count; }
static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) { if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) { return false; } pt = cubic.xyAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2); return true; }
void CubicPathToSimple(const SkPath& cubicPath, SkPath* simplePath) { simplePath->reset(); SkDCubic cubic; SkPath::RawIter iter(cubicPath); uint8_t verb; SkPoint pts[4]; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: simplePath->moveTo(pts[0].fX, pts[0].fY); continue; case SkPath::kLine_Verb: simplePath->lineTo(pts[1].fX, pts[1].fY); break; case SkPath::kQuad_Verb: simplePath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); break; case SkPath::kCubic_Verb: { cubic.set(pts); double tInflects[2]; int inflections = cubic.findInflections(tInflects); if (inflections > 1 && tInflects[0] > tInflects[1]) { SkTSwap(tInflects[0], tInflects[1]); } double lo = 0; for (int index = 0; index <= inflections; ++index) { double hi = index < inflections ? tInflects[index] : 1; SkDCubic part = cubic.subDivide(lo, hi); SkPoint cPts[3]; cPts[0] = part[1].asSkPoint(); cPts[1] = part[2].asSkPoint(); cPts[2] = part[3].asSkPoint(); simplePath->cubicTo(cPts[0].fX, cPts[0].fY, cPts[1].fX, cPts[1].fY, cPts[2].fX, cPts[2].fY); lo = hi; } break; } case SkPath::kClose_Verb: simplePath->close(); break; default: SkDEBUGFAIL("bad verb"); return; } } }
void CubicToQuads(const SkDCubic& cubic, double precision, SkTArray<SkDQuad, true>& quads) { SkTArray<double, true> ts; toQuadraticTs(&cubic, precision, &ts); if (ts.count() <= 0) { SkDQuad quad = cubic.toQuad(); quads.push_back(quad); return; } double tStart = 0; for (int i1 = 0; i1 <= ts.count(); ++i1) { const double tEnd = i1 < ts.count() ? ts[i1] : 1; SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); quads.push_back(quad); tStart = tEnd; } }
SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkPoint* reducePts) { if (SkDPoint::ApproximatelyEqual(a[0], a[1]) && SkDPoint::ApproximatelyEqual(a[0], a[2]) && SkDPoint::ApproximatelyEqual(a[0], a[3])) { reducePts[0] = a[0]; return SkPath::kMove_Verb; } SkDCubic cubic; cubic.set(a); SkReduceOrder reducer; int order = reducer.reduce(cubic, kAllow_Quadratics); if (order == 2 || order == 3) { // cubic became line or quad for (int index = 0; index < order; ++index) { *reducePts++ = reducer.fQuad[index].asSkPoint(); } } return SkPathOpsPointsToVerb(order - 1); }
static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2, bool coin) { SkASSERT(ValidCubic(cubic1)); SkASSERT(ValidCubic(cubic2)); #if ONE_OFF_DEBUG SkDebugf("computed quadratics given\n"); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY, cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY, cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY); #endif SkIntersections intersections; intersections.intersect(cubic1, cubic2); #if DEBUG_T_SECT_DUMP == 3 SkDebugf("</div>\n\n"); SkDebugf("<script type=\"text/javascript\">\n\n"); SkDebugf("var testDivs = [\n"); for (int index = 1; index <= gDumpTSectNum; ++index) { SkDebugf("sect%d,\n", index); } #endif if (coin && intersections.used() != 2) { SkDebugf(""); } REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; xy1 = cubic1.ptAtT(tt1); tt2 = intersections[1][pt3]; xy2 = cubic2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX, iPt.fY, xy2.fX, xy2.fY, tt2); #endif REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) { int last = i.used() - 1; if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) { return false; } pt = cubic.xyAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2); return true; }
static int check_linear(const SkDCubic& cubic, int minX, int maxX, int minY, int maxY, SkDCubic& reduction) { if (!cubic.isLinear(0, 3)) { return 0; } // four are colinear: return line formed by outside reduction[0] = cubic[0]; reduction[1] = cubic[3]; return reductionLineCount(reduction); }
SkDPoint SkDCubic::top(double startT, double endT) const { SkDCubic sub = subDivide(startT, endT); SkDPoint topPt = sub[0]; if (topPt.fY > sub[3].fY || (topPt.fY == sub[3].fY && topPt.fX > sub[3].fX)) { topPt = sub[3]; } double extremeTs[2]; if (!sub.monotonicInY()) { int roots = FindExtrema(sub[0].fY, sub[1].fY, sub[2].fY, sub[3].fY, extremeTs); for (int index = 0; index < roots; ++index) { double t = startT + (endT - startT) * extremeTs[index]; SkDPoint mid = ptAtT(t); if (topPt.fY > mid.fY || (topPt.fY == mid.fY && topPt.fX > mid.fX)) { topPt = mid; } } } return topPt; }
void SkIntersections::cubicInsert(double one, double two, const SkDPoint& pt, const SkDCubic& cubic1, const SkDCubic& cubic2) { for (int index = 0; index < fUsed; ++index) { if (fT[0][index] == one) { double oldTwo = fT[1][index]; if (oldTwo == two) { return; } SkDPoint mid = cubic2.ptAtT((oldTwo + two) / 2); if (mid.approximatelyEqual(fPt[index])) { return; } } if (fT[1][index] == two) { SkDPoint mid = cubic1.ptAtT((fT[0][index] + two) / 2); if (mid.approximatelyEqual(fPt[index])) { return; } } } insert(one, two, pt); }
void CubicPathToQuads(const SkPath& cubicPath, SkPath* quadPath) { quadPath->reset(); SkDCubic cubic; SkTArray<SkDQuad, true> quads; SkPath::RawIter iter(cubicPath); uint8_t verb; SkPoint pts[4]; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: quadPath->moveTo(pts[0].fX, pts[0].fY); continue; case SkPath::kLine_Verb: quadPath->lineTo(pts[1].fX, pts[1].fY); break; case SkPath::kQuad_Verb: quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); break; case SkPath::kCubic_Verb: quads.reset(); cubic.set(pts); CubicToQuads(cubic, cubic.calcPrecision(), quads); for (int index = 0; index < quads.count(); ++index) { SkPoint qPts[2] = { quads[index][1].asSkPoint(), quads[index][2].asSkPoint() }; quadPath->quadTo(qPts[0].fX, qPts[0].fY, qPts[1].fX, qPts[1].fY); } break; case SkPath::kClose_Verb: quadPath->close(); break; default: SkDEBUGFAIL("bad verb"); return; } } }
double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const { double extremeTs[2]; double topT = -1; int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs); for (int index = 0; index < roots; ++index) { double t = startT + (endT - startT) * extremeTs[index]; SkDPoint mid = dCurve.ptAtT(t); if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) { topT = t; *topPt = mid; } } return topT; }
int SkIntersections::intersect(const SkDCubic& c) { // check to see if x or y end points are the extrema. Are other quick rejects possible? if (c.endsAreExtremaInXOrY()) { return false; } (void) intersect(c, c); if (used() > 0) { SkASSERT(used() == 1); if (fT[0][0] > fT[1][0]) { swapPts(); } } return used(); }
void SkDRect::setBounds(const SkDCubic& c) { set(c[0]); add(c[3]); double tValues[4]; int roots = 0; if (!is_bounded_by_end_points(c[0].fX, c[1].fX, c[2].fX, c[3].fX)) { roots = SkDCubic::FindExtrema(c[0].fX, c[1].fX, c[2].fX, c[3].fX, tValues); } if (!is_bounded_by_end_points(c[0].fY, c[1].fY, c[2].fY, c[3].fY)) { roots += SkDCubic::FindExtrema(c[0].fY, c[1].fY, c[2].fY, c[3].fY, &tValues[roots]); } for (int x = 0; x < roots; ++x) { add(c.ptAtT(tValues[x])); } }
static int check_linear(const SkDCubic& cubic, int minX, int maxX, int minY, int maxY, SkDCubic& reduction) { int startIndex = 0; int endIndex = 3; while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) { --endIndex; if (endIndex == 0) { endIndex = 3; break; } } if (!cubic.isLinear(startIndex, endIndex)) { return 0; } // four are colinear: return line formed by outside reduction[0] = cubic[0]; reduction[1] = cubic[3]; return reductionLineCount(reduction); }
static double calc_t_div(const SkDCubic& cubic, double precision, double start) { const double adjust = sqrt(3.) / 36; SkDCubic sub; const SkDCubic* cPtr; if (start == 0) { cPtr = &cubic; } else { // OPTIMIZE: special-case half-split ? sub = cubic.subDivide(start, 1); cPtr = ⊂ } const SkDCubic& c = *cPtr; double dx = c[3].fX - 3 * (c[2].fX - c[1].fX) - c[0].fX; double dy = c[3].fY - 3 * (c[2].fY - c[1].fY) - c[0].fY; double dist = sqrt(dx * dx + dy * dy); double tDiv3 = precision / (adjust * dist); double t = SkDCubeRoot(tDiv3); if (start > 0) { t = start + (1 - start) * t; } return t; }
bool SkDConic::hullIntersects(const SkDCubic& cubic, bool* isLinear) const { return cubic.hullIntersects(*this, isLinear); }
// determine that slop required after quad/quad finds a candidate intersection // use the cross of the tangents plus the distance from 1 or 0 as knobs DEF_TEST(PathOpsCubicQuadSlop, reporter) { // create a random non-selfintersecting cubic // break it into quadratics // offset the quadratic, measuring the slop required to find the intersection if (!gPathOpCubicQuadSlopVerbose) { // takes a while to run -- so exclude it by default return; } int results[101]; sk_bzero(results, sizeof(results)); double minCross[101]; sk_bzero(minCross, sizeof(minCross)); double maxCross[101]; sk_bzero(maxCross, sizeof(maxCross)); double sumCross[101]; sk_bzero(sumCross, sizeof(sumCross)); int foundOne = 0; int slopCount = 1; SkRandom ran; for (int index = 0; index < 10000000; ++index) { if (index % 1000 == 999) SkDebugf("."); SkDCubic cubic = {{ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)} }}; SkIntersections i; if (i.intersect(cubic)) { continue; } SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts; cubic.toQuadraticTs(cubic.calcPrecision(), &ts); double tStart = 0; int tsCount = ts.count(); for (int i1 = 0; i1 <= tsCount; ++i1) { const double tEnd = i1 < tsCount ? ts[i1] : 1; SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); SkReduceOrder reducer; int order = reducer.reduce(quad); if (order != 3) { continue; } for (int i2 = 0; i2 < 100; ++i2) { SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)}; SkDQuad nearby = {{ {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY}, {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)}, {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY} }}; order = reducer.reduce(nearby); if (order != 3) { continue; } SkIntersections locals; locals.allowNear(false); locals.intersect(quad, nearby); if (locals.used() != 1) { continue; } // brute force find actual intersection SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }}; SkIntersections liner; int i3; int found = -1; int foundErr = true; for (i3 = 1; i3 <= 1000; ++i3) { cubicLine[0] = cubicLine[1]; cubicLine[1] = cubic.ptAtT(i3 / 1000.); liner.reset(); liner.allowNear(false); liner.intersect(nearby, cubicLine); if (liner.used() == 0) { continue; } if (liner.used() > 1) { foundErr = true; break; } if (found > 0) { foundErr = true; break; } foundErr = false; found = i3; } if (foundErr) { continue; } SkDVector dist = liner.pt(0) - locals.pt(0); SkDVector qV = nearby.dxdyAtT(locals[0][0]); double cubicT = (found - 1 + liner[1][0]) / 1000.; SkDVector cV = cubic.dxdyAtT(cubicT); double qxc = qV.crossCheck(cV); double qvLen = qV.length(); double cvLen = cV.length(); double maxLen = SkTMax(qvLen, cvLen); qxc /= maxLen; double quadT = tStart + (tEnd - tStart) * locals[0][0]; double diffT = fabs(cubicT - quadT); int diffIdx = (int) (diffT * 100); results[diffIdx]++; double absQxc = fabs(qxc); if (sumCross[diffIdx] == 0) { minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc; } else { minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc); maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc); sumCross[diffIdx] += absQxc; } if (diffIdx >= 20) { #if 01 SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, nearby[2].fX, nearby[2].fY, liner.pt(0).fX, liner.pt(0).fY, locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc); #else SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", quadT, cubicT, dist.length(), qxc); SkDebugf("<div id=\"slop%d\">\n", ++slopCount); SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, nearby[2].fX, nearby[2].fY, liner.pt(0).fX, liner.pt(0).fY, locals.pt(0).fX, locals.pt(0).fY); SkDebugf("</div>\n\n"); #endif } ++foundOne; } tStart = tEnd; } if (++foundOne >= 100000) { break; } } #if 01 SkDebugf("slopCount=%d\n", slopCount); int max = 100; while (results[max] == 0) { --max; } for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%d ", results[i]); } SkDebugf("min\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", minCross[i]); } SkDebugf("max\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", maxCross[i]); } SkDebugf("avg\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", sumCross[i] / results[i]); } #else for (int i = 1; i < slopCount; ++i) { SkDebugf(" slop%d,\n", i); } #endif SkDebugf("\n"); }