Add "enabled" flag to affectors, fix curve interaction with stable random

Flag is helpful while editing things (and could be a useful property to
animate, as well). The curve change fixes a bug where the stable generator
gets out of phase if all segments of a curve don't use the same options.

Bug: skia:
Change-Id: Ie151e775aee22957e79fa88feaafad72b6c781ff
Reviewed-on: https://skia-review.googlesource.com/c/195120
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/include/SkCurve.h b/modules/particles/include/SkCurve.h
index b3c0ca6..ec61228 100644
--- a/modules/particles/include/SkCurve.h
+++ b/modules/particles/include/SkCurve.h
@@ -45,7 +45,7 @@
  */
 
 struct SkCurveSegment {
-    SkScalar eval(SkScalar x, SkRandom& random) const;
+    SkScalar eval(SkScalar x, SkScalar t, bool negate) const;
     void visitFields(SkFieldVisitor* v);
 
     void setConstant(SkScalar c) {
diff --git a/modules/particles/include/SkParticleAffector.h b/modules/particles/include/SkParticleAffector.h
index 97a5b0f..9a10d9c 100644
--- a/modules/particles/include/SkParticleAffector.h
+++ b/modules/particles/include/SkParticleAffector.h
@@ -21,7 +21,8 @@
 public:
     REFLECTED_ABSTRACT(SkParticleAffector, SkReflected)
 
-    virtual void apply(SkParticleUpdateParams& params, SkParticleState& ps) = 0;
+    void apply(SkParticleUpdateParams& params, SkParticleState& ps);
+    void visitFields(SkFieldVisitor* v) override;
 
     static void RegisterAffectorTypes();
 
@@ -36,6 +37,11 @@
     static sk_sp<SkParticleAffector> MakeSize(const SkCurve& curve);
     static sk_sp<SkParticleAffector> MakeFrame(const SkCurve& curve);
     static sk_sp<SkParticleAffector> MakeColor(const SkColorCurve& curve);
+
+private:
+    virtual void onApply(SkParticleUpdateParams& params, SkParticleState& ps) = 0;
+
+    bool fEnabled = true;
 };
 
 #endif // SkParticleAffector_DEFINED
diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp
index aa03853..1f14a79 100644
--- a/modules/particles/src/SkCurve.cpp
+++ b/modules/particles/src/SkCurve.cpp
@@ -24,12 +24,12 @@
     return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x);
 }
 
-SkScalar SkCurveSegment::eval(SkScalar x, SkRandom& random) const {
+SkScalar SkCurveSegment::eval(SkScalar x, SkScalar t, bool negate) const {
     SkScalar result = fConstant ? fMin[0] : eval_cubic(fMin, x);
     if (fRanged) {
-        result += ((fConstant ? fMax[0] : eval_cubic(fMax, x)) - result) * random.nextF();
+        result += ((fConstant ? fMax[0] : eval_cubic(fMax, x)) - result) * t;
     }
-    if (fBidirectional && random.nextBool()) {
+    if (fBidirectional && negate) {
         result = -result;
     }
     return result;
@@ -66,7 +66,12 @@
         segmentX = rangeMin;
     }
     SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
-    return fSegments[i].eval(segmentX, random);
+
+    // Always pull t and negate here, so that the stable generator behaves consistently, even if
+    // our segments use an inconsistent feature-set.
+    SkScalar t = random.nextF();
+    bool negate = random.nextBool();
+    return fSegments[i].eval(segmentX, t, negate);
 }
 
 void SkCurve::visitFields(SkFieldVisitor* v) {
diff --git a/modules/particles/src/SkParticleAffector.cpp b/modules/particles/src/SkParticleAffector.cpp
index eb6fa0f..74d55e9 100644
--- a/modules/particles/src/SkParticleAffector.cpp
+++ b/modules/particles/src/SkParticleAffector.cpp
@@ -11,6 +11,16 @@
 #include "SkParticleData.h"
 #include "SkRandom.h"
 
+void SkParticleAffector::apply(SkParticleUpdateParams& params, SkParticleState& ps) {
+    if (fEnabled) {
+        this->onApply(params, ps);
+    }
+}
+
+void SkParticleAffector::visitFields(SkFieldVisitor* v) {
+    v->visit("Enabled", fEnabled);
+}
+
 class SkLinearVelocityAffector : public SkParticleAffector {
 public:
     SkLinearVelocityAffector(const SkCurve& angle = 0.0f,
@@ -24,7 +34,7 @@
 
     REFLECTED(SkLinearVelocityAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         float angle = fAngle.eval(ps.fAge, ps.fStableRandom);
         SkScalar c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
         SkVector heading = fLocal ? ps.fPose.fHeading : SkVector{ 0, -1 };
@@ -40,6 +50,7 @@
     }
 
     void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
         v->visit("Force", fForce);
         v->visit("Local", fLocal);
         v->visit("Angle", fAngle);
@@ -61,7 +72,7 @@
 
     REFLECTED(SkPointForceAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         SkVector toPoint = fPoint - ps.fPose.fPosition;
         SkScalar lenSquare = toPoint.dot(toPoint);
         toPoint.normalize();
@@ -69,6 +80,7 @@
     }
 
     void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
         v->visit("Point", fPoint);
         v->visit("Constant", fConstant);
         v->visit("InvSquare", fInvSquare);
@@ -86,7 +98,7 @@
 
     REFLECTED(SkOrientAlongVelocityAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         SkVector heading = ps.fVelocity.fLinear;
         if (!heading.normalize()) {
             heading.set(0, -1);
@@ -94,7 +106,9 @@
         ps.fPose.fHeading = heading;
     }
 
-    void visitFields(SkFieldVisitor*) override {}
+    void visitFields(SkFieldVisitor *v) override {
+        SkParticleAffector::visitFields(v);
+    }
 };
 
 class SkSizeAffector : public SkParticleAffector {
@@ -103,11 +117,12 @@
 
     REFLECTED(SkSizeAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         ps.fPose.fScale = fCurve.eval(ps.fAge, ps.fStableRandom);
     }
 
     void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
         v->visit("Curve", fCurve);
     }
 
@@ -121,11 +136,12 @@
 
     REFLECTED(SkFrameAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         ps.fFrame = fCurve.eval(ps.fAge, ps.fStableRandom);
     }
 
     void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
         v->visit("Curve", fCurve);
     }
 
@@ -140,11 +156,12 @@
 
     REFLECTED(SkColorAffector, SkParticleAffector)
 
-    void apply(SkParticleUpdateParams& params, SkParticleState& ps) override {
+    void onApply(SkParticleUpdateParams& params, SkParticleState& ps) override {
         ps.fColor = fCurve.eval(ps.fAge, ps.fStableRandom);
     }
 
     void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
         v->visit("Curve", fCurve);
     }