qstmgesturelib/recognisers/qstmpinchgesturerecogniser.cpp
changeset 0 1450b09d0cfd
child 3 0954f5dd2cd0
equal deleted inserted replaced
-1:000000000000 0:1450b09d0cfd
       
     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 }