blob: 0eaedc74bb930acfc95b13cffef585b7200803d0 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001#include "GrTouchGesture.h"
2#include "SkMatrix.h"
3#include "SkTime.h"
4
5#include <math.h>
6
7static const SkMSec MAX_DBL_TAP_INTERVAL = 300;
8static const float MAX_DBL_TAP_DISTANCE = 100;
9static const float MAX_JITTER_RADIUS = 2;
10
11// if true, then ignore the touch-move, 'cause its probably just jitter
12static bool close_enough_for_jitter(float x0, float y0, float x1, float y1) {
13 return sk_float_abs(x0 - x1) <= MAX_JITTER_RADIUS &&
14 sk_float_abs(y0 - y1) <= MAX_JITTER_RADIUS;
15}
16
17///////////////////////////////////////////////////////////////////////////////
18
19GrTouchGesture::GrTouchGesture() {
20 this->reset();
21}
22
23GrTouchGesture::~GrTouchGesture() {
24}
25
26void GrTouchGesture::reset() {
27 fTouches.reset();
28 fState = kEmpty_State;
29 fLocalM.reset();
30 fGlobalM.reset();
31
32 fLastUpT = SkTime::GetMSecs() - 2*MAX_DBL_TAP_INTERVAL;
33 fLastUpP.set(0, 0);
34}
35
36void GrTouchGesture::flushLocalM() {
37 fGlobalM.postConcat(fLocalM);
38 fLocalM.reset();
39}
40
41const SkMatrix& GrTouchGesture::localM() {
42 if (fFlinger.isActive()) {
43 if (!fFlinger.evaluateMatrix(&fLocalM)) {
44 this->flushLocalM();
45 }
46 }
47 return fLocalM;
48}
49
50void GrTouchGesture::appendNewRec(void* owner, float x, float y) {
51 Rec* rec = fTouches.append();
52 rec->fOwner = owner;
53 rec->fStartX = rec->fPrevX = rec->fLastX = x;
54 rec->fStartY = rec->fPrevY = rec->fLastY = y;
55 rec->fLastT = rec->fPrevT = SkTime::GetMSecs();
56}
57
58void GrTouchGesture::touchBegin(void* owner, float x, float y) {
59// GrPrintf("--- %d touchBegin %p %g %g\n", fTouches.count(), owner, x, y);
60
61 int index = this->findRec(owner);
62 if (index >= 0) {
63 this->flushLocalM();
64 fTouches.removeShuffle(index);
65 GrPrintf("---- already exists, removing\n");
66 }
67
68 if (fTouches.count() == 2) {
69 return;
70 }
71
72 this->flushLocalM();
73 fFlinger.stop();
74
75 this->appendNewRec(owner, x, y);
76
77 switch (fTouches.count()) {
78 case 1:
79 fState = kTranslate_State;
80 break;
81 case 2:
82 fState = kZoom_State;
83 break;
84 default:
85 break;
86 }
87}
88
89int GrTouchGesture::findRec(void* owner) const {
90 for (int i = 0; i < fTouches.count(); i++) {
91 if (owner == fTouches[i].fOwner) {
92 return i;
93 }
94 }
95 return -1;
96}
97
98static float center(float pos0, float pos1) {
99 return (pos0 + pos1) * 0.5f;
100}
101
102static const float MAX_ZOOM_SCALE = 4;
103static const float MIN_ZOOM_SCALE = 0.25f;
104
105float GrTouchGesture::limitTotalZoom(float scale) const {
106 // this query works 'cause we know that we're square-scale w/ no skew/rotation
107 const float curr = fGlobalM[0];
108
109 if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
110 scale = MAX_ZOOM_SCALE / curr;
111 } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
112 scale = MIN_ZOOM_SCALE / curr;
113 }
114 return scale;
115}
116
117void GrTouchGesture::touchMoved(void* owner, float x, float y) {
118// GrPrintf("--- %d touchMoved %p %g %g\n", fTouches.count(), owner, x, y);
119
120 GrAssert(kEmpty_State != fState);
121
122 int index = this->findRec(owner);
123 if (index < 0) {
124 // not found, so I guess we should add it...
125 GrPrintf("---- add missing begin\n");
126 this->appendNewRec(owner, x, y);
127 index = fTouches.count() - 1;
128 }
129
130 Rec& rec = fTouches[index];
131
132 // not sure how valuable this is
133 if (fTouches.count() == 2) {
134 if (close_enough_for_jitter(rec.fLastX, rec.fLastY, x, y)) {
135// GrPrintf("--- drop touchMove, withing jitter tolerance %g %g\n", rec.fLastX - x, rec.fLastY - y);
136 return;
137 }
138 }
139
140 rec.fPrevX = rec.fLastX; rec.fLastX = x;
141 rec.fPrevY = rec.fLastY; rec.fLastY = y;
142 rec.fPrevT = rec.fLastT; rec.fLastT = SkTime::GetMSecs();
143
144 switch (fTouches.count()) {
145 case 1: {
146 float dx = rec.fLastX - rec.fStartX;
147 float dy = rec.fLastY - rec.fStartY;
148 dx = (float)sk_float_round2int(dx);
149 dy = (float)sk_float_round2int(dy);
150 fLocalM.setTranslate(dx, dy);
151 } break;
152 case 2: {
153 GrAssert(kZoom_State == fState);
154 const Rec& rec0 = fTouches[0];
155 const Rec& rec1 = fTouches[1];
156
157 float scale = this->computePinch(rec0, rec1);
158 scale = this->limitTotalZoom(scale);
159
160 fLocalM.setTranslate(-center(rec0.fStartX, rec1.fStartX),
161 -center(rec0.fStartY, rec1.fStartY));
162 fLocalM.postScale(scale, scale);
163 fLocalM.postTranslate(center(rec0.fLastX, rec1.fLastX),
164 center(rec0.fLastY, rec1.fLastY));
165 } break;
166 default:
167 break;
168 }
169}
170
171void GrTouchGesture::touchEnd(void* owner) {
172// GrPrintf("--- %d touchEnd %p\n", fTouches.count(), owner);
173
174 int index = this->findRec(owner);
175 if (index < 0) {
176 GrPrintf("--- not found\n");
177 return;
178 }
179
180 const Rec& rec = fTouches[index];
181 if (this->handleDblTap(rec.fLastX, rec.fLastY)) {
182 return;
183 }
184
185 // count() reflects the number before we removed the owner
186 switch (fTouches.count()) {
187 case 1: {
188 this->flushLocalM();
189 float dx = rec.fLastX - rec.fPrevX;
190 float dy = rec.fLastY - rec.fPrevY;
191 float dur = (rec.fLastT - rec.fPrevT) * 0.001f;
192 if (dur > 0) {
193 fFlinger.reset(dx / dur, dy / dur);
194 }
195 fState = kEmpty_State;
196 } break;
197 case 2:
198 this->flushLocalM();
199 GrAssert(kZoom_State == fState);
200 fState = kEmpty_State;
201 break;
202 default:
203 GrAssert(kZoom_State == fState);
204 break;
205 }
206
207 fTouches.removeShuffle(index);
208}
209
210float GrTouchGesture::computePinch(const Rec& rec0, const Rec& rec1) {
211 double dx = rec0.fStartX - rec1.fStartX;
212 double dy = rec0.fStartY - rec1.fStartY;
213 double dist0 = sqrt(dx*dx + dy*dy);
214
215 dx = rec0.fLastX - rec1.fLastX;
216 dy = rec0.fLastY - rec1.fLastY;
217 double dist1 = sqrt(dx*dx + dy*dy);
218
219 double scale = dist1 / dist0;
220 return (float)scale;
221}
222
223bool GrTouchGesture::handleDblTap(float x, float y) {
224 bool found = false;
225 SkMSec now = SkTime::GetMSecs();
226 if (now - fLastUpT <= MAX_DBL_TAP_INTERVAL) {
227 if (SkPoint::Length(fLastUpP.fX - x,
228 fLastUpP.fY - y) <= MAX_DBL_TAP_DISTANCE) {
229 fFlinger.stop();
230 fLocalM.reset();
231 fGlobalM.reset();
232 fTouches.reset();
233 fState = kEmpty_State;
234 found = true;
235 }
236 }
237
238 fLastUpT = now;
239 fLastUpP.set(x, y);
240 return found;
241}
242
243