Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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;
224 size = SkSize::Make(rrect.width(), rrect.height());
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:2141
#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)
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
void setStyle(Style style)
Definition SkPaint.cpp:105
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)
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)
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
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
SkScalar offsetX
SkScalar w
SkScalar offsetY
SkScalar h
float fX
x-axis value
static constexpr SkPoint Make(float x, float y)
void scale(float scale, SkPoint *dst) const
Definition SkPoint.cpp:17
float fY
y-axis value
constexpr float y() const
constexpr float x() const
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
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