Few more particle features and some generalization work

Add particle "frame" enum, to allow effects relative to local, world,
or velocity. Remove the "orient along velocity" and replace with a much
more general orientation affector (angle curve + frame). Add an angular
velocity affector to mirror the behavior of the linear velocity affector.

Bug: skia:
Change-Id: Ibbaaeb352c9547d00d81c7916d00148dd65ed2b9
Reviewed-on: https://skia-review.googlesource.com/c/195361
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/src/SkParticleAffector.cpp b/modules/particles/src/SkParticleAffector.cpp
index de1a66c..8ff80a0 100644
--- a/modules/particles/src/SkParticleAffector.cpp
+++ b/modules/particles/src/SkParticleAffector.cpp
@@ -21,16 +21,33 @@
     v->visit("Enabled", fEnabled);
 }
 
+static inline SkVector get_heading(const SkParticleState& ps, SkParticleFrame frame) {
+    switch (frame) {
+        case kLocal_ParticleFrame:
+            return ps.fPose.fHeading;
+        case kVelocity_ParticleFrame: {
+            SkVector heading = ps.fVelocity.fLinear;
+            if (!heading.normalize()) {
+                heading.set(0, -1);
+            }
+            return heading;
+        }
+        case kWorld_ParticleFrame:
+        default:
+            return SkVector{ 0, -1 };
+    }
+}
+
 class SkLinearVelocityAffector : public SkParticleAffector {
 public:
     SkLinearVelocityAffector(const SkCurve& angle = 0.0f,
                              const SkCurve& strength = 0.0f,
                              bool force = true,
-                             bool local = false)
+                             SkParticleFrame frame = kWorld_ParticleFrame)
         : fAngle(angle)
         , fStrength(strength)
         , fForce(force)
-        , fLocal(local) {}
+        , fFrame(frame) {}
 
     REFLECTED(SkLinearVelocityAffector, SkParticleAffector)
 
@@ -38,7 +55,7 @@
         for (int i = 0; i < count; ++i) {
             float angle = fAngle.eval(ps[i].fAge, ps[i].fStableRandom);
             SkScalar c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
-            SkVector heading = fLocal ? ps[i].fPose.fHeading : SkVector{ 0, -1 };
+            SkVector heading = get_heading(ps[i], static_cast<SkParticleFrame>(fFrame));
             SkScalar c = heading.fX * c_local - heading.fY * s_local;
             SkScalar s = heading.fX * s_local + heading.fY * c_local;
             float strength = fStrength.eval(ps[i].fAge, ps[i].fStableRandom);
@@ -54,7 +71,7 @@
     void visitFields(SkFieldVisitor* v) override {
         SkParticleAffector::visitFields(v);
         v->visit("Force", fForce);
-        v->visit("Local", fLocal);
+        v->visit("Frame", fFrame);
         v->visit("Angle", fAngle);
         v->visit("Strength", fStrength);
     }
@@ -63,7 +80,37 @@
     SkCurve fAngle;
     SkCurve fStrength;
     bool fForce;
-    bool fLocal;
+    int  fFrame;
+};
+
+class SkAngularVelocityAffector : public SkParticleAffector {
+public:
+    SkAngularVelocityAffector(const SkCurve& strength = 0.0f, bool force = true)
+        : fStrength(strength)
+        , fForce(force) {}
+
+    REFLECTED(SkAngularVelocityAffector, SkParticleAffector)
+
+    void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
+        for (int i = 0; i < count; ++i) {
+            float strength = fStrength.eval(ps[i].fAge, ps[i].fStableRandom);
+            if (fForce) {
+                ps[i].fVelocity.fAngular += strength * params.fDeltaTime;
+            } else {
+                ps[i].fVelocity.fAngular = strength;
+            }
+        }
+    }
+
+    void visitFields(SkFieldVisitor* v) override {
+        SkParticleAffector::visitFields(v);
+        v->visit("Force", fForce);
+        v->visit("Strength", fStrength);
+    }
+
+private:
+    SkCurve fStrength;
+    bool    fForce;
 };
 
 class SkPointForceAffector : public SkParticleAffector {
@@ -97,25 +144,34 @@
     SkScalar fInvSquare;
 };
 
