blob: 82ea43fffbeaed1a64bd3617eebfe890d2945e60 [file] [log] [blame]
/*
* 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));
}