966348413f
Bug: skia: Change-Id: I499262277ac1c8d92a39a66f6e846e248b102aef Reviewed-on: https://skia-review.googlesource.com/c/skia/+/197767 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Kevin Lubick <kjlubick@google.com>
479 lines
15 KiB
C++
479 lines
15 KiB
C++
/*
|
|
* 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 c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
|
|
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 c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
|
|
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));
|
|
}
|