New SkCurve type with multiple curve segments

- Converted all linear force stuff into a single affector,
  used at either spawn or update time appropriately.
  The new affector can either set or adjust velocity.
- Converted lifetime to a curve.
- Removed SkRangedFloat, initial velocity params, etc.

Looks like a large addition, but that's mostly down to the
JSON getting bigger. There's a net reduction in LoC.

Bug: skia:
Change-Id: Iac7417f15f96d0313efd08c4b26dc3250b80fa77
Reviewed-on: https://skia-review.googlesource.com/c/192102
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp
index fdd1a9c..a206e76ed 100644
--- a/modules/particles/src/SkCurve.cpp
+++ b/modules/particles/src/SkCurve.cpp
@@ -10,15 +10,26 @@
 #include "SkRandom.h"
 #include "SkReflected.h"
 
-SkScalar SkCurve::eval(float x, SkRandom& random) const {
-    float ix = (1 - x);
-    float y0 = fMin[0] * ix*ix*ix + fMin[1] * 3 * ix*ix*x + fMin[2] * 3 * ix*x*x + fMin[3] * x*x*x;
-    float y1 = fMax[0] * ix*ix*ix + fMax[1] * 3 * ix*ix*x + fMax[2] * 3 * ix*x*x + fMax[3] * x*x*x;
-    return fRanged ? y0 + (y1 - y0) * random.nextF() : y0;
+static SkScalar eval_cubic(const SkScalar* pts, SkScalar x) {
+    SkScalar ix = (1 - x);
+    return pts[0]*ix*ix*ix + pts[1]*3*ix*ix*x + pts[2]*3*ix*x*x + pts[3]*x*x*x;
 }
 
-void SkCurve::visitFields(SkFieldVisitor* v) {
+SkScalar SkCurveSegment::eval(SkScalar x, SkRandom& random) const {
+    SkScalar result = fConstant ? fMin[0] : eval_cubic(fMin, x);
+    if (fRanged) {
+        result += ((fConstant ? fMax[0] : eval_cubic(fMax, x)) - result) * random.nextF();
+    }
+    if (fBidirectional && random.nextBool()) {
+        result = -result;
+    }
+    return result;
+}
+
+void SkCurveSegment::visitFields(SkFieldVisitor* v) {
+    v->visit("Constant", fConstant);
     v->visit("Ranged", fRanged);
+    v->visit("Bidirectional", fBidirectional);
     v->visit("A0", fMin[0]);
     v->visit("B0", fMin[1]);
     v->visit("C0", fMin[2]);
@@ -29,11 +40,33 @@
     v->visit("D1", fMax[3]);
 }
 
-float SkRangedFloat::eval(SkRandom& random) const {
-    return random.nextRangeF(fMin, fMax);
+SkScalar SkCurve::eval(SkScalar x, SkRandom& random) const {
+    SkASSERT(fSegments.count() == fXValues.count() + 1);
+
+    int i = 0;
+    for (; i < fXValues.count(); ++i) {
+        if (x <= fXValues[i]) {
+            break;
+        }
+    }
+
+    SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
+    SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
+    SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
+    SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
+    return fSegments[i].eval(segmentX, random);
 }
 
-void SkRangedFloat::visitFields(SkFieldVisitor* v) {
-    v->visit("min", fMin);
-    v->visit("max", fMax);
+void SkCurve::visitFields(SkFieldVisitor* v) {
+    v->visit("XValues", fXValues);
+    v->visit("Segments", fSegments);
+
+    // Validate and fixup
+    if (fSegments.empty()) {
+        fSegments.push_back().setConstant(0.0f);
+    }
+    fXValues.resize_back(fSegments.count() - 1);
+    for (int i = 0; i < fXValues.count(); ++i) {
+        fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
+    }
 }