New SkCurve type with multiple curve segments
- Converted all linear force stuff into a single affector,
used at either spawn or update time appropriately.
The new affector can either set or adjust velocity.
- Converted lifetime to a curve.
- Removed SkRangedFloat, initial velocity params, etc.
Looks like a large addition, but that's mostly down to the
JSON getting bigger. There's a net reduction in LoC.
Bug: skia:
Change-Id: Iac7417f15f96d0313efd08c4b26dc3250b80fa77
Reviewed-on: https://skia-review.googlesource.com/c/192102
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp
index fdd1a9c..a206e76ed 100644
--- a/modules/particles/src/SkCurve.cpp
+++ b/modules/particles/src/SkCurve.cpp
@@ -10,15 +10,26 @@
#include "SkRandom.h"
#include "SkReflected.h"
-SkScalar SkCurve::eval(float x, SkRandom& random) const {
- float ix = (1 - x);
- float y0 = fMin[0] * ix*ix*ix + fMin[1] * 3 * ix*ix*x + fMin[2] * 3 * ix*x*x + fMin[3] * x*x*x;
- float y1 = fMax[0] * ix*ix*ix + fMax[1] * 3 * ix*ix*x + fMax[2] * 3 * ix*x*x + fMax[3] * x*x*x;
- return fRanged ? y0 + (y1 - y0) * random.nextF() : y0;
+static SkScalar eval_cubic(const SkScalar* pts, SkScalar x) {
+ SkScalar ix = (1 - x);
+ return pts[0]*ix*ix*ix + pts[1]*3*ix*ix*x + pts[2]*3*ix*x*x + pts[3]*x*x*x;
}
-void SkCurve::visitFields(SkFieldVisitor* v) {
+SkScalar SkCurveSegment::eval(SkScalar x, SkRandom& random) const {
+ SkScalar result = fConstant ? fMin[0] : eval_cubic(fMin, x);
+ if (fRanged) {
+ result += ((fConstant ? fMax[0] : eval_cubic(fMax, x)) - result) * random.nextF();
+ }
+ if (fBidirectional && random.nextBool()) {
+ result = -result;
+ }
+ return result;
+}
+
+void SkCurveSegment::visitFields(SkFieldVisitor* v) {
+ v->visit("Constant", fConstant);
v->visit("Ranged", fRanged);
+ v->visit("Bidirectional", fBidirectional);
v->visit("A0", fMin[0]);
v->visit("B0", fMin[1]);
v->visit("C0", fMin[2]);
@@ -29,11 +40,33 @@
v->visit("D1", fMax[3]);
}
-float SkRangedFloat::eval(SkRandom& random) const {
- return random.nextRangeF(fMin, fMax);
+SkScalar SkCurve::eval(SkScalar x, SkRandom& random) const {
+ SkASSERT(fSegments.count() == fXValues.count() + 1);
+
+ int i = 0;
+ for (; i < fXValues.count(); ++i) {
+ if (x <= fXValues[i]) {
+ break;
+ }
+ }
+
+ SkScalar rangeMin = (i == 0) ? 0.0f : fXValues[i - 1];
+ SkScalar rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i];
+ SkScalar segmentX = (x - rangeMin) / (rangeMax - rangeMin);
+ SkASSERT(0.0f <= segmentX && segmentX <= 1.0f);
+ return fSegments[i].eval(segmentX, random);
}
-void SkRangedFloat::visitFields(SkFieldVisitor* v) {
- v->visit("min", fMin);
- v->visit("max", fMax);
+void SkCurve::visitFields(SkFieldVisitor* v) {
+ v->visit("XValues", fXValues);
+ v->visit("Segments", fSegments);
+
+ // Validate and fixup
+ if (fSegments.empty()) {
+ fSegments.push_back().setConstant(0.0f);
+ }
+ fXValues.resize_back(fSegments.count() - 1);
+ for (int i = 0; i < fXValues.count(); ++i) {
+ fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f);
+ }
}