blob: 9904ca21b64c390bd3249631c3d4742a48d7b0c6 [file] [log] [blame]
Brian Osman7c979f52019-02-12 13:27:51 -05001/*
2* Copyright 2019 Google LLC
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#include "SkParticleAffector.h"
9
Brian Osman3d76d1b2019-02-28 15:48:05 -050010#include "SkContourMeasure.h"
Brian Osman8b6283f2019-02-14 16:55:21 -050011#include "SkCurve.h"
Brian Osman3d76d1b2019-02-28 15:48:05 -050012#include "SkParsePath.h"
Brian Osman7c979f52019-02-12 13:27:51 -050013#include "SkParticleData.h"
Brian Osman3d76d1b2019-02-28 15:48:05 -050014#include "SkPath.h"
Brian Osman7c979f52019-02-12 13:27:51 -050015#include "SkRandom.h"
Brian Osman3d76d1b2019-02-28 15:48:05 -050016#include "SkTextUtils.h"
17
18#include "sk_tool_utils.h"
Brian Osman7c979f52019-02-12 13:27:51 -050019
Brian Osmane5d532e2019-02-26 14:58:40 -050020constexpr SkFieldVisitor::EnumStringMapping gParticleFrameMapping[] = {
21 { kWorld_ParticleFrame, "World" },
22 { kLocal_ParticleFrame, "Local" },
23 { kVelocity_ParticleFrame, "Velocity" },
24};
25
Brian Osman14a67a32019-02-25 14:30:44 -050026void SkParticleAffector::apply(SkParticleUpdateParams& params, SkParticleState ps[], int count) {
Brian Osman1b20cd82019-02-25 14:15:02 -050027 if (fEnabled) {
Brian Osman14a67a32019-02-25 14:30:44 -050028 this->onApply(params, ps, count);
Brian Osman1b20cd82019-02-25 14:15:02 -050029 }
30}
31
32void SkParticleAffector::visitFields(SkFieldVisitor* v) {
33 v->visit("Enabled", fEnabled);
34}
35
Brian Osman0c486812019-02-26 10:02:15 -050036static inline SkVector get_heading(const SkParticleState& ps, SkParticleFrame frame) {
37 switch (frame) {
38 case kLocal_ParticleFrame:
39 return ps.fPose.fHeading;
40 case kVelocity_ParticleFrame: {
41 SkVector heading = ps.fVelocity.fLinear;
42 if (!heading.normalize()) {
43 heading.set(0, -1);
44 }
45 return heading;
46 }
47 case kWorld_ParticleFrame:
48 default:
49 return SkVector{ 0, -1 };
50 }
51}
52
Brian Osman8b6283f2019-02-14 16:55:21 -050053class SkLinearVelocityAffector : public SkParticleAffector {
Brian Osman7c979f52019-02-12 13:27:51 -050054public:
Brian Osman8b6283f2019-02-14 16:55:21 -050055 SkLinearVelocityAffector(const SkCurve& angle = 0.0f,
56 const SkCurve& strength = 0.0f,
Brian Osmand5c57fe2019-02-22 11:48:18 -050057 bool force = true,
Brian Osman0c486812019-02-26 10:02:15 -050058 SkParticleFrame frame = kWorld_ParticleFrame)
Brian Osman8b6283f2019-02-14 16:55:21 -050059 : fAngle(angle)
60 , fStrength(strength)
Brian Osmand5c57fe2019-02-22 11:48:18 -050061 , fForce(force)
Brian Osman0c486812019-02-26 10:02:15 -050062 , fFrame(frame) {}
Brian Osman7c979f52019-02-12 13:27:51 -050063
Brian Osman8b6283f2019-02-14 16:55:21 -050064 REFLECTED(SkLinearVelocityAffector, SkParticleAffector)
Brian Osman7c979f52019-02-12 13:27:51 -050065
Brian Osman14a67a32019-02-25 14:30:44 -050066 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
67 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -050068 float angle = fAngle.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman14a67a32019-02-25 14:30:44 -050069 SkScalar c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
Brian Osman0c486812019-02-26 10:02:15 -050070 SkVector heading = get_heading(ps[i], static_cast<SkParticleFrame>(fFrame));
Brian Osman14a67a32019-02-25 14:30:44 -050071 SkScalar c = heading.fX * c_local - heading.fY * s_local;
72 SkScalar s = heading.fX * s_local + heading.fY * c_local;
Brian Osmane5d532e2019-02-26 14:58:40 -050073 float strength = fStrength.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman14a67a32019-02-25 14:30:44 -050074 SkVector force = { c * strength, s * strength };
75 if (fForce) {
76 ps[i].fVelocity.fLinear += force * params.fDeltaTime;
77 } else {
78 ps[i].fVelocity.fLinear = force;
79 }
Brian Osman8b6283f2019-02-14 16:55:21 -050080 }
Brian Osman7c979f52019-02-12 13:27:51 -050081 }
82
83 void visitFields(SkFieldVisitor* v) override {
Brian Osman1b20cd82019-02-25 14:15:02 -050084 SkParticleAffector::visitFields(v);
Brian Osman7c979f52019-02-12 13:27:51 -050085 v->visit("Force", fForce);
Brian Osmane5d532e2019-02-26 14:58:40 -050086 v->visit("Frame", fFrame, gParticleFrameMapping, SK_ARRAY_COUNT(gParticleFrameMapping));
Brian Osman7c979f52019-02-12 13:27:51 -050087 v->visit("Angle", fAngle);
88 v->visit("Strength", fStrength);
Brian Osman7c979f52019-02-12 13:27:51 -050089 }
90
91private:
92 SkCurve fAngle;
93 SkCurve fStrength;
Brian Osman8b6283f2019-02-14 16:55:21 -050094 bool fForce;
Brian Osman0c486812019-02-26 10:02:15 -050095 int fFrame;
96};
97
98class SkAngularVelocityAffector : public SkParticleAffector {
99public:
100 SkAngularVelocityAffector(const SkCurve& strength = 0.0f, bool force = true)
101 : fStrength(strength)
102 , fForce(force) {}
103
104 REFLECTED(SkAngularVelocityAffector, SkParticleAffector)
105
106 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
107 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500108 float strength = fStrength.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman0c486812019-02-26 10:02:15 -0500109 if (fForce) {
110 ps[i].fVelocity.fAngular += strength * params.fDeltaTime;
111 } else {
112 ps[i].fVelocity.fAngular = strength;
113 }
114 }
115 }
116
117 void visitFields(SkFieldVisitor* v) override {
118 SkParticleAffector::visitFields(v);
119 v->visit("Force", fForce);
120 v->visit("Strength", fStrength);
121 }
122
123private:
124 SkCurve fStrength;
125 bool fForce;
Brian Osman7c979f52019-02-12 13:27:51 -0500126};
127
128class SkPointForceAffector : public SkParticleAffector {
129public:
130 SkPointForceAffector(SkPoint point = { 0.0f, 0.0f }, SkScalar constant = 0.0f,
131 SkScalar invSquare = 0.0f)
132 : fPoint(point), fConstant(constant), fInvSquare(invSquare) {}
133
134 REFLECTED(SkPointForceAffector, SkParticleAffector)
135
Brian Osman14a67a32019-02-25 14:30:44 -0500136 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
137 for (int i = 0; i < count; ++i) {
138 SkVector toPoint = fPoint - ps[i].fPose.fPosition;
139 SkScalar lenSquare = toPoint.dot(toPoint);
140 toPoint.normalize();
141 ps[i].fVelocity.fLinear +=
142 toPoint * (fConstant + (fInvSquare / lenSquare)) * params.fDeltaTime;
143 }
Brian Osman7c979f52019-02-12 13:27:51 -0500144 }
145
146 void visitFields(SkFieldVisitor* v) override {
Brian Osman1b20cd82019-02-25 14:15:02 -0500147 SkParticleAffector::visitFields(v);
Brian Osman7c979f52019-02-12 13:27:51 -0500148 v->visit("Point", fPoint);
149 v->visit("Constant", fConstant);
150 v->visit("InvSquare", fInvSquare);
151 }
152
153private:
154 SkPoint fPoint;
155 SkScalar fConstant;
156 SkScalar fInvSquare;
157};
158
Brian Osman0c486812019-02-26 10:02:15 -0500159class SkOrientationAffector : public SkParticleAffector {
Brian Osman7c979f52019-02-12 13:27:51 -0500160public:
Brian Osman0c486812019-02-26 10:02:15 -0500161 SkOrientationAffector(const SkCurve& angle = 0.0f,
162 SkParticleFrame frame = kLocal_ParticleFrame)
163 : fAngle(angle)
164 , fFrame(frame) {}
Brian Osman7c979f52019-02-12 13:27:51 -0500165
Brian Osman0c486812019-02-26 10:02:15 -0500166 REFLECTED(SkOrientationAffector, SkParticleAffector)
Brian Osman7c979f52019-02-12 13:27:51 -0500167
Brian Osman14a67a32019-02-25 14:30:44 -0500168 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
169 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500170 float angle = fAngle.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman0c486812019-02-26 10:02:15 -0500171 SkScalar c_local, s_local = SkScalarSinCos(SkDegreesToRadians(angle), &c_local);
172 SkVector heading = get_heading(ps[i], static_cast<SkParticleFrame>(fFrame));
173 ps[i].fPose.fHeading.set(heading.fX * c_local - heading.fY * s_local,
174 heading.fX * s_local + heading.fY * c_local);
Brian Osman7c979f52019-02-12 13:27:51 -0500175 }
Brian Osman7c979f52019-02-12 13:27:51 -0500176 }
177
Brian Osman1b20cd82019-02-25 14:15:02 -0500178 void visitFields(SkFieldVisitor *v) override {
179 SkParticleAffector::visitFields(v);
Brian Osmane5d532e2019-02-26 14:58:40 -0500180 v->visit("Frame", fFrame, gParticleFrameMapping, SK_ARRAY_COUNT(gParticleFrameMapping));
Brian Osman0c486812019-02-26 10:02:15 -0500181 v->visit("Angle", fAngle);
Brian Osman1b20cd82019-02-25 14:15:02 -0500182 }
Brian Osman0c486812019-02-26 10:02:15 -0500183
184private:
185 SkCurve fAngle;
186 int fFrame;
Brian Osman7c979f52019-02-12 13:27:51 -0500187};
188
Brian Osman3d76d1b2019-02-28 15:48:05 -0500189class SkPositionInCircleAffector : public SkParticleAffector {
190public:
191 SkPositionInCircleAffector(const SkCurve& x = 0.0f, const SkCurve& y = 0.0f,
192 const SkCurve& radius = 0.0f, bool setHeading = true)
193 : fX(x)
194 , fY(y)
195 , fRadius(radius)
196 , fSetHeading(setHeading) {}
197
198 REFLECTED(SkPositionInCircleAffector, SkParticleAffector)
199
200 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
201 for (int i = 0; i < count; ++i) {
202 SkVector v;
203 do {
204 v.fX = ps[i].fRandom.nextSScalar1();
205 v.fY = ps[i].fRandom.nextSScalar1();
206 } while (v.dot(v) > 1);
207
208 SkPoint center = { fX.eval(ps[i].fAge, ps[i].fRandom),
209 fY.eval(ps[i].fAge, ps[i].fRandom) };
210 SkScalar radius = fRadius.eval(ps[i].fAge, ps[i].fRandom);
211 ps[i].fPose.fPosition = center + (v * radius);
212 if (fSetHeading) {
213 if (!v.normalize()) {
214 v.set(0, -1);
215 }
216 ps[i].fPose.fHeading = v;
217 }
218 }
219 }
220
221 void visitFields(SkFieldVisitor* v) override {
222 SkParticleAffector::visitFields(v);
223 v->visit("SetHeading", fSetHeading);
224 v->visit("X", fX);
225 v->visit("Y", fY);
226 v->visit("Radius", fRadius);
227 }
228
229private:
230 SkCurve fX;
231 SkCurve fY;
232 SkCurve fRadius;
233 bool fSetHeading;
234};
235
236class SkPositionOnPathAffector : public SkParticleAffector {
237public:
238 SkPositionOnPathAffector(const char* path = "", bool setHeading = true, bool random = true)
239 : fPath(path)
240 , fSetHeading(setHeading)
241 , fRandom(random) {
242 this->rebuild();
243 }
244
245 REFLECTED(SkPositionOnPathAffector, SkParticleAffector)
246
247 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
248 if (fContours.empty()) {
249 return;
250 }
251
252 for (int i = 0; i < count; ++i) {
253 float t = fRandom ? ps[i].fRandom.nextF() : ps[i].fAge;
254 SkScalar len = fTotalLength * t;
255 int idx = 0;
256 while (idx < fContours.count() && len > fContours[idx]->length()) {
257 len -= fContours[idx++]->length();
258 }
259 SkVector localXAxis;
260 if (!fContours[idx]->getPosTan(len, &ps[i].fPose.fPosition, &localXAxis)) {
261 ps[i].fPose.fPosition = { 0, 0 };
262 localXAxis = { 1, 0 };
263 }
264 if (fSetHeading) {
265 ps[i].fPose.fHeading.set(localXAxis.fY, -localXAxis.fX);
266 }
267 }
268 }
269
270 void visitFields(SkFieldVisitor* v) override {
271 SkString oldPath = fPath;
272
273 SkParticleAffector::visitFields(v);
274 v->visit("SetHeading", fSetHeading);
275 v->visit("Random", fRandom);
276 v->visit("Path", fPath);
277
278 if (fPath != oldPath) {
279 this->rebuild();
280 }
281 }
282
283private:
284 SkString fPath;
285 bool fSetHeading;
286 bool fRandom;
287
288 void rebuild() {
289 SkPath path;
290 if (!SkParsePath::FromSVGString(fPath.c_str(), &path)) {
291 return;
292 }
293
294 fTotalLength = 0;
295 fContours.reset();
296
297 SkContourMeasureIter iter(path, false);
298 while (auto contour = iter.next()) {
299 fContours.push_back(contour);
300 fTotalLength += contour->length();
301 }
302 }
303
304 // Cached
305 SkScalar fTotalLength;
306 SkTArray<sk_sp<SkContourMeasure>> fContours;
307};
308
309class SkPositionOnTextAffector : public SkParticleAffector {
310public:
311 SkPositionOnTextAffector(const char* text = "", SkScalar fontSize = 96, bool setHeading = true,
312 bool random = true)
313 : fText(text)
314 , fFontSize(fontSize)
315 , fSetHeading(setHeading)
316 , fRandom(random) {
317 this->rebuild();
318 }
319
320 REFLECTED(SkPositionOnTextAffector, SkParticleAffector)
321
322 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
323 if (fContours.empty()) {
324 return;
325 }
326
327 // TODO: Refactor to share code with PositionOnPathAffector
328 for (int i = 0; i < count; ++i) {
329 float t = fRandom ? ps[i].fRandom.nextF() : ps[i].fAge;
330 SkScalar len = fTotalLength * t;
331 int idx = 0;
332 while (idx < fContours.count() && len > fContours[idx]->length()) {
333 len -= fContours[idx++]->length();
334 }
335 SkVector localXAxis;
336 if (!fContours[idx]->getPosTan(len, &ps[i].fPose.fPosition, &localXAxis)) {
337 ps[i].fPose.fPosition = { 0, 0 };
338 localXAxis = { 1, 0 };
339 }
340 if (fSetHeading) {
341 ps[i].fPose.fHeading.set(localXAxis.fY, -localXAxis.fX);
342 }
343 }
344 }
345
346 void visitFields(SkFieldVisitor* v) override {
347 SkString oldText = fText;
348 SkScalar oldSize = fFontSize;
349
350 SkParticleAffector::visitFields(v);
351 v->visit("SetHeading", fSetHeading);
352 v->visit("Random", fRandom);
353 v->visit("Text", fText);
354 v->visit("FontSize", fFontSize);
355
356 if (fText != oldText || fFontSize != oldSize) {
357 this->rebuild();
358 }
359 }
360
361private:
362 SkString fText;
363 SkScalar fFontSize;
364 bool fSetHeading;
365 bool fRandom;
366
367 void rebuild() {
368 fTotalLength = 0;
369 fContours.reset();
370
371 if (fText.isEmpty()) {
372 return;
373 }
374
375 SkFont font(sk_tool_utils::create_portable_typeface());
376 font.setSize(fFontSize);
377 SkPath path;
378 SkTextUtils::GetPath(fText.c_str(), fText.size(), kUTF8_SkTextEncoding, 0, 0, font, &path);
379 SkContourMeasureIter iter(path, false);
380 while (auto contour = iter.next()) {
381 fContours.push_back(contour);
382 fTotalLength += contour->length();
383 }
384 }
385
386 // Cached
387 SkScalar fTotalLength;
388 SkTArray<sk_sp<SkContourMeasure>> fContours;
389};
390
Brian Osman8b6283f2019-02-14 16:55:21 -0500391class SkSizeAffector : public SkParticleAffector {
Brian Osman7c979f52019-02-12 13:27:51 -0500392public:
Brian Osman8b6283f2019-02-14 16:55:21 -0500393 SkSizeAffector(const SkCurve& curve = 1.0f) : fCurve(curve) {}
Brian Osman7c979f52019-02-12 13:27:51 -0500394
Brian Osman8b6283f2019-02-14 16:55:21 -0500395 REFLECTED(SkSizeAffector, SkParticleAffector)
Brian Osman7c979f52019-02-12 13:27:51 -0500396
Brian Osman14a67a32019-02-25 14:30:44 -0500397 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
398 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500399 ps[i].fPose.fScale = fCurve.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman14a67a32019-02-25 14:30:44 -0500400 }
Brian Osman7c979f52019-02-12 13:27:51 -0500401 }
402
403 void visitFields(SkFieldVisitor* v) override {
Brian Osman1b20cd82019-02-25 14:15:02 -0500404 SkParticleAffector::visitFields(v);
Brian Osman8b6283f2019-02-14 16:55:21 -0500405 v->visit("Curve", fCurve);
Brian Osman7c979f52019-02-12 13:27:51 -0500406 }
407
408private:
Brian Osman8b6283f2019-02-14 16:55:21 -0500409 SkCurve fCurve;
Brian Osman7c979f52019-02-12 13:27:51 -0500410};
411
Brian Osman125daa42019-02-20 12:25:20 -0500412class SkFrameAffector : public SkParticleAffector {
413public:
414 SkFrameAffector(const SkCurve& curve = 1.0f) : fCurve(curve) {}
415
416 REFLECTED(SkFrameAffector, SkParticleAffector)
417
Brian Osman14a67a32019-02-25 14:30:44 -0500418 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
419 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500420 ps[i].fFrame = fCurve.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman14a67a32019-02-25 14:30:44 -0500421 }
Brian Osman125daa42019-02-20 12:25:20 -0500422 }
423
424 void visitFields(SkFieldVisitor* v) override {
Brian Osman1b20cd82019-02-25 14:15:02 -0500425 SkParticleAffector::visitFields(v);
Brian Osman125daa42019-02-20 12:25:20 -0500426 v->visit("Curve", fCurve);
427 }
428
429private:
430 SkCurve fCurve;
431};
432
433class SkColorAffector : public SkParticleAffector {
434public:
435 SkColorAffector(const SkColorCurve& curve = SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f })
436 : fCurve(curve) {}
437
438 REFLECTED(SkColorAffector, SkParticleAffector)
439
Brian Osman14a67a32019-02-25 14:30:44 -0500440 void onApply(SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
441 for (int i = 0; i < count; ++i) {
Brian Osmane5d532e2019-02-26 14:58:40 -0500442 ps[i].fColor = fCurve.eval(ps[i].fAge, ps[i].fRandom);
Brian Osman14a67a32019-02-25 14:30:44 -0500443 }
Brian Osman125daa42019-02-20 12:25:20 -0500444 }
445
446 void visitFields(SkFieldVisitor* v) override {
Brian Osman1b20cd82019-02-25 14:15:02 -0500447 SkParticleAffector::visitFields(v);
Brian Osman125daa42019-02-20 12:25:20 -0500448 v->visit("Curve", fCurve);
449 }
450
451private:
452 SkColorCurve fCurve;
453};
454
Brian Osman7c979f52019-02-12 13:27:51 -0500455void SkParticleAffector::RegisterAffectorTypes() {
456 REGISTER_REFLECTED(SkParticleAffector);
Brian Osman8b6283f2019-02-14 16:55:21 -0500457 REGISTER_REFLECTED(SkLinearVelocityAffector);
Brian Osman0c486812019-02-26 10:02:15 -0500458 REGISTER_REFLECTED(SkAngularVelocityAffector);
Brian Osman7c979f52019-02-12 13:27:51 -0500459 REGISTER_REFLECTED(SkPointForceAffector);
Brian Osman0c486812019-02-26 10:02:15 -0500460 REGISTER_REFLECTED(SkOrientationAffector);
Brian Osman3d76d1b2019-02-28 15:48:05 -0500461 REGISTER_REFLECTED(SkPositionInCircleAffector);
462 REGISTER_REFLECTED(SkPositionOnPathAffector);
463 REGISTER_REFLECTED(SkPositionOnTextAffector);
Brian Osman8b6283f2019-02-14 16:55:21 -0500464 REGISTER_REFLECTED(SkSizeAffector);
Brian Osman125daa42019-02-20 12:25:20 -0500465 REGISTER_REFLECTED(SkFrameAffector);
466 REGISTER_REFLECTED(SkColorAffector);
Brian Osman7c979f52019-02-12 13:27:51 -0500467}
468
Brian Osman8b6283f2019-02-14 16:55:21 -0500469sk_sp<SkParticleAffector> SkParticleAffector::MakeLinearVelocity(const SkCurve& angle,
470 const SkCurve& strength,
Brian Osmand5c57fe2019-02-22 11:48:18 -0500471 bool force,
Brian Osman0c486812019-02-26 10:02:15 -0500472 SkParticleFrame frame) {
473 return sk_sp<SkParticleAffector>(new SkLinearVelocityAffector(angle, strength, force, frame));
474}
475
476sk_sp<SkParticleAffector> SkParticleAffector::MakeAngularVelocity(const SkCurve& strength,
477 bool force) {
478 return sk_sp<SkParticleAffector>(new SkAngularVelocityAffector(strength, force));
Brian Osman7c979f52019-02-12 13:27:51 -0500479}
480
Brian Osman8b6283f2019-02-14 16:55:21 -0500481sk_sp<SkParticleAffector> SkParticleAffector::MakePointForce(SkPoint point, SkScalar constant,
482 SkScalar invSquare) {
Brian Osman7c979f52019-02-12 13:27:51 -0500483 return sk_sp<SkParticleAffector>(new SkPointForceAffector(point, constant, invSquare));
484}
485
Brian Osman0c486812019-02-26 10:02:15 -0500486sk_sp<SkParticleAffector> SkParticleAffector::MakeOrientation(const SkCurve& angle,
487 SkParticleFrame frame) {
488 return sk_sp<SkParticleAffector>(new SkOrientationAffector(angle, frame));
Brian Osman7c979f52019-02-12 13:27:51 -0500489}
Brian Osman8b6283f2019-02-14 16:55:21 -0500490
Brian Osman125daa42019-02-20 12:25:20 -0500491sk_sp<SkParticleAffector> SkParticleAffector::MakeSize(const SkCurve& curve) {
Brian Osman8b6283f2019-02-14 16:55:21 -0500492 return sk_sp<SkParticleAffector>(new SkSizeAffector(curve));
493}
Brian Osman125daa42019-02-20 12:25:20 -0500494
495sk_sp<SkParticleAffector> SkParticleAffector::MakeFrame(const SkCurve& curve) {
496 return sk_sp<SkParticleAffector>(new SkFrameAffector(curve));
497}
498
499sk_sp<SkParticleAffector> SkParticleAffector::MakeColor(const SkColorCurve& curve) {
500 return sk_sp<SkParticleAffector>(new SkColorAffector(curve));
501}