Refactor SkCurveMeasure to use existing eval code
authorhstern <hstern@google.com>
Wed, 10 Aug 2016 17:55:09 +0000 (10:55 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 10 Aug 2016 17:55:09 +0000 (10:55 -0700)
- Use quad, cubic, conic eval code from SkGeometry.h
- Implement evaluateDerivativeLength, evaluateDerivative and evaluate switch cases for lines along with the refactor

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2226973004

Review-Url: https://codereview.chromium.org/2226973004

src/utils/SkCurveMeasure.cpp
src/utils/SkCurveMeasure.h

index fc2aa84faa119558d5dce1c30f6bdd826c3eb890..823f56adcff9388f8e7f82980afee91bfa1c2e5d 100644 (file)
@@ -6,10 +6,66 @@
  */
 
 #include "SkCurveMeasure.h"
+#include "SkGeometry.h"
 
 // for abs
 #include <cmath>
 
+#define UNIMPLEMENTED SkDEBUGF(("%s:%d unimplemented\n", __FILE__, __LINE__))
+
+/// 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;
+}
+
+/// 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;
+}
+/// Used in ArcLengthIntegrator::computeLength
 static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
                                             const Sk8f (&xCoeff)[3],
                                             const Sk8f (&yCoeff)[3],
@@ -22,17 +78,18 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
             y = yCoeff[0]*ts + yCoeff[1];
             break;
         case kLine_SegType:
-            SkDebugf("Unimplemented");
-            break;
+            // length of line derivative is constant
+            // and we precompute it in the constructor
+            return xCoeff[0];
         case kCubic_SegType:
             x = (xCoeff[0]*ts + xCoeff[1])*ts + xCoeff[2];
             y = (yCoeff[0]*ts + yCoeff[1])*ts + yCoeff[2];
             break;
         case kConic_SegType:
-            SkDebugf("Unimplemented");
+            UNIMPLEMENTED;
             break;
         default:
-            SkDebugf("Unimplemented");
+            UNIMPLEMENTED;
     }
 
     x = x * x;
@@ -40,6 +97,7 @@ static inline Sk8f evaluateDerivativeLength(const Sk8f& ts,
 
     return (x + y).sqrt();
 }
+
 ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
     : fSegType(segType) {
     switch (fSegType) {
@@ -59,8 +117,13 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
             yCoeff[1] = Sk8f(2.0f*(By - Ay));
         }
             break;
-        case kLine_SegType:
-            SkDEBUGF(("Unimplemented"));
+        case kLine_SegType: {
+            // the length of the derivative of a line is constant
+            // we put in in both coeff arrays for consistency's sake
+            SkScalar length = (pts[1] - pts[0]).length();
+            xCoeff[0] = Sk8f(length);
+            yCoeff[0] = Sk8f(length);
+        }
             break;
         case kCubic_SegType:
         {
@@ -73,6 +136,7 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
             float Cy = pts[2].y();
             float Dy = pts[3].y();
 
+            // precompute coefficients for derivative
             xCoeff[0] = Sk8f(3.0f*(-Ax + 3.0f*(Bx - Cx) + Dx));
             xCoeff[1] = Sk8f(3.0f*(2.0f*(Ax - 2.0f*Bx + Cx)));
             xCoeff[2] = Sk8f(3.0f*(-Ax + Bx));
@@ -83,10 +147,10 @@ ArcLengthIntegrator::ArcLengthIntegrator(const SkPoint* pts, SkSegType segType)
         }
             break;
         case kConic_SegType:
-            SkDEBUGF(("Unimplemented"));
+            UNIMPLEMENTED;
             break;
         default:
-            SkDEBUGF(("Unimplemented"));
+            UNIMPLEMENTED;
     }
 }
 
@@ -117,7 +181,8 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
             }
             break;
         case SkSegType::kLine_SegType:
-            SkDebugf("Unimplemented");
+            fPts[0] = pts[0];
+            fPts[1] = pts[1];
             break;
         case SkSegType::kCubic_SegType:
             for (size_t i = 0; i < 4; i++) {
@@ -125,10 +190,12 @@ SkCurveMeasure::SkCurveMeasure(const SkPoint* pts, SkSegType segType)
             }
             break;
         case SkSegType::kConic_SegType:
-            SkDebugf("Unimplemented");
+            for (size_t i = 0; i < 4; i++) {
+                fPts[i] = pts[i];
+            }
             break;
         default:
-            SkDEBUGF(("Unimplemented"));
+            UNIMPLEMENTED;
             break;
     }
     fIntegrator = ArcLengthIntegrator(fPts, fSegType);
