static void tesselate(const SkPath& src, SkPath* dst) { SkPath::Iter iter(src, true); SkPoint pts[4]; SkPath::Verb verb; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: dst->moveTo(pts[0]); break; case SkPath::kLine_Verb: dst->lineTo(pts[1]); break; case SkPath::kQuad_Verb: { SkPoint p; for (int i = 1; i <= 8; ++i) { SkEvalQuadAt(pts, i / 8.0f, &p, NULL); dst->lineTo(p); } } break; case SkPath::kCubic_Verb: { SkPoint p; for (int i = 1; i <= 8; ++i) { SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL); dst->lineTo(p); } } break; } } }
/// Used inside SkCurveMeasure::getTime's Newton's iteration static inline SkPoint evaluate(const SkPoint pts[4], SkSegType segType, SkScalar t) { SkPoint pos; switch (segType) { case kQuad_SegType: pos = SkEvalQuadAt(pts, t); break; case kLine_SegType: pos = SkPoint::Make(SkScalarInterp(pts[0].x(), pts[1].x(), t), SkScalarInterp(pts[0].y(), pts[1].y(), t)); break; case kCubic_SegType: SkEvalCubicAt(pts, t, &pos, nullptr, nullptr); break; case kConic_SegType: { SkConic conic(pts, pts[3].x()); conic.evalAt(t, &pos); } break; default: UNIMPLEMENTED; } return pos; }
SkPoint SkCubicBoundary::eval(Edge e, SkScalar t) { SkASSERT((unsigned)e < 4); // ensure our 4th cubic wraps to the start of the first fPts[12] = fPts[0]; SkPoint loc; SkEvalCubicAt(&fPts[e * 3], t, &loc, NULL, NULL); return loc; }
static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) { SkScalar t = 0; SkScalar dt = SK_Scalar1 / segs; samples[0] = cubic[0]; for (int i = 1; i < segs; i++) { t += dt; SkEvalCubicAt(cubic, t, &samples[i], nullptr, nullptr); } }
static void test_cubic_tangents(skiatest::Reporter* reporter) { SkPoint pts[] = { { 10, 20}, {10, 20}, {20, 30}, {30, 40}, { 10, 20}, {15, 25}, {20, 30}, {30, 40}, { 10, 20}, {20, 30}, {30, 40}, {30, 40}, }; int count = (int) SK_ARRAY_COUNT(pts) / 4; for (int index = 0; index < count; ++index) { SkConic conic(&pts[index * 3], 0.707f); SkVector start, mid, end; SkEvalCubicAt(&pts[index * 4], 0, nullptr, &start, nullptr); SkEvalCubicAt(&pts[index * 4], .5f, nullptr, &mid, nullptr); SkEvalCubicAt(&pts[index * 4], 1, nullptr, &end, nullptr); REPORTER_ASSERT(reporter, start.fX && start.fY); REPORTER_ASSERT(reporter, mid.fX && mid.fY); REPORTER_ASSERT(reporter, end.fX && end.fY); REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid))); REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end))); } }
/// Used inside SkCurveMeasure::getTime's Newton's iteration static inline SkVector evaluateDerivative(const SkPoint pts[4], SkSegType segType, SkScalar t) { SkVector tan; switch (segType) { case kQuad_SegType: tan = SkEvalQuadTangentAt(pts, t); break; case kLine_SegType: tan = pts[1] - pts[0]; break; case kCubic_SegType: SkEvalCubicAt(pts, t, nullptr, &tan, nullptr); break; case kConic_SegType: { SkConic conic(pts, pts[3].x()); conic.evalAt(t, nullptr, &tan); } break; default: UNIMPLEMENTED; } return tan; }
void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) { bool degenerateAB = SkPath::IsLineDegenerate(fPrevPt, pt1); bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2); bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3); if (degenerateAB + degenerateBC + degenerateCD >= 2) { this->lineTo(pt3); return; } SkVector normalAB, unitAB, normalCD, unitCD; // find the first tangent (which might be pt1 or pt2 { const SkPoint* nextPt = &pt1; if (degenerateAB) nextPt = &pt2; this->preJoinTo(*nextPt, &normalAB, &unitAB, false); } { SkPoint pts[4], tmp[13]; int i, count; SkVector n, u; SkScalar tValues[3]; pts[0] = fPrevPt; pts[1] = pt1; pts[2] = pt2; pts[3] = pt3; #if 1 count = SkChopCubicAtMaxCurvature(pts, tmp, tValues); #else count = 1; memcpy(tmp, pts, 4 * sizeof(SkPoint)); #endif n = normalAB; u = unitAB; for (i = 0; i < count; i++) { this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD, kMaxCubicSubdivide); if (i == count - 1) { break; } n = normalCD; u = unitCD; } // check for too pinchy for (i = 1; i < count; i++) { SkPoint p; SkVector v, c; SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c); SkScalar dot = SkPoint::DotProduct(c, c); v.scale(SkScalarInvert(dot)); if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) { fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction); } } } this->postJoinTo(pt3, normalCD, unitCD); }
bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) { // Find the minimum and maximum y of the extrema, which are the // first and last points since this cubic is monotonic SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY); SkScalar max_y = SkMaxScalar(cubic[0].fY, cubic[3].fY); if (pt.fY == cubic[0].fY || pt.fY < min_y || pt.fY > max_y) { // The query line definitely does not cross the curve return false; } SkScalar min_x = SkMinScalar( SkMinScalar( SkMinScalar(cubic[0].fX, cubic[1].fX), cubic[2].fX), cubic[3].fX); if (pt.fX < min_x) { // The query line definitely crosses the curve return true; } SkScalar max_x = SkMaxScalar( SkMaxScalar( SkMaxScalar(cubic[0].fX, cubic[1].fX), cubic[2].fX), cubic[3].fX); if (pt.fX > max_x) { // The query line definitely does not cross the curve return false; } // Do a binary search to find the parameter value which makes y as // close as possible to the query point. See whether the query // line's origin is to the left of the associated x coordinate. // kMaxIter is chosen as the number of mantissa bits for a float, // since there's no way we are going to get more precision by // iterating more times than that. const int kMaxIter = 23; SkPoint eval; int iter = 0; SkScalar upper_t; SkScalar lower_t; // Need to invert direction of t parameter if cubic goes up // instead of down if (cubic[3].fY > cubic[0].fY) { upper_t = SK_Scalar1; lower_t = SkFloatToScalar(0); } else { upper_t = SkFloatToScalar(0); lower_t = SK_Scalar1; } do { SkScalar t = SkScalarAve(upper_t, lower_t); SkEvalCubicAt(cubic, t, &eval, NULL, NULL); if (pt.fY > eval.fY) { lower_t = t; } else { upper_t = t; } } while (++iter < kMaxIter && !SkScalarNearlyZero(eval.fY - pt.fY)); if (pt.fX <= eval.fX) { return true; } return false; }