Flutter Engine
The Flutter Engine
SVGPongSlide.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
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
20#include "src/base/SkRandom.h"
22#include "tools/viewer/Slide.h"
23
24namespace {
25
26static const SkRect kBounds = SkRect::MakeLTRB(0.1f, 0.1f, 0.9f, 0.9f);
27static const SkSize kPaddleSize = SkSize::Make(0.03f, 0.1f);
28static const SkScalar kBallSize = 0.04f;
29static const SkScalar kShadowOpacity = 0.40f;
30static const SkScalar kShadowParallax = 0.04f;
31static const SkScalar kBackgroundStroke = 0.01f;
32static const uint32_t kBackgroundDashCount = 20;
33
34static const SkScalar kBallSpeedMax = 0.0020f;
35static const SkScalar kBallSpeedMin = 0.0005f;
36static const SkScalar kBallSpeedFuzz = 0.0002f;
37
38static const SkScalar kTimeScaleMin = 0.0f;
39static const SkScalar kTimeScaleMax = 5.0f;
40
41// Box the value within [min, max), by applying infinite reflection on the interval endpoints.
42SkScalar box_reflect(SkScalar v, SkScalar min, SkScalar max) {
43 const SkScalar intervalLen = max - min;
44 SkASSERT(intervalLen > 0);
45
46 // f(v) is periodic in 2 * intervalLen: one normal progression + one reflection
47 const SkScalar P = intervalLen * 2;
48 // relative to P origin
49 const SkScalar vP = v - min;
50 // map to [0, P)
51 const SkScalar vMod = (vP < 0) ? P - SkScalarMod(-vP, P) : SkScalarMod(vP, P);
52 // reflect if needed, to map to [0, intervalLen)
53 const SkScalar vInterval = vMod < intervalLen ? vMod : P - vMod;
54 // finally, reposition relative to min
55 return vInterval + min;
56}
57
58// Compute <t, y> for the trajectory intersection with the next vertical edge.
59std::tuple<SkScalar, SkScalar> find_yintercept(const SkPoint& pos, const SkVector& spd,
60 const SkRect& box) {
61 const SkScalar edge = spd.fX > 0 ? box.fRight : box.fLeft;
62 const SkScalar t = (edge - pos.fX) / spd.fX;
63 SkASSERT(t >= 0);
64 const SkScalar dY = t * spd.fY;
65
66 return std::make_tuple(t, box_reflect(pos.fY + dY, box.fTop, box.fBottom));
67}
68
69void update_pos(const sk_sp<sksg::RRect>& rr, const SkPoint& pos) {
70 // TODO: position setters on RRect?
71
72 const auto r = rr->getRRect().rect();
73 const auto offsetX = pos.x() - r.x(),
74 offsetY = pos.y() - r.y();
75 rr->setRRect(rr->getRRect().makeOffset(offsetX, offsetY));
76}
77
78} // namespace
79
80class PongSlide final : public Slide {
81public:
82 PongSlide() { fName = "SGPong"; }
83
84 void load(SkScalar w, SkScalar h) override {
85 const SkRect fieldBounds = kBounds.makeOutset(kBallSize / 2, kBallSize / 2);
86 const SkRRect ball = SkRRect::MakeOval(SkRect::MakeWH(kBallSize, kBallSize));
87 const SkRRect paddle = SkRRect::MakeRectXY(SkRect::MakeWH(kPaddleSize.width(),
88 kPaddleSize.height()),
89 kPaddleSize.width() / 2,
90 kPaddleSize.width() / 2);
91 fBall.initialize(ball,
92 SkPoint::Make(kBounds.centerX(), kBounds.centerY()),
93 SkVector::Make(fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax),
94 fRand.nextRangeScalar(kBallSpeedMin, kBallSpeedMax)));
95 fPaddle0.initialize(paddle,
96 SkPoint::Make(fieldBounds.left() - kPaddleSize.width() / 2,
97 fieldBounds.centerY()),
98 SkVector::Make(0, 0));
99 fPaddle1.initialize(paddle,
100 SkPoint::Make(fieldBounds.right() + kPaddleSize.width() / 2,
101 fieldBounds.centerY()),
102 SkVector::Make(0, 0));
103
104 // Background decoration.
105 SkPathBuilder bgPath;
106 bgPath.moveTo(kBounds.left() , fieldBounds.top())
107 .lineTo(kBounds.right(), fieldBounds.top())
108 .moveTo(kBounds.left() , fieldBounds.bottom())
109 .lineTo(kBounds.right(), fieldBounds.bottom());
110 // TODO: stroke-dash support would come in handy right about now.
111 for (uint32_t i = 0; i < kBackgroundDashCount; ++i) {
112 bgPath.moveTo(kBounds.centerX(),
113 kBounds.top() + (i + 0.25f) * kBounds.height() / kBackgroundDashCount)
114 .lineTo(kBounds.centerX(),
115 kBounds.top() + (i + 0.75f) * kBounds.height() / kBackgroundDashCount);
116 }
117
118 auto bg_path = sksg::Path::Make(bgPath.detach());
119 auto bg_paint = sksg::Color::Make(SK_ColorBLACK);
120 bg_paint->setStyle(SkPaint::kStroke_Style);
121 bg_paint->setStrokeWidth(kBackgroundStroke);
122
123 auto ball_paint = sksg::Color::Make(SK_ColorGREEN),
124 paddle0_paint = sksg::Color::Make(SK_ColorBLUE),
125 paddle1_paint = sksg::Color::Make(SK_ColorRED),
126 shadow_paint = sksg::Color::Make(SK_ColorBLACK);
127 ball_paint->setAntiAlias(true);
128 paddle0_paint->setAntiAlias(true);
129 paddle1_paint->setAntiAlias(true);
130 shadow_paint->setAntiAlias(true);
131 shadow_paint->setOpacity(kShadowOpacity);
132
133 // Build the scene graph.
134 auto group = sksg::Group::Make();
135 group->addChild(sksg::Draw::Make(std::move(bg_path), std::move(bg_paint)));
136 group->addChild(sksg::Draw::Make(fPaddle0.shadowNode, shadow_paint));
137 group->addChild(sksg::Draw::Make(fPaddle1.shadowNode, shadow_paint));
138 group->addChild(sksg::Draw::Make(fBall.shadowNode, shadow_paint));
139 group->addChild(sksg::Draw::Make(fPaddle0.objectNode, paddle0_paint));
140 group->addChild(sksg::Draw::Make(fPaddle1.objectNode, paddle1_paint));
141 group->addChild(sksg::Draw::Make(fBall.objectNode, ball_paint));
142
143 // Handle everything in a normalized 1x1 space.
144 fContentMatrix = sksg::Matrix<SkMatrix>::Make(
146 SkRect::MakeWH(w, h)));
147 auto root = sksg::TransformEffect::Make(std::move(group), fContentMatrix);
148 fScene = sksg::Scene::Make(std::move(root));
149
150 // Off we go.
151 this->updatePaddleStrategy();
152 }
153
154 bool onChar(SkUnichar uni) override {
155 switch (uni) {
156 case '[':
157 fTimeScale = SkTPin(fTimeScale - 0.1f, kTimeScaleMin, kTimeScaleMax);
158 return true;
159 case ']':
160 fTimeScale = SkTPin(fTimeScale + 0.1f, kTimeScaleMin, kTimeScaleMax);
161 return true;
162 case 'I':
163 fShowInval = !fShowInval;
164 return true;
165 default:
166 break;
167 }
168 return false;
169 }
170
171 void resize(SkScalar w, SkScalar h) override {
172 if (fContentMatrix) {
173 fContentMatrix->setMatrix(SkMatrix::RectToRect(SkRect::MakeWH(1, 1),
174 SkRect::MakeWH(w, h)));
175 }
176 }
177
178 void draw(SkCanvas* canvas) override {
180 fScene->render(canvas);
181
182 if (fShowInval) {
183 SkPaint fill, stroke;
184 fill.setAntiAlias(true);
185 fill.setColor(0x40ff0000);
186 stroke.setAntiAlias(true);
187 stroke.setColor(0xffff0000);
189
190 for (const auto& r : ic) {
191 canvas->drawRect(r, fill);
192 canvas->drawRect(r, stroke);
193 }
194 }
195 }
196
197 bool animate(double nanos) override {
198 // onAnimate may fire before the first draw.
199 if (fScene) {
200 SkScalar dt = (TimeUtils::NanosToMSec(nanos) - fLastTick) * fTimeScale;
201 fLastTick = TimeUtils::NanosToMSec(nanos);
202
203 fPaddle0.posTick(dt);
204 fPaddle1.posTick(dt);
205 fBall.posTick(dt);
206
207 this->enforceConstraints();
208
209 fPaddle0.updateDom();
210 fPaddle1.updateDom();
211 fBall.updateDom();
212 }
213 return true;
214 }
215
216private:
217 struct Object {
218 void initialize(const SkRRect& rrect, const SkPoint& p, const SkVector& s) {
219 objectNode = sksg::RRect::Make(rrect);
220 shadowNode = sksg::RRect::Make(rrect);
221
222 pos = p;
223 spd = s;
225 }
226
227 void posTick(SkScalar dt) {
228 pos += spd * dt;
229 }
230
231 void updateDom() {
232 const SkPoint corner = pos - SkPoint::Make(size.width() / 2, size.height() / 2);
233 update_pos(objectNode, corner);
234
235 // Simulate parallax shadow for a centered light source.
236 SkPoint shadowOffset = pos - SkPoint::Make(kBounds.centerX(), kBounds.centerY());
237 shadowOffset.scale(kShadowParallax);
238 const SkPoint shadowCorner = corner + shadowOffset;
239
240 update_pos(shadowNode, shadowCorner);
241 }
242
243 sk_sp<sksg::RRect> objectNode,
244 shadowNode;
245 SkPoint pos;
246 SkVector spd;
247 SkSize size;
248 };
249
250 void enforceConstraints() {
251 // Perfect vertical reflection.
252 if (fBall.pos.fY < kBounds.fTop || fBall.pos.fY >= kBounds.fBottom) {
253 fBall.spd.fY = -fBall.spd.fY;
254 fBall.pos.fY = box_reflect(fBall.pos.fY, kBounds.fTop, kBounds.fBottom);
255 }
256
257 // Horizontal bounce - introduces a speed fuzz.
258 if (fBall.pos.fX < kBounds.fLeft || fBall.pos.fX >= kBounds.fRight) {
259 fBall.spd.fX = this->fuzzBallSpeed(-fBall.spd.fX);
260 fBall.spd.fY = this->fuzzBallSpeed(fBall.spd.fY);
261 fBall.pos.fX = box_reflect(fBall.pos.fX, kBounds.fLeft, kBounds.fRight);
262 this->updatePaddleStrategy();
263 }
264 }
265
266 SkScalar fuzzBallSpeed(SkScalar spd) {
267 // The speed limits are absolute values.
268 const SkScalar sign = spd >= 0 ? 1.0f : -1.0f;
269 const SkScalar fuzzed = fabs(spd) + fRand.nextRangeScalar(-kBallSpeedFuzz, kBallSpeedFuzz);
270
271 return sign * SkTPin(fuzzed, kBallSpeedMin, kBallSpeedMax);
272 }
273
274 void updatePaddleStrategy() {
275 Object* pitcher = fBall.spd.fX > 0 ? &fPaddle0 : &fPaddle1;
276 Object* catcher = fBall.spd.fX > 0 ? &fPaddle1 : &fPaddle0;
277
278 SkScalar t, yIntercept;
279 std::tie(t, yIntercept) = find_yintercept(fBall.pos, fBall.spd, kBounds);
280
281 // The pitcher aims for a neutral/centered position.
282 pitcher->spd.fY = (kBounds.centerY() - pitcher->pos.fY) / t;
283
284 // The catcher goes for the ball. Duh.
285 catcher->spd.fY = (yIntercept - catcher->pos.fY) / t;
286 }
287
288 std::unique_ptr<sksg::Scene> fScene;
289 sk_sp<sksg::Matrix<SkMatrix>> fContentMatrix;
290 Object fPaddle0, fPaddle1, fBall;
291 SkRandom fRand;
292
293 SkMSec fLastTick = 0;
294 SkScalar fTimeScale = 1.0f;
295 bool fShowInval = false;
296};
297
298DEF_SLIDE( return new PongSlide(); )
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
constexpr SkColor SK_ColorBLUE
Definition: SkColor.h:135
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition: SkColor.h:131
static int sign(SkScalar x)
Definition: SkPath.cpp:2205
#define SkScalarMod(x, y)
Definition: SkScalar.h:41
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
int32_t SkUnichar
Definition: SkTypes.h:175
uint32_t SkMSec
Definition: SkTypes.h:184
#define DEF_SLIDE(code)
Definition: Slide.h:25
bool onChar(SkUnichar uni) override
void draw(SkCanvas *canvas) override
bool animate(double nanos) override
void load(SkScalar w, SkScalar h) override
void resize(SkScalar w, SkScalar h) override
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition: SkMatrix.h:157
void setColor(SkColor color)
Definition: SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition: SkPaint.h:170
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
SkPathBuilder & lineTo(SkPoint pt)
SkPathBuilder & moveTo(SkPoint pt)
static SkRRect MakeOval(const SkRect &oval)
Definition: SkRRect.h:162
SkScalar width() const
Definition: SkRRect.h:95
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.h:180
SkScalar height() const
Definition: SkRRect.h:102
SkScalar nextRangeScalar(SkScalar min, SkScalar max)
Definition: SkRandom.h:106
Definition: Slide.h:29
SkString fName
Definition: Slide.h:54
static sk_sp< Color > Make(SkColor c)
Definition: SkSGPaint.cpp:44
static sk_sp< Draw > Make(sk_sp< GeometryNode > geo, sk_sp< PaintNode > paint)
Definition: SkSGDraw.h:35
static sk_sp< Group > Make()
Definition: SkSGGroup.h:31
static sk_sp< Matrix > Make(const T &m)
Definition: SkSGTransform.h:70
static sk_sp< Path > Make()
Definition: SkSGPath.h:31
static sk_sp< RRect > Make()
Definition: SkSGRect.h:82
static std::unique_ptr< Scene > Make(sk_sp< RenderNode > root)
Definition: SkSGScene.cpp:16
static sk_sp< TransformEffect > Make(sk_sp< RenderNode > child, sk_sp< Transform > transform)
Definition: SkSGTransform.h:97
float SkScalar
Definition: extension.cpp:12
struct MyStruct s
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SkRRect rrect
Definition: SkRecords.h:232
static SkMSec NanosToMSec(double nanos)
Definition: TimeUtils.h:16
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
string root
Definition: scale_cpu.py:20
SkScalar offsetX
SkScalar w
SkScalar offsetY
SkScalar h
float fX
x-axis value
Definition: SkPoint_impl.h:164
static constexpr SkPoint Make(float x, float y)
Definition: SkPoint_impl.h:173
void scale(float scale, SkPoint *dst) const
Definition: SkPoint.cpp:17
float fY
y-axis value
Definition: SkPoint_impl.h:165
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
constexpr float right() const
Definition: SkRect.h:748
constexpr float centerY() const
Definition: SkRect.h:785
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
constexpr float bottom() const
Definition: SkRect.h:755
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
Definition: SkSize.h:52
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition: SkSize.h:56
SkScalar width() const
Definition: SkSize.h:76
SkScalar height() const
Definition: SkSize.h:77