@@ -199,9 +266,8 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
 
         prevT = currentT;
         if (iterations < kNewtonIters) {
-            // TODO(hstern) switch here on curve type.
             // This is just newton's formula.
-            SkScalar dt = evaluateQuadDerivative(currentT).length();
+            SkScalar dt = evaluateDerivative(fPts, fSegType, currentT).length();
             newT = currentT - (lengthDiff / dt);
 
             // If newT is out of bounds, bisect inside newton.
@@ -218,7 +284,7 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
             newT = (minT + maxT) * 0.5f;
         } else {
             SkDEBUGF(("%.7f %.7f didn't get close enough after bisection.\n",
-                     currentT, currentLength));
+                      currentT, currentLength));
             break;
         }
         currentT = newT;
@@ -235,52 +301,16 @@ SkScalar SkCurveMeasure::getTime(SkScalar targetLength) {
 }
 
 void SkCurveMeasure::getPosTanTime(SkScalar targetLength, SkPoint* pos,
-                               SkVector* tan, SkScalar* time) {
+                                   SkVector* tan, SkScalar* time) {
     SkScalar t = getTime(targetLength);
 
     if (time) {
         *time = t;
     }
     if (pos) {
-        // TODO(hstern) switch here on curve type.
-        *pos = evaluateQuad(t);
+        *pos = evaluate(fPts, fSegType, t);
     }
     if (tan) {
-        // TODO(hstern) switch here on curve type.
-        *tan = evaluateQuadDerivative(t);
+        *tan = evaluateDerivative(fPts, fSegType, t);
     }
 }
-
-// this is why I feel that the ArcLengthIntegrator should be combined
-// with some sort of evaluator that caches the constants computed from the
-// control points. this is basically the same code in ArcLengthIntegrator
-SkPoint SkCurveMeasure::evaluateQuad(SkScalar t) {
-    SkScalar ti = 1.0f - t;
-
-    SkScalar Ax = fPts[0].x();
-    SkScalar Bx = fPts[1].x();
-    SkScalar Cx = fPts[2].x();
-    SkScalar Ay = fPts[0].y();
-    SkScalar By = fPts[1].y();
-    SkScalar Cy = fPts[2].y();
-
-    SkScalar x = Ax*ti*ti + 2.0f*Bx*t*ti + Cx*t*t;
-    SkScalar y = Ay*ti*ti + 2.0f*By*t*ti + Cy*t*t;
-    return SkPoint::Make(x, y);
-}
-
-SkVector SkCurveMeasure::evaluateQuadDerivative(SkScalar t) {
-    SkScalar Ax = fPts[0].x();
-    SkScalar Bx = fPts[1].x();
-    SkScalar Cx = fPts[2].x();
-    SkScalar Ay = fPts[0].y();
-    SkScalar By = fPts[1].y();
-    SkScalar Cy = fPts[2].y();
-
-    SkScalar A2BCx = 2.0f*(Ax - 2*Bx + Cx);
-    SkScalar A2BCy = 2.0f*(Ay - 2*By + Cy);
-    SkScalar ABx = 2.0f*(Bx - Ax);
-    SkScalar ABy = 2.0f*(By - Ay);
-
-    return SkPoint::Make(A2BCx*t + ABx, A2BCy*t + ABy);
-}
index 2846103633145729861e63d7b282fcc5ee67defe..5807211236efbe96d146eff421f096e5697e7957 100644 (file)
@@ -44,6 +44,15 @@ private:
 class SkCurveMeasure {
 public:
     SkCurveMeasure() {}
+
+    // Almost exactly the same as in SkPath::Iter:
+    // kLine_SegType  -> 2 points: start end
+    // kQuad_SegType  -> 3 points: start control end
+    // kCubic_SegType -> 4 points: start control1 control2 end
+    // kConic_SegType -> 4 points: start control end (w, w)
+    //
+    // i.e. the only difference is that the conic's last point is a point
+    // consisting of the w value twice
     SkCurveMeasure(const SkPoint* pts, SkSegType segType);
 
     SkScalar getTime(SkScalar targetLength);
@@ -51,13 +60,6 @@ public:
     SkScalar getLength();
 
 private:
-    SkPoint evaluateQuad(SkScalar t);
-    SkVector evaluateQuadDerivative(SkScalar t);
-    //SkPoint evaluate_cubic(SkScalar t);
-    //SkVector evaluate_cubic_derivative(SkScalar t);
-    //SkPoint evaluate_conic(SkScalar t);
-    //SkVector evaluate_conic_derivative(SkScalar t);
-
     const SkScalar kTolerance = 0.0001f;
     const int kNewtonIters = 5;
     const int kBisectIters = 5;