|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "qstmpinchgesturerecogniser.h" |
|
20 #include "qstmgenericsimplegesture.h" |
|
21 #include <qstmuievent_if.h> |
|
22 #include <qstmfilelogger.h> |
|
23 #include <qstmutils.h> |
|
24 |
|
25 |
|
26 using namespace qstmGesture ; |
|
27 using namespace qstmUiEventEngine; |
|
28 |
|
29 |
|
30 QStm_PinchGestureRecogniser::QStm_PinchGestureRecogniser(QStm_GestureListenerIf* listener) : |
|
31 QStm_GestureRecogniser(listener) |
|
32 { |
|
33 m_pinching = false ; |
|
34 m_pinchingspeed = 3.5 ; // by default something suitable for capacitive |
|
35 m_holdseen = false ; |
|
36 } |
|
37 |
|
38 QStm_PinchGestureRecogniser::~QStm_PinchGestureRecogniser() |
|
39 { |
|
40 } |
|
41 |
|
42 QStm_GestureRecognitionState QStm_PinchGestureRecogniser::recognise(int numOfActiveStreams, |
|
43 QStm_GestureEngineIf* pge) |
|
44 { |
|
45 QStm_GestureRecognitionState state = m_state = ENotMyGesture; |
|
46 // Check if we are enabled or not |
|
47 if (!m_gestureEnabled) return state ; |
|
48 |
|
49 if (m_loggingenabled) { |
|
50 LOGARG("QStm_PinchGestureRecogniser: %d %d %d ", m_pinching, m_holdseen, numOfActiveStreams) ; |
|
51 } |
|
52 #if !defined(ADVANCED_POINTER_EVENTS) |
|
53 // Look at the events to see if it looks like pinch in single touch |
|
54 // WARNING: this code is a hack : in single touch capacitive touch device (like Alvin with 52.50) it works so-and-so, |
|
55 // because the pointer events were reported from the corners of the rectangle formed by two fingers pressing. |
|
56 // In resistive touch device like Tube or Ivalo the reported points are somewhere in the middle between the fingers |
|
57 // and jumping a lot, so it is very difficult to get it right. |
|
58 if (numOfActiveStreams == 1) { |
|
59 // Then look at the event stream, first we need to see a hold and then a fast jump |
|
60 const qstmUiEventEngine::QStm_UiEventIf* puie = pge->getUiEvents(0); |
|
61 int countOfEvents = puie->countOfEvents() ; |
|
62 qstmUiEventEngine::QStm_UiEventCode eventCode = puie->code() ; |
|
63 |
|
64 if (countOfEvents > 0 ) {// how many events |
|
65 if (m_loggingenabled) { |
|
66 LOGARG("QStm_PinchGestureRecogniser: %d %d %d %d %d, m: %d b: %d", |
|
67 m_pinching, m_holdseen, numOfActiveStreams, countOfEvents, eventCode, int(m_m), int(m_b)) ; |
|
68 } |
|
69 |
|
70 if (m_pinching) { |
|
71 // We have entered pinching state, lets move one of the points unless it is a release |
|
72 if (eventCode == qstmUiEventEngine::ERelease) { |
|
73 m_pinching = false ; |
|
74 m_holdseen = false ; |
|
75 } |
|
76 else { |
|
77 bool pointIgnored = true ; // for logging purposes |
|
78 int currentLength = m_loggingenabled ? QStm_Utils::distance(m_pinchstart, m_pinchend) : 0; |
|
79 |
|
80 QPoint oStart(m_pinchstart) ; |
|
81 QPoint oEnd(m_pinchend) ; |
|
82 int difference = 0 ; |
|
83 state = ELockToThisGesture ; |
|
84 const QPoint& tp = puie->currentXY(); |
|
85 // calculate the distance of the new point from the stored vector |
|
86 int d1 = ((m_pinchstart.x() - tp.x()) * (m_pinchstart.x() - tp.x())) + |
|
87 ((m_pinchstart.y() - tp.y()) * (m_pinchstart.y() - tp.y())) ; |
|
88 int d2 = ((m_pinchend.x() - tp.x()) * (m_pinchend.x() - tp.x())) + |
|
89 ((m_pinchend.y() - tp.y()) * (m_pinchend.y() - tp.y())) ; |
|
90 // check also if the Y coordinate happens to be near the hold point, |
|
91 // this seems to be the case at least with alvin, we keep getting two points, |
|
92 // where one is near the Y coordinate of the hold point |
|
93 int diffY = ABS(tp.y() - m_pinchstart.y()) ; |
|
94 |
|
95 if (d1 < d2 || diffY < 12) { |
|
96 // the detected point is near the first point, |
|
97 // or the detected point is about on the same horizontal line with the hold point |
|
98 // do not do anything, but keep the gesture |
|
99 } |
|
100 else { |
|
101 pointIgnored = false ; |
|
102 // the detected point is close to the other end, then adjust the stored vector |
|
103 int xd = m_pinchend.x() - tp.x() ; |
|
104 int yd = m_pinchend.y() - tp.y() ; |
|
105 if (xd < 0 ) xd = - xd ; |
|
106 if (yd < 0 ) yd = - yd ; |
|
107 // look which coordinate is closer to the original and use that |
|
108 if (xd < yd) { |
|
109 // calculate new point based on the X value |
|
110 m_pinchend.setX(tp.x()) ; |
|
111 m_pinchend.setY(m_m * m_pinchend.x() + m_b) ; |
|
112 if (m_pinchend.y() < 0) m_pinchend.setY(0) ; |
|
113 } |
|
114 else { |
|
115 if (m_m != 0) { |
|
116 m_pinchend.setY(tp.y()) ; |
|
117 m_pinchend.setX((m_pinchend.y() - m_b) / m_m) ; |
|
118 if (m_pinchend.x() < 0 ) m_pinchend.setX(0) ; |
|
119 } |
|
120 else { |
|
121 m_pinchend.setX(tp.x()) ; |
|
122 m_pinchend.setY(m_m * m_pinchend.x() + m_b) ; |
|
123 if (m_pinchend.y() < 0) m_pinchend.setY(0) ; |
|
124 } |
|
125 } |
|
126 float newd = calculateDistance() ; |
|
127 // check if the difference is too big and adjust accordingly |
|
128 // the method also updates the m_ddistance |
|
129 difference = adjustPinchMove(m_ddistance, newd) ; |
|
130 // Now we have a pinch gesture with size as details |
|
131 qstmGesture::QStm_TwoPointGesture pgest(KUid, m_pinchstart, m_pinchend); |
|
132 pgest.setLogging(m_loggingenabled); |
|
133 pgest.setDetails(&difference) ; |
|
134 pgest.setTarget(puie->target()); |
|
135 // inform the listener |
|
136 m_listener->gestureEnter(pgest); |
|
137 } |
|
138 if (m_loggingenabled) { |
|
139 int newLength = QStm_Utils::distance(m_pinchstart, m_pinchend); |
|
140 float speed = puie->speed() ; |
|
141 |
|
142 LOGARG("QStm_PinchGestureRecogniser: %d: o: %d, n: %d, d: %d (%d,%d) speed %f (%d,%d : %d,%d) (from: (%d,%d : %d,%d) (m: %f b: %f)", |
|
143 pointIgnored, |
|
144 currentLength, newLength, difference, |
|
145 tp.x(), tp.y(), double(speed), |
|
146 m_pinchstart.x(), m_pinchstart.y(), m_pinchend.x(), m_pinchend.y(), |
|
147 oStart.x(), oStart.y(), oEnd.x(), oEnd.y(), |
|
148 double(m_m), double(m_b)) ; |
|
149 } |
|
150 |
|
151 } |
|
152 } |
|
153 else if (eventCode == qstmUiEventEngine::EMove) { // The last one is move and we were not pinching |
|
154 |
|
155 if (m_loggingenabled) { |
|
156 LOGARG("QStm_PinchGestureRecogniser: %d: num %d code %d", m_pinching, countOfEvents, eventCode); |
|
157 } |
|
158 qstmUiEventEngine::QStm_UiEventIf* puieFirst = puie->previousEvent(); |
|
159 |
|
160 // check if we have seen hold |
|
161 if (m_holdseen) { |
|
162 const QPoint& tp1 = puie->currentXY() ; |
|
163 float speed = puie->speed() ; |
|
164 if (m_loggingenabled) { |
|
165 LOGARG("QStm_PinchGestureRecogniser: tp1: %d %d hold %d %d, speed %f", |
|
166 tp1.x(), tp1.x(), m_holdseenAtPos.x(), m_holdseenAtPos.y(), double(speed) ); |
|
167 } |
|
168 // is the speed extremely high so that it looks like other finger pressing in different location? |
|
169 if (speed > m_pinchingspeed) { |
|
170 QTime tstamp = puie->timestamp() ; |
|
171 |
|
172 int tim = m_holdseenAtTime.msecsTo(tstamp); |
|
173 |
|
174 m_pinching = true; |
|
175 m_pinchstart = m_holdseenAtPos; |
|
176 m_pinchend = tp1; |
|
177 calculateZoomingLine(); |
|
178 m_ddistance = calculateDistance(); |
|
179 state = ELockToThisGesture ; // NOTE: once pinch is started, it will stay until release |
|
180 // create the first pich gesture which does not yet resize anything |
|
181 qstmGesture::QStm_TwoPointGesture pgest(KUid, m_pinchstart, m_pinchend); |
|
182 pgest.setTarget(puie->target()); |
|
183 pgest.setLogging(m_loggingenabled); |
|
184 pgest.setDetails(0) ; |
|
185 // inform the listener |
|
186 m_listener->gestureEnter(pgest); |
|
187 } |
|
188 } |
|
189 } |
|
190 } |
|
191 if (!m_pinching) { |
|
192 if (m_loggingenabled) { |
|
193 LOGARG("QStm_PinchGestureRecogniser: not pinching %d", puie); |
|
194 } |
|
195 if (puie != NULL && puie->code() == qstmUiEventEngine::EHold) { // The last one is hold and we were not pinching |
|
196 |
|
197 m_holdseen = true; |
|
198 m_holdseenAtPos = puie->currentXY(); |
|
199 m_holdseenAtTime = puie->timestamp() ; |
|
200 if (m_loggingenabled) { |
|
201 LOGARG("QStm_PinchGestureRecogniser: hold seen at(%d, %d) at %s", |
|
202 m_holdseenAtPos.x(), m_holdseenAtPos.y(), m_holdseenAtTime.toString("hh:mm:ss.zzz")); |
|
203 } |
|
204 } |
|
205 } |
|
206 if (puie && puie->code() == qstmUiEventEngine::ETouch) {// The last one is touch |
|
207 m_holdseen = false; |
|
208 } |
|
209 else if (puie && puie->code() == qstmUiEventEngine::ERelease) {// The last one is release |
|
210 m_holdseen = false; |
|
211 } |
|
212 } |
|
213 #else |
|
214 // This is the multi touch case: two event streams needs to be there; this is the real pinch zoom |
|
215 if (numOfActiveStreams == 2) { |
|
216 const qstmUiEventEngine::QStm_UiEventIf* puie1 = pge->getUiEvents(0); |
|
217 const qstmUiEventEngine::QStm_UiEventIf* puie2 = pge->getUiEvents(1); |
|
218 qstmUiEventEngine::QStm_UiEventCode eventCode1 = puie1->code() ; |
|
219 qstmUiEventEngine::QStm_UiEventCode eventCode2 = puie2->code() ; |
|
220 |
|
221 if (m_loggingenabled) { |
|
222 QPoint p1 = puie1->currentXY() ; |
|
223 QPoint p2 = puie2->currentXY() ; |
|
224 LOGARG("QStm_PinchGestureRecogniser: two streams: %s at [%d,%d], %s at [%d,%d]", |
|
225 qstmUiEventEngine::event_name(eventCode1), p1.x(), p1.y(), |
|
226 qstmUiEventEngine::event_name(eventCode1), p2.x(), p2.y() |
|
227 ) ; |
|
228 |
|
229 } |
|
230 |
|
231 |
|
232 if (!m_pinching) { |
|
233 // This means we start pinching, the events can be any combination of ETouch, EMove, EHold |
|
234 if ((eventCode1 == qstmUiEventEngine::ETouch || |
|
235 eventCode1 == qstmUiEventEngine::EMove || |
|
236 eventCode1 == qstmUiEventEngine::EHold) && |
|
237 (eventCode2 == qstmUiEventEngine::ETouch || |
|
238 eventCode2 == qstmUiEventEngine::EMove || |
|
239 eventCode2 == qstmUiEventEngine::EHold)) { |
|
240 |
|
241 // This is valid pinching start |
|
242 m_pinching = true ; |
|
243 // get the start and end position for the picnhing vector |
|
244 m_pinchstart = puie1->currentXY() ; |
|
245 m_pinchend = puie2->currentXY() ; |
|
246 calculateZoomingLine(); |
|
247 m_ddistance = calculateDistance(); |
|
248 state = ELockToThisGesture ; // NOTE: once pich is started, it will stay until release |
|
249 if (m_loggingenabled) { |
|
250 LOGARG("QStm_PinchGestureRecogniser: pinch start: [%d,%d][%d,%d]", |
|
251 m_pinchstart.x(), m_pinchstart.y(), m_pinchend.x(), m_pinchend.y()) ; |
|
252 |
|
253 } |
|
254 // create the first pich gesture which does not yet resize anything |
|
255 qstmGesture::QStm_TwoPointGesture pgest(KUid, m_pinchstart, m_pinchend); |
|
256 pgest.setLogging(m_loggingenabled); |
|
257 pgest.setDetails(0) ; |
|
258 pgest.setTarget(puie->target()); |
|
259 // inform the listener |
|
260 m_listener->gestureEnter(pgest); |
|
261 } |
|
262 else { |
|
263 // Not a valid pinching start, do nothing (maybe it were easier to just check if one of the events is ERelease) |
|
264 } |
|
265 } |
|
266 else { |
|
267 // We have entered pinching state, lets move one of the points unless it is a release |
|
268 if (eventCode1 == qstmUiEventEngine::ERelease || eventCode2 == qstmUiEventEngine::ERelease) { |
|
269 m_pinching = false ; |
|
270 } |
|
271 else { |
|
272 state = ELockToThisGesture ; |
|
273 |
|
274 // get the start and end position for the picnhing vector |
|
275 m_pinchstart = puie1->currentXY() ; |
|
276 m_pinchend = puie2->currentXY() ; |
|
277 float newd = calculateDistance() ; |
|
278 // check if the difference is too big and adjust accordingly |
|
279 // the method also updates the m_ddistance |
|
280 int difference = adjustPinchMove(m_ddistance, newd) ; |
|
281 // Now we have a pinch gesture with size |
|
282 if (m_loggingenabled) { |
|
283 LOGARG("QStm_PinchGestureRecogniser: pinch: [%d,%d][%d,%d], diff %d", |
|
284 m_pinchstart.x(), m_pinchstart.y(), m_pinchend.x(), m_pinchend.y(), difference) ; |
|
285 |
|
286 } |
|
287 |
|
288 qstmGesture::QStm_TwoPointGesture pgest(KUid, m_pinchstart, m_pinchend); |
|
289 pgest.setLogging(m_loggingenabled); |
|
290 pgest.setDetails(difference) ; |
|
291 pgest.setTarget(puie->target()); |
|
292 // inform the listener |
|
293 m_listener->gestureEnter(pgest); |
|
294 } |
|
295 } |
|
296 |
|
297 } |
|
298 #endif |
|
299 |
|
300 if (state == ENotMyGesture) { |
|
301 if (m_loggingenabled) { |
|
302 LOGARG("QStm_PinchGestureRecogniser: NotMyGesture %d %d %d ", m_pinching, m_holdseen, numOfActiveStreams) ; |
|
303 } |
|
304 // if it was not our gesture, then the state can not be pinching... |
|
305 m_pinching = false ; |
|
306 } |
|
307 m_state = state; |
|
308 |
|
309 return state; |
|
310 } |
|
311 |
|
312 void QStm_PinchGestureRecogniser::release(QStm_GestureEngineIf* pge) |
|
313 { |
|
314 m_pinching = false ; |
|
315 const qstmUiEventEngine::QStm_UiEventIf* puie = pge->getUiEvents(0); |
|
316 qstmGesture::QStm_TwoPointGesture pgest(KUid, m_pinchstart, m_pinchend); |
|
317 pgest.setDetails(0) ; |
|
318 pgest.setTarget(puie->target()); |
|
319 m_listener->gestureExit(pgest) ; |
|
320 m_state = ENotMyGesture; |
|
321 } |
|
322 |
|
323 |
|
324 /*! |
|
325 * Now that we know the two points where the zooming started, we move those points only along |
|
326 * the same line y = mx + b, so lets calculate m and b. |
|
327 */ |
|
328 void QStm_PinchGestureRecogniser::calculateZoomingLine() |
|
329 { |
|
330 int sX = m_pinchstart.x() ; |
|
331 int sY = m_pinchstart.y() ; |
|
332 int eX = m_pinchend.x() ; |
|
333 int eY = m_pinchend.y() ; |
|
334 |
|
335 if (eX == sX) { |
|
336 m_m = 0.f ; |
|
337 } |
|
338 else { |
|
339 m_m = float(eY-sY)/(eX-sX) ; |
|
340 } |
|
341 m_b = sY-(m_m*sX) ; |
|
342 } |
|
343 |
|
344 /*! |
|
345 * calculate the distance, return as float |
|
346 */ |
|
347 float QStm_PinchGestureRecogniser::calculateDistance() |
|
348 { |
|
349 /* |
|
350 double x = ((m_pinchstart.x() - m_pinchend.x()) * (m_pinchstart.x() - m_pinchend.x())) + |
|
351 ((m_pinchstart.y() - m_pinchend.y()) * (m_pinchstart.y() - m_pinchend.y())) ; |
|
352 double ddist; |
|
353 SQRT(ddist, x) ; |
|
354 return float(ddist) ; |
|
355 */ |
|
356 return QStm_Utils::distanceF(m_pinchstart, m_pinchend); |
|
357 } |
|
358 |
|
359 /*! |
|
360 * Set the pinching speed as pixels / ms (meaning that in case of singletouch device |
|
361 * the other finger looks like the EMove UI event suddenly jumps to new location; |
|
362 * in resistive the new location is somewhere in the middle of the touches, in capacitive |
|
363 * the driver seems to report three or four points: |
|
364 * original (x,y), new (a,b) and also (a,y), sometimes (x,b) |
|
365 */ |
|
366 void QStm_PinchGestureRecogniser::setPinchingSpeed(float speed) /* __SOFTFP */ |
|
367 { |
|
368 m_pinchingspeed = speed ; |
|
369 } |
|
370 |
|
371 /*! |
|
372 * Adjust the pinch move so that it will not be too jumpy |
|
373 */ |
|
374 int QStm_PinchGestureRecogniser::adjustPinchMove(float& previousDistance, float newDistance) |
|
375 { |
|
376 float diff = newDistance - previousDistance ; |
|
377 float logdiff = diff ; |
|
378 if (diff < 0) diff = -diff ; // Note that the next calculations need the positive diff value, but keep the original in logdiff |
|
379 float changePercentage = (diff/previousDistance)*100.f ; |
|
380 if (changePercentage > 10.f) { |
|
381 // change more than 10%, make at most 10% |
|
382 float newdiff = previousDistance*0.1f ; |
|
383 if (previousDistance > newDistance) newdiff = -newdiff ; |
|
384 if (m_loggingenabled) { |
|
385 LOGARG("QStm_PinchGestureRecogniser: adjustPinchMove from %f to %f : was, now %f %f", |
|
386 double(logdiff), double(newdiff), double(previousDistance), double(newDistance)); |
|
387 } |
|
388 |
|
389 previousDistance = previousDistance + newdiff ; |
|
390 diff = newdiff ; |
|
391 } |
|
392 else { |
|
393 if (m_loggingenabled) { |
|
394 LOGARG("QStm_PinchGestureRecogniser: adjustPinchMove from %f to %f : was, now %f %f", |
|
395 double(logdiff), double(diff), double(previousDistance), double(newDistance)); |
|
396 } |
|
397 previousDistance = newDistance ; // accept the new value and update the new length |
|
398 diff = logdiff ; // put the original back (this is why the logdiff can not be ABS(diff)! |
|
399 } |
|
400 return (int)diff ; |
|
401 } |