-class SkOrientAlongVelocityAffector : public SkParticleAffector {
+class SkOrientationAffector : public SkParticleAffector {
 public:
-    SkOrientAlongVelocityAffector() {}
+    SkOrientationAffector(const SkCurve& angle = 0.0f,
+                          SkParticleFrame frame = kLocal_ParticleFrame)
+        : fAngle(angle)
+        , fFrame(frame) {}
 
-    REFLECTED(SkOrientAlongVelocityAffector, SkParticleAffector)
+    REFLECTED(SkOrientationAffector, SkParticleAffector)
 
     void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
         for (int i = 0; i < count; ++i) {
-            SkVector heading = ps[i].fVelocity.fLinear;
-            if (!heading.normalize()) {
-                heading.set(0, -1);
-            }
-            ps[i].fPose.fHeading = heading;
+            float angle = fAngle.eval(ps[i].fAge, ps[i].fStableRandom);
+            SkScalar c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
+            SkVector heading = get_heading(ps[i], static_cast<SkParticleFrame>(fFrame));
+            ps[i].fPose.fHeading.set(heading.fX * c_local - heading.fY * s_local,
+                                     heading.fX * s_local + heading.fY * c_local);
         }
     }
 
     void visitFields(SkFieldVisitor *v) override {
         SkParticleAffector::visitFields(v);
+        v->visit("Frame", fFrame);
+        v->visit("Angle", fAngle);
     }
+
+private:
+    SkCurve fAngle;
+    int     fFrame;
 };
 
 class SkSizeAffector : public SkParticleAffector {
@@ -185,8 +241,9 @@
 void SkParticleAffector::RegisterAffectorTypes() {
     REGISTER_REFLECTED(SkParticleAffector);
     REGISTER_REFLECTED(SkLinearVelocityAffector);
+    REGISTER_REFLECTED(SkAngularVelocityAffector);
     REGISTER_REFLECTED(SkPointForceAffector);
-    REGISTER_REFLECTED(SkOrientAlongVelocityAffector);
+    REGISTER_REFLECTED(SkOrientationAffector);
     REGISTER_REFLECTED(SkSizeAffector);
     REGISTER_REFLECTED(SkFrameAffector);
     REGISTER_REFLECTED(SkColorAffector);
@@ -195,8 +252,13 @@
 sk_sp<SkParticleAffector> SkParticleAffector::MakeLinearVelocity(const SkCurve& angle,
                                                                  const SkCurve& strength,
                                                                  bool force,
-                                                                 bool local) {
-    return sk_sp<SkParticleAffector>(new SkLinearVelocityAffector(angle, strength, force, local));
+                                                                 SkParticleFrame frame) {
+    return sk_sp<SkParticleAffector>(new SkLinearVelocityAffector(angle, strength, force, frame));
+}
+
+sk_sp<SkParticleAffector> SkParticleAffector::MakeAngularVelocity(const SkCurve& strength,
+                                                                  bool force) {
+    return sk_sp<SkParticleAffector>(new SkAngularVelocityAffector(strength, force));
 }
 
 sk_sp<SkParticleAffector> SkParticleAffector::MakePointForce(SkPoint point, SkScalar constant,
@@ -204,8 +266,9 @@
     return sk_sp<SkParticleAffector>(new SkPointForceAffector(point, constant, invSquare));
 }
 
-sk_sp<SkParticleAffector> SkParticleAffector::MakeOrientAlongVelocity() {
-    return sk_sp<SkParticleAffector>(new SkOrientAlongVelocityAffector());
+sk_sp<SkParticleAffector> SkParticleAffector::MakeOrientation(const SkCurve& angle,
+                                                              SkParticleFrame frame) {
+    return sk_sp<SkParticleAffector>(new SkOrientationAffector(angle, frame));
 }
 
 sk_sp<SkParticleAffector> SkParticleAffector::MakeSize(const SkCurve& curve) {