Flutter Engine
The Flutter Engine
TouchGesture.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2010 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
9
14#include "src/base/SkTime.h"
15
16#include <algorithm>
17#include <cmath>
18
19#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER true
20
22
24 if (speed > MAX_FLING_SPEED) {
25 speed = MAX_FLING_SPEED;
26 }
27 return speed;
28}
29
30static double getseconds() {
31 return SkTime::GetMSecs() * 0.001;
32}
33
34// returns +1 or -1, depending on the sign of x
35// returns +1 if z is zero
38 if (x < 0) {
39 sign = -sign;
40 }
41 return sign;
42}
43
44static void unit_axis_align(SkVector* unit) {
45 const SkScalar TOLERANCE = SkDoubleToScalar(0.15);
46 if (SkScalarAbs(unit->fX) < TOLERANCE) {
47 unit->fX = 0;
48 unit->fY = SkScalarSignNonZero(unit->fY);
49 } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
50 unit->fX = SkScalarSignNonZero(unit->fX);
51 unit->fY = 0;
52 }
53}
54
55void TouchGesture::FlingState::reset(float sx, float sy) {
56 fActive = true;
57 fDirection.set(sx, sy);
58 fSpeed0 = SkPoint::Normalize(&fDirection);
59 fSpeed0 = pin_max_fling(fSpeed0);
60 fTime0 = getseconds();
61
62 unit_axis_align(&fDirection);
63// printf("---- speed %g dir %g %g\n", fSpeed0, fDirection.fX, fDirection.fY);
64}
65
66bool TouchGesture::FlingState::evaluateMatrix(SkMatrix* matrix) {
67 if (!fActive) {
68 return false;
69 }
70
71 const float t = (float)(getseconds() - fTime0);
72 const float MIN_SPEED = 2;
73 const float K0 = 5;
74 const float K1 = 0.02f;
75 const float speed = fSpeed0 * (std::exp(- K0 * t) - K1);
76 if (speed <= MIN_SPEED) {
77 fActive = false;
78 return false;
79 }
80 float dist = (fSpeed0 - speed) / K0;
81
82// printf("---- time %g speed %g dist %g\n", t, speed, dist);
83 float tx = fDirection.fX * dist;
84 float ty = fDirection.fY * dist;
86 tx = (float)sk_float_round2int(tx);
87 ty = (float)sk_float_round2int(ty);
88 }
89 matrix->setTranslate(tx, ty);
90// printf("---- evaluate (%g %g)\n", tx, ty);
91
92 return true;
93}
94
95///////////////////////////////////////////////////////////////////////////////
96
97static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
98static const float MAX_DBL_TAP_DISTANCE = 100;
99static const float MAX_JITTER_RADIUS = 2;
100
101// if true, then ignore the touch-move, 'cause its probably just jitter
102static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
103 return std::fabs(x0 - x1) <= MAX_JITTER_RADIUS &&
104 std::fabs(y0 - y1) <= MAX_JITTER_RADIUS;
105}
106
107///////////////////////////////////////////////////////////////////////////////
108
110 this->reset();
111}
112
114}
115
117 fIsTransLimited = false;
118 fTouches.reset();
119 fState = kEmpty_State;
120 fLocalM.reset();
121
122 fLastUpMillis = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
123 fLastUpP.set(0, 0);
124}
125
127 fGlobalM.reset();
128 this->resetTouchState();
129}
130
131void TouchGesture::flushLocalM() {
132 fGlobalM.postConcat(fLocalM);
133 fLocalM.reset();
134}
135
137 if (fFlinger.isActive()) {
138 if (!fFlinger.evaluateMatrix(&fLocalM)) {
139 this->flushLocalM();
140 }
141 }
142 return fLocalM;
143}
144
145void TouchGesture::appendNewRec(void* owner, float x, float y) {
146 Rec* rec = fTouches.append();
147 rec->fOwner = owner;
148 rec->fStartX = rec->fPrevX = rec->fLastX = x;
149 rec->fStartY = rec->fPrevY = rec->fLastY = y;
150 rec->fLastT = rec->fPrevT = static_cast<float>(SkTime::GetSecs());
151}
152
153void TouchGesture::touchBegin(void* owner, float x, float y) {
154// SkDebugf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
155
156 int index = this->findRec(owner);
157 if (index >= 0) {
158 this->flushLocalM();
159 fTouches.removeShuffle(index);
160 SkDebugf("---- already exists, removing\n");
161 }
162
163 if (fTouches.size() == 2) {
164 return;
165 }
166
167 this->flushLocalM();
168 fFlinger.stop();
169
170 this->appendNewRec(owner, x, y);
171
172 switch (fTouches.size()) {
173 case 1:
174 fState = kTranslate_State;
175 break;
176 case 2:
177 this->startZoom();
178 break;
179 default:
180 break;
181 }
182}
183
184int TouchGesture::findRec(void* owner) const {
185 for (int i = 0; i < fTouches.size(); i++) {
186 if (owner == fTouches[i].fOwner) {
187 return i;
188 }
189 }
190 return -1;
191}
192
193static SkScalar center(float pos0, float pos1) {
194 return (pos0 + pos1) * 0.5f;
195}
196
198 fState = kZoom_State;
199}
200
201void TouchGesture::updateZoom(float scale, float startX, float startY, float lastX, float lastY) {
202 fLocalM.setTranslate(-startX, -startY);
203 fLocalM.postScale(scale, scale);
204 fLocalM.postTranslate(lastX, lastY);
205}
206
208 this->flushLocalM();
209 SkASSERT(kZoom_State == fState);
210 fState = kEmpty_State;
211}
212
213void TouchGesture::touchMoved(void* owner, float x, float y) {
214// SkDebugf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
215
216 if (kEmpty_State == fState) {
217 return;
218 }
219
220 int index = this->findRec(owner);
221 if (index < 0) {
222 SkDebugf("---- ignoring move without begin\n");
223 return;
224 }
225
226 Rec& rec = fTouches[index];
227
228 // not sure how valuable this is
229 if (fTouches.size() == 2) {
230 if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
231// SkDebugf("--- drop touchMove, within jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
232 return;
233 }
234 }
235
236 rec.fPrevX = rec.fLastX; rec.fLastX = x;
237 rec.fPrevY = rec.fLastY; rec.fLastY = y;
238 rec.fPrevT = rec.fLastT;
239 rec.fLastT = static_cast<float>(SkTime::GetSecs());
240
241 switch (fTouches.size()) {
242 case 1: {
243 float dx = rec.fLastX - rec.fStartX;
244 float dy = rec.fLastY - rec.fStartY;
246 dy = (float)sk_float_round2int(dy);
247 fLocalM.setTranslate(dx, dy);
248 } break;
249 case 2: {
250 SkASSERT(kZoom_State == fState);
251 const Rec& rec0 = fTouches[0];
252 const Rec& rec1 = fTouches[1];
253
254 float scale = this->computePinch(rec0, rec1);
255 this->updateZoom(scale,
256 center(rec0.fStartX, rec1.fStartX),
257 center(rec0.fStartY, rec1.fStartY),
258 center(rec0.fLastX, rec1.fLastX),
259 center(rec0.fLastY, rec1.fLastY));
260 } break;
261 default:
262 break;
263 }
264}
265
266void TouchGesture::touchEnd(void* owner) {
267// SkDebugf("--- %d touchEnd %p\n", fTouches.count(), owner);
268
269 int index = this->findRec(owner);
270 if (index < 0) {
271 SkDebugf("--- not found\n");
272 return;
273 }
274
275 const Rec& rec = fTouches[index];
276 if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
277 return;
278 }
279
280 // count() reflects the number before we removed the owner
281 switch (fTouches.size()) {
282 case 1: {
283 this->flushLocalM();
284 float dx = rec.fLastX - rec.fPrevX;
285 float dy = rec.fLastY - rec.fPrevY;
286 float dur = rec.fLastT - rec.fPrevT;
287 if (dur > 0) {
288 fFlinger.reset(dx / dur, dy / dur);
289 }
290 fState = kEmpty_State;
291 } break;
292 case 2:
293 this->endZoom();
294 break;
295 default:
296 SkASSERT(kZoom_State == fState);
297 break;
298 }
299
300 fTouches.removeShuffle(index);
301
302 limitTrans();
303}
304
306 if (fFlinger.isActive()) {
307 SkScalar speed;
308 fFlinger.get(dir, &speed);
309 if (speed > 1000) {
310 return true;
311 }
312 }
313 return false;
314}
315
316float TouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
317 double dx = rec0.fStartX - rec1.fStartX;
318 double dy = rec0.fStartY - rec1.fStartY;
319 double dist0 = sqrt(dx*dx + dy*dy);
320
321 dx = rec0.fLastX - rec1.fLastX;
322 dy = rec0.fLastY - rec1.fLastY;
323 double dist1 = sqrt(dx*dx + dy*dy);
324
325 double scale = dist1 / dist0;
326 return (float)scale;
327}
328
329bool TouchGesture::handleDblTap(float x, float y) {
330 bool found = false;
331 double now = SkTime::GetMSecs();
332 if (now - fLastUpMillis <= MAX_DBL_TAP_INTERVAL) {
333 if (SkPoint::Length(fLastUpP.fX - x,
334 fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
335 fFlinger.stop();
336 fLocalM.reset();
337 fGlobalM.reset();
338 fTouches.reset();
339 fState = kEmpty_State;
340 found = true;
341 }
342 }
343
344 fLastUpMillis = now;
345 fLastUpP.set(x, y);
346 return found;
347}
348
349void TouchGesture::setTransLimit(const SkRect& contentRect, const SkRect& windowRect,
350 const SkMatrix& preTouchMatrix) {
351 fIsTransLimited = true;
352 fContentRect = contentRect;
353 fWindowRect = windowRect;
354 fPreTouchM = preTouchMatrix;
355}
356
357void TouchGesture::limitTrans() {
358 if (!fIsTransLimited) {
359 return;
360 }
361
362 SkRect scaledContent = fContentRect;
363 fPreTouchM.mapRect(&scaledContent);
364 fGlobalM.mapRect(&scaledContent);
365 const SkScalar ZERO = 0;
366
367 fGlobalM.postTranslate(ZERO, std::min(ZERO, fWindowRect.fBottom - scaledContent.fTop));
368 fGlobalM.postTranslate(ZERO, std::max(ZERO, fWindowRect.fTop - scaledContent.fBottom));
369 fGlobalM.postTranslate(std::min(ZERO, fWindowRect.fRight - scaledContent.fLeft), ZERO);
370 fGlobalM.postTranslate(std::max(ZERO, fWindowRect.fLeft - scaledContent.fRight), ZERO);
371}
m reset()
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define sk_float_round2int(x)
static int sign(SkScalar x)
Definition: SkPath.cpp:2205
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define SkScalarAbs(x)
Definition: SkScalar.h:39
uint32_t SkMSec
Definition: SkTypes.h:184
#define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER
static SkScalar pin_max_fling(SkScalar speed)
static SkScalar SkScalarSignNonZero(SkScalar x)
static const SkScalar MAX_FLING_SPEED
static const float MAX_JITTER_RADIUS
static const float MAX_DBL_TAP_DISTANCE
static void unit_axis_align(SkVector *unit)
static double getseconds()
static SkScalar center(float pos0, float pos1)
static const SkMSec MAX_DBL_TAP_INTERVAL
static bool close_enough_for_jitter(float x0, float y0, float x1, float y1)
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
SkMatrix & postConcat(const SkMatrix &other)
Definition: SkMatrix.cpp:683
SkMatrix & postScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:360
SkMatrix & setTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:254
SkMatrix & reset()
Definition: SkMatrix.cpp:49
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
int size() const
Definition: SkTDArray.h:138
void reset()
Definition: SkTDArray.h:171
T * append()
Definition: SkTDArray.h:191
void removeShuffle(int index)
Definition: SkTDArray.h:214
void setTransLimit(const SkRect &contentRect, const SkRect &windowRect, const SkMatrix &preTouchM)
void touchEnd(void *owner)
bool isFling(SkPoint *dir)
void touchBegin(void *owner, float x, float y)
void touchMoved(void *owner, float x, float y)
const SkMatrix & localM()
void resetTouchState()
void updateZoom(float scale, float startX, float startY, float lastX, float lastY)
float SkScalar
Definition: extension.cpp:12
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
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
double GetSecs()
Definition: SkTime.h:16
double GetMSecs()
Definition: SkTime.h:17
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
const Scalar scale
float fX
x-axis value
Definition: SkPoint_impl.h:164
static float Normalize(SkVector *vec)
Definition: SkPoint.cpp:71
void set(float x, float y)
Definition: SkPoint_impl.h:200
static float Length(float x, float y)
Definition: SkPoint.cpp:79
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15