text: /*
  * Copyright 2019 Google LLC
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
  
  #include "SkParticleAffector.h"
  
  #include "SkContourMeasure.h"
  #include "SkCurve.h"
  #include "SkParsePath.h"
  #include "SkParticleData.h"
  #include "SkPath.h"
  #include "SkRandom.h"
  #include "SkTextUtils.h"
  
  
  void SkParticleAffector::apply(const SkParticleUpdateParams& params,
                                 SkParticleState ps[], int count) {
      if (fEnabled) {
          this->onApply(params, ps, count);
      }
  }
  
  void SkParticleAffector::visitFields(SkFieldVisitor* v) {
      v->visit("Enabled", fEnabled);
  }
  
  class SkLinearVelocityAffector : public SkParticleAffector {
  public:
      SkLinearVelocityAffector(const SkCurve& angle = 0.0f,
                               const SkCurve& strength = 0.0f,
                               bool force = true,
                               SkParticleFrame frame = kWorld_ParticleFrame)
          : fAngle(angle)
          , fStrength(strength)
          , fForce(force)
          , fFrame(frame) {}
  
      REFLECTED(SkLinearVelocityAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              float angle = fAngle.eval(params, ps[i]);
              SkScalar rad = SkDegreesToRadians(angle);
              SkScalar s_local = SkScalarSin(rad),
                       c_local = SkScalarCos(rad);
              SkVector heading = ps[i].getFrameHeading(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(params, ps[i]);
              SkVector force = { c * strength, s * strength };
              if (fForce) {
                  ps[i].fVelocity.fLinear += force * params.fDeltaTime;
              } else {
                  ps[i].fVelocity.fLinear = force;
              }
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("Force", fForce);
          v->visit("Frame", fFrame, gParticleFrameMapping, SK_ARRAY_COUNT(gParticleFrameMapping));
          v->visit("Angle", fAngle);
          v->visit("Strength", fStrength);
      }
  
  private:
      SkCurve fAngle;
      SkCurve fStrength;
      bool fForce;
      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(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              float strength = fStrength.eval(params, ps[i]);
              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 {
  public:
      SkPointForceAffector(SkPoint point = { 0.0f, 0.0f }, SkScalar constant = 0.0f,
                           SkScalar invSquare = 0.0f)
              : fPoint(point), fConstant(constant), fInvSquare(invSquare) {}
  
      REFLECTED(SkPointForceAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              SkVector toPoint = fPoint - ps[i].fPose.fPosition;
              SkScalar lenSquare = toPoint.dot(toPoint);
              toPoint.normalize();
              ps[i].fVelocity.fLinear +=
                      toPoint * (fConstant + (fInvSquare / lenSquare)) * params.fDeltaTime;
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("Point", fPoint);
          v->visit("Constant", fConstant);
          v->visit("InvSquare", fInvSquare);
      }
  
  private:
      SkPoint  fPoint;
      SkScalar fConstant;
      SkScalar fInvSquare;
  };
  
  class SkOrientationAffector : public SkParticleAffector {
  public:
      SkOrientationAffector(const SkCurve& angle = 0.0f,
                            SkParticleFrame frame = kLocal_ParticleFrame)
          : fAngle(angle)
          , fFrame(frame) {}
  
      REFLECTED(SkOrientationAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              float angle = fAngle.eval(params, ps[i]);
              SkScalar rad = SkDegreesToRadians(angle);
              SkScalar s_local = SkScalarSin(rad),
                       c_local = SkScalarCos(rad);
              SkVector heading = ps[i].getFrameHeading(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, gParticleFrameMapping, SK_ARRAY_COUNT(gParticleFrameMapping));
          v->visit("Angle", fAngle);
      }
  
  private:
      SkCurve fAngle;
      int     fFrame;
  };
  
  class SkPositionInCircleAffector : public SkParticleAffector {
  public:
      SkPositionInCircleAffector(const SkCurve& x = 0.0f, const SkCurve& y = 0.0f,
                                 const SkCurve& radius = 0.0f, bool setHeading = true)
          : fX(x)
          , fY(y)
          , fRadius(radius)
          , fSetHeading(setHeading) {}
  
      REFLECTED(SkPositionInCircleAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              SkVector v;
              do {
                  v.fX = ps[i].fRandom.nextSScalar1();
                  v.fY = ps[i].fRandom.nextSScalar1();
              } while (v.dot(v) > 1);
  
              SkPoint center = { fX.eval(params, ps[i]), fY.eval(params, ps[i]) };
              SkScalar radius = fRadius.eval(params, ps[i]);
              ps[i].fPose.fPosition = center + (v * radius);
              if (fSetHeading) {
                  if (!v.normalize()) {
                      v.set(0, -1);
                  }
                  ps[i].fPose.fHeading = v;
              }
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("SetHeading", fSetHeading);
          v->visit("X", fX);
          v->visit("Y", fY);
          v->visit("Radius", fRadius);
      }
  
  private:
      SkCurve fX;
      SkCurve fY;
      SkCurve fRadius;
      bool    fSetHeading;
  };
  
  class SkPositionOnPathAffector : public SkParticleAffector {
  public:
      SkPositionOnPathAffector(const char* path = "", bool setHeading = true,
                               SkParticleValue input = SkParticleValue())
              : fPath(path)
              , fInput(input)
              , fSetHeading(setHeading) {
          this->rebuild();
      }
  
      REFLECTED(SkPositionOnPathAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          if (fContours.empty()) {
              return;
          }
  
          for (int i = 0; i < count; ++i) {
              float t = fInput.eval(params, ps[i]);
              SkScalar len = fTotalLength * t;
              int idx = 0;
              while (idx < fContours.count() && len > fContours[idx]->length()) {
                  len -= fContours[idx++]->length();
              }
              SkVector localXAxis;
              if (!fContours[idx]->getPosTan(len, &ps[i].fPose.fPosition, &localXAxis)) {
                  ps[i].fPose.fPosition = { 0, 0 };
                  localXAxis = { 1, 0 };
              }
              if (fSetHeading) {
                  ps[i].fPose.fHeading.set(localXAxis.fY, -localXAxis.fX);
              }
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkString oldPath = fPath;
  
          SkParticleAffector::visitFields(v);
          v->visit("Input", fInput);
          v->visit("SetHeading", fSetHeading);
          v->visit("Path", fPath);
  
          if (fPath != oldPath) {
              this->rebuild();
          }
      }
  
  private:
      SkString        fPath;
      SkParticleValue fInput;
      bool            fSetHeading;
  
      void rebuild() {
          SkPath path;
          if (!SkParsePath::FromSVGString(fPath.c_str(), &path)) {
              return;
          }
  
          fTotalLength = 0;
          fContours.reset();
  
          SkContourMeasureIter iter(path, false);
          while (auto contour = iter.next()) {
              fContours.push_back(contour);
              fTotalLength += contour->length();
          }
      }
  
      // Cached
      SkScalar                          fTotalLength;
      SkTArray<sk_sp<SkContourMeasure>> fContours;
  };
  
  class SkPositionOnTextAffector : public SkParticleAffector {
  public:
      SkPositionOnTextAffector(const char* text = "", SkScalar fontSize = 96, bool setHeading = true,
                               SkParticleValue input = SkParticleValue())
              : fText(text)
              , fFontSize(fontSize)
              , fInput(input)
              , fSetHeading(setHeading) {
          this->rebuild();
      }
  
      REFLECTED(SkPositionOnTextAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          if (fContours.empty()) {
              return;
          }
  
          // TODO: Refactor to share code with PositionOnPathAffector
          for (int i = 0; i < count; ++i) {
              float t = fInput.eval(params, ps[i]);
              SkScalar len = fTotalLength * t;
              int idx = 0;
              while (idx < fContours.count() && len > fContours[idx]->length()) {
                  len -= fContours[idx++]->length();
              }
              SkVector localXAxis;
              if (!fContours[idx]->getPosTan(len, &ps[i].fPose.fPosition, &localXAxis)) {
                  ps[i].fPose.fPosition = { 0, 0 };
                  localXAxis = { 1, 0 };
              }
              if (fSetHeading) {
                  ps[i].fPose.fHeading.set(localXAxis.fY, -localXAxis.fX);
              }
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkString oldText = fText;
          SkScalar oldSize = fFontSize;
  
          SkParticleAffector::visitFields(v);
          v->visit("Input", fInput);
          v->visit("SetHeading", fSetHeading);
          v->visit("Text", fText);
          v->visit("FontSize", fFontSize);
  
          if (fText != oldText || fFontSize != oldSize) {
              this->rebuild();
          }
      }
  
  private:
      SkString        fText;
      SkScalar        fFontSize;
      SkParticleValue fInput;
      bool            fSetHeading;
  
      void rebuild() {
          fTotalLength = 0;
          fContours.reset();
  
          if (fText.isEmpty()) {
              return;
          }
  
          // Use the font manager's default font
          SkFont font(nullptr, fFontSize);
          SkPath path;
          SkTextUtils::GetPath(fText.c_str(), fText.size(), kUTF8_SkTextEncoding, 0, 0, font, &path);
          SkContourMeasureIter iter(path, false);
          while (auto contour = iter.next()) {
              fContours.push_back(contour);
              fTotalLength += contour->length();
          }
      }
  
      // Cached
      SkScalar                          fTotalLength;
      SkTArray<sk_sp<SkContourMeasure>> fContours;
  };
  
  class SkSizeAffector : public SkParticleAffector {
  public:
      SkSizeAffector(const SkCurve& curve = 1.0f) : fCurve(curve) {}
  
      REFLECTED(SkSizeAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              ps[i].fPose.fScale = fCurve.eval(params, ps[i]);
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("Curve", fCurve);
      }
  
  private:
      SkCurve fCurve;
  };
  
  class SkFrameAffector : public SkParticleAffector {
  public:
      SkFrameAffector(const SkCurve& curve = 1.0f) : fCurve(curve) {}
  
      REFLECTED(SkFrameAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              ps[i].fFrame = fCurve.eval(params, ps[i]);
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("Curve", fCurve);
      }
  
  private:
      SkCurve fCurve;
  };
  
  class SkColorAffector : public SkParticleAffector {
  public:
      SkColorAffector(const SkColorCurve& curve = SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f })
          : fCurve(curve) {}
  
      REFLECTED(SkColorAffector, SkParticleAffector)
  
      void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
          for (int i = 0; i < count; ++i) {
              ps[i].fColor = fCurve.eval(params, ps[i]);
          }
      }
  
      void visitFields(SkFieldVisitor* v) override {
          SkParticleAffector::visitFields(v);
          v->visit("Curve", fCurve);
      }
  
  private:
      SkColorCurve fCurve;
  };
  
  void SkParticleAffector::RegisterAffectorTypes() {
      REGISTER_REFLECTED(SkParticleAffector);
      REGISTER_REFLECTED(SkLinearVelocityAffector);
      REGISTER_REFLECTED(SkAngularVelocityAffector);
      REGISTER_REFLECTED(SkPointForceAffector);
      REGISTER_REFLECTED(SkOrientationAffector);
      REGISTER_REFLECTED(SkPositionInCircleAffector);
      REGISTER_REFLECTED(SkPositionOnPathAffector);
      REGISTER_REFLECTED(SkPositionOnTextAffector);
      REGISTER_REFLECTED(SkSizeAffector);
      REGISTER_REFLECTED(SkFrameAffector);
      REGISTER_REFLECTED(SkColorAffector);
  }
  
  sk_sp<SkParticleAffector> SkParticleAffector::MakeLinearVelocity(const SkCurve& angle,
                                                                   const SkCurve& strength,
                                                                   bool force,
                                                                   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,
                                                               SkScalar invSquare) {
      return sk_sp<SkParticleAffector>(new SkPointForceAffector(point, constant, invSquare));
  }
  
  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) {
      return sk_sp<SkParticleAffector>(new SkSizeAffector(curve));
  }
  
  sk_sp<SkParticleAffector> SkParticleAffector::MakeFrame(const SkCurve& curve) {
      return sk_sp<SkParticleAffector>(new SkFrameAffector(curve));
  }
  
  sk_sp<SkParticleAffector> SkParticleAffector::MakeColor(const SkColorCurve& curve) {
      return sk_sp<SkParticleAffector>(new SkColorAffector(curve));
  }
  