--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/webkitutils/stmgesturefw/src/pinchgesturerecogniser.cpp Tue Feb 02 00:56:45 2010 +0200
@@ -0,0 +1,440 @@
+/*
+* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Gesture helper implementation
+*
+*/
+#include <e32math.h>
+#include "pinchgesturerecogniser.h"
+#include "GenericSimpleGesture.h"
+#include <rt_uievent.h>
+#include "filelogger.h"
+#include "statemachine.h" // for stmUiEventEngine::Distance(dx,dy)
+
+using namespace stmGesture ;
+
+extern long Mm2Pixels(long mm) ;
+
+
+CPinchGestureRecogniser::CPinchGestureRecogniser(MGestureListener* aListener) :
+ CGestureRecogniser(aListener)
+{
+ m_pinching = false ;
+ m_pinchingspeed = 3.5 ; // by default something suitable for capacitive
+ m_holdseen = false ;
+}
+
+CPinchGestureRecogniser* CPinchGestureRecogniser::NewL(MGestureListener* aListener)
+{
+ CPinchGestureRecogniser* self = new (ELeave) CPinchGestureRecogniser(aListener) ;
+ return self;
+}
+
+CPinchGestureRecogniser::~CPinchGestureRecogniser()
+{
+}
+
+TGestureRecognitionState CPinchGestureRecogniser::recognise(int numOfActiveStreams,
+ MGestureEngineIf* pge)
+{
+ TGestureRecognitionState state = ENotMyGesture;
+ // Check if we are enabled or not
+ if (!m_gestureEnabled) return state ;
+
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: %d %d %d ", m_pinching, m_holdseen, numOfActiveStreams) ;
+ }
+#if !defined(ADVANCED_POINTER_EVENTS)
+ // Look at the events to see if it looks like pinch in single touch
+ // WARNING: this code is a hack : in single touch capacitive touch device (like Alvin with 52.50) it works so-and-so,
+ // because the pointer events were reported from the corners of the rectangle formed by two fingers pressing.
+ // In resistive touch device like Tube or Ivalo the reported points are somewhere int he middle between the fingers
+ // and jumping a lot, so it is very difficult to get it right.
+ if (numOfActiveStreams == 1)
+ {
+ // Then look at the event stream, first we need to see a hold and then a fast jump
+ const stmUiEventEngine::MUiEvent* puie = pge->getUiEvents(0);
+ int countOfEvents = puie->countOfEvents() ;
+ stmUiEventEngine::TUiEventCode eventCode = puie->Code() ;
+
+ if (countOfEvents > 0 ) // how many events
+ {
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: %d %d %d %d %d, m: %d b: %d",
+ m_pinching, m_holdseen, numOfActiveStreams, countOfEvents, eventCode,
+ int(m_m), int(m_b)) ;
+ }
+
+ if (m_pinching)
+ {
+ // We have entered pinching state, lets move one of the points unless it is a release
+ if (eventCode == stmUiEventEngine::ERelease)
+ {
+ m_pinching = false ;
+ m_holdseen = false ;
+ }
+ else
+ {
+ bool pointIgnored = true ; // for logging purposes
+ int currentLength = m_loggingenabled ?
+ stmUiEventEngine::Distance(m_pinchstart, m_pinchend) : 0;
+
+ TPoint oStart(m_pinchstart) ;
+ TPoint oEnd(m_pinchend) ;
+ int difference = 0 ;
+ state = ELockToThisGesture ;
+ const TPoint& tp = puie->CurrentXY();
+ // calculate the distance of the new point from the stored vector
+ int d1 = ((m_pinchstart.iX-tp.iX)*(m_pinchstart.iX-tp.iX)) +
+ ((m_pinchstart.iY-tp.iY)*(m_pinchstart.iY-tp.iY)) ;
+ int d2 = ((m_pinchend.iX-tp.iX)*(m_pinchend.iX-tp.iX)) +
+ ((m_pinchend.iY-tp.iY)*(m_pinchend.iY-tp.iY)) ;
+ // check also if the Y coordinate happens to be near the hold point,
+ // this seems to be the case at least with alvin, we keep getting two points,
+ // where one is near the Y coordinate of the hold point
+ int diffY = Abs(tp.iY-m_pinchstart.iY) ;
+
+ if (d1 < d2 || diffY < 12)
+ {
+ // the detected point is near the first point,
+ // or the detected point is about on the same horizontal line with the hold point
+ // do not do anything, but keep the gesture
+ }
+ else
+ {
+ pointIgnored = false ;
+ // the detected point is close to the other end, then adjust the stored vector
+ int xd = m_pinchend.iX-tp.iX ;
+ int yd = m_pinchend.iY-tp.iY ;
+ if (xd < 0 ) xd = - xd ;
+ if (yd < 0 ) yd = - yd ;
+ // look which coordinate is closer to the original and use that
+ if (xd < yd)
+ {
+ // calculate new point based on the X value
+ m_pinchend.iX = tp.iX ;
+ m_pinchend.iY = m_m*m_pinchend.iX + m_b ;
+ if (m_pinchend.iY < 0) m_pinchend.iY = 0 ;
+ }
+ else
+ {
+ if (m_m != 0)
+ {
+ m_pinchend.iY = tp.iY ;
+ m_pinchend.iX = (m_pinchend.iY - m_b)/m_m ;
+ if (m_pinchend.iX <0 ) m_pinchend.iX = 0 ;
+ }
+ else
+ {
+ m_pinchend.iX = tp.iX ;
+ m_pinchend.iY = m_m*m_pinchend.iX + m_b ;
+ if (m_pinchend.iY < 0) m_pinchend.iY = 0 ;
+ }
+ }
+ float newd = calculateDistance() ;
+ // check if the difference is too big and adjust accordingly
+ // the method also updates the m_ddistance
+ difference = adjustPinchMove(m_ddistance, newd) ;
+ // Now we have a pinch gesture with size as details
+ stmGesture::TTwoPointGesture pgest(KUid, m_pinchstart, m_pinchend);
+ pgest.setLogging(m_loggingenabled);
+ pgest.setDetails(difference) ;
+ // inform the listener
+ m_listener->gestureEnter(pgest);
+ }
+ if (m_loggingenabled)
+ {
+ int newLength = stmUiEventEngine::Distance(m_pinchstart, m_pinchend);
+ float speedX = puie->speedX() ;
+ float speedY = puie->speedY() ;
+
+ LOGARG("CPinchGestureRecogniser: %d: o: %d, n: %d, d: %d (%d,%d) " \
+ "speed %f (%d,%d : %d,%d) (from: (%d,%d : %d,%d) (m: %f b: %f)",
+ pointIgnored,
+ currentLength, newLength, difference,
+ tp.iX, tp.iY, double(speedX),
+ m_pinchstart.iX, m_pinchstart.iY, m_pinchend.iX, m_pinchend.iY,
+ oStart.iX, oStart.iY, oEnd.iX, oEnd.iY,
+ double(m_m), double(m_b)) ;
+
+ }
+
+ }
+ }
+ else if (eventCode == stmUiEventEngine::EMove) // The last one is move and we were not pinching
+ {
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: %d: num %d code %d", m_pinching, countOfEvents, eventCode);
+ }
+ stmUiEventEngine::MUiEvent* puieFirst = puie->previousEvent();
+
+ // check if we have seen hold
+ if (m_holdseen)
+ {
+ const TPoint& tp1 = puie->CurrentXY() ;
+ float speedX = puie->speedX() ;
+ float speedY = puie->speedY() ;
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: tp1: %d %d hold %d %d, speed %f",
+ tp1.iX, tp1.iY,
+ m_holdseenAtPos.iX, m_holdseenAtPos.iY, double(speedX) );
+ }
+ // is the speed extremely high so that it looks like other finger pressing in different location?
+ if ( (speedX > m_pinchingspeed) || (speedY > m_pinchingspeed) )
+ {
+ TInt64 tstamp = puie->timestamp() ;
+ TTime now(tstamp) ;
+ TTimeIntervalMicroSeconds tim = now.MicroSecondsFrom(m_holdseenAtTime) ;
+ m_pinching = true;
+ m_pinchstart = m_holdseenAtPos;
+ m_pinchend = tp1;
+ calculateZoomingLine();
+ m_ddistance = calculateDistance();
+ state = ELockToThisGesture ; // NOTE: once pinch is started, it will stay until release
+ // create the first pich gesture which does not yet resize anything
+ stmGesture::TTwoPointGesture pgest(KUid, m_pinchstart, m_pinchend);
+ pgest.setLogging(m_loggingenabled);
+ pgest.setDetails(0) ;
+ // inform the listener
+ m_listener->gestureEnter(pgest);
+ }
+ }
+ }
+ }
+ if (!m_pinching)
+ {
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: not pinching %d", puie);
+ }
+ if (puie && puie->Code() == stmUiEventEngine::EHold) // The last one is hold and we were not pinching
+ {
+ m_holdseen = true;
+ m_holdseenAtPos = puie->CurrentXY();
+ m_holdseenAtTime = puie->timestamp() ;
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: hold seen at(%d, %d) at %Ld",
+ m_holdseenAtPos.iX, m_holdseenAtPos.iY, m_holdseenAtTime.Int64());
+ }
+ }
+ }
+ if (puie && puie->Code() == stmUiEventEngine::ETouch) // The last one is touch
+ {
+ m_holdseen = false;
+ }
+ else if (puie && puie->Code() == stmUiEventEngine::ERelease) // The last one is release
+ {
+ m_holdseen = false;
+ }
+ }
+#else
+ // This is the multi touch case: two event streams needs to be there; this is the real pinch zoom
+ if (numOfActiveStreams == 2)
+ {
+ const stmUiEventEngine::MUiEvent* puie1 = pge->getUiEvents(0);
+ const stmUiEventEngine::MUiEvent* puie2 = pge->getUiEvents(1);
+ stmUiEventEngine::TUiEventCode eventCode1 = puie1->Code() ;
+ stmUiEventEngine::TUiEventCode eventCode2 = puie2->Code() ;
+
+ if (m_loggingenabled)
+ {
+ TPoint p1 = puie1->CurrentXY() ;
+ TPoint p2 = puie2->CurrentXY() ;
+ LOGARG("CPinchGestureRecogniser: two streams: %s at [%d,%d], %s at [%d,%d]",
+ stmUiEventEngine::EventName(eventCode1), p1.iX, p1.iY,
+ stmUiEventEngine::EventName(eventCode1), p2.iX, p2.iY
+ ) ;
+
+ }
+
+
+ if (!m_pinching)
+ {
+ // This means we start pinching, the events can be any combination of ETouch, EMove, EHold
+ if ( ( eventCode1 == stmUiEventEngine::ETouch ||
+ eventCode1 == stmUiEventEngine::EMove ||
+ eventCode1 == stmUiEventEngine::EHold
+ ) &&
+ ( eventCode2 == stmUiEventEngine::ETouch ||
+ eventCode2 == stmUiEventEngine::EMove ||
+ eventCode2 == stmUiEventEngine::EHold )
+ )
+ {
+ // This is valid pinching start
+ m_pinching = true ;
+ // get the start and end position for the picnhing vector
+ m_pinchstart = puie1->CurrentXY() ;
+ m_pinchend = puie2->CurrentXY() ;
+ calculateZoomingLine();
+ m_ddistance = calculateDistance();
+ state = ELockToThisGesture ; // NOTE: once pich is started, it will stay until release
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: pinch start: [%d,%d][%d,%d]",
+ m_pinchstart.iX, m_pinchstart.iY, m_pinchend.iX, m_pinchend.iY) ;
+
+ }
+ // create the first pich gesture which does not yet resize anything
+ stmGesture::TTwoPointGesture pgest(KUid, m_pinchstart, m_pinchend);
+ pgest.setLogging(m_loggingenabled);
+ pgest.setDetails(0) ;
+ // inform the listener
+ m_listener->gestureEnter(pgest);
+ }
+ else
+ {
+ // Not a valid pinching start, do nothing (maybe it were easier to just check if one of the events is ERelease)
+ }
+ }
+ else
+ {
+ // We have entered pinching state, lets move one of the points unless it is a release
+ if (eventCode1 == stmUiEventEngine::ERelease || eventCode2 == stmUiEventEngine::ERelease)
+ {
+ release(pge);
+ }
+ else
+ {
+ state = ELockToThisGesture ;
+
+ // get the start and end position for the picnhing vector
+ m_pinchstart = puie1->CurrentXY() ;
+ m_pinchend = puie2->CurrentXY() ;
+ float newd = calculateDistance() ;
+ // check if the difference is too big and adjust accordingly
+ // the method also updates the m_ddistance
+ int difference = adjustPinchMove(m_ddistance, newd) ;
+ // Now we have a pinch gesture with size
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: pinch: [%d,%d][%d,%d], diff %d",
+ m_pinchstart.iX, m_pinchstart.iY, m_pinchend.iX, m_pinchend.iY, difference) ;
+
+ }
+
+ stmGesture::TTwoPointGesture pgest(KUid, m_pinchstart, m_pinchend);
+ pgest.setLogging(m_loggingenabled);
+ pgest.setDetails(difference) ;
+ // inform the listener
+ m_listener->gestureEnter(pgest);
+ }
+ }
+
+ }
+#endif
+
+ if (state == ENotMyGesture)
+ {
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: NotMyGesture %d %d %d ",
+ m_pinching, m_holdseen, numOfActiveStreams) ;
+ }
+ // if it was not our gesture, then the state can not be pinching...
+ m_pinching = false ;
+ }
+ return state;
+}
+
+void CPinchGestureRecogniser::release(MGestureEngineIf* /*ge*/)
+{
+ m_pinching = false ;
+ m_listener->gestureExit(KUid) ;
+}
+
+/*!
+ * Now that we know the two points where the zooming started, we move those points only along
+ * the same line y = mx + b, so lets calculate m and b.
+ */
+void CPinchGestureRecogniser::calculateZoomingLine()
+{
+ int sX = m_pinchstart.iX ;
+ int sY = m_pinchstart.iY ;
+ int eX = m_pinchend.iX ;
+ int eY = m_pinchend.iY ;
+
+ if (eX == sX)
+ {
+ m_m = 0.f ;
+ }
+ else
+ {
+ m_m = float(eY-sY)/(eX-sX) ;
+ }
+ m_b = sY-(m_m*sX) ;
+}
+
+/*!
+ * calculate the distance, return as float
+ */
+float CPinchGestureRecogniser::calculateDistance()
+{
+ double x = ((m_pinchstart.iX-m_pinchend.iX)*(m_pinchstart.iX-m_pinchend.iX))+
+ ((m_pinchstart.iY-m_pinchend.iY)*(m_pinchstart.iY-m_pinchend.iY)) ;
+ double ddist ;
+ Math::Sqrt(ddist, x) ;
+ return float(ddist) ;
+}
+
+/*!
+ * Set the pinching speed as pixels / ms (meaning that in case of singletouch device
+ * the other finger looks like the EMove UI event suddenly jumps to new location;
+ * in resistive the new location is somewhere in the middle of the touches, in capacitive
+ * the driver seems to report three or four points:
+ * original (x,y), new (a,b) and also (a,y), sometimes (x,b)
+ */
+void CPinchGestureRecogniser::setPinchingSpeed(float aSpeed) __SOFTFP
+{
+ m_pinchingspeed = aSpeed ;
+}
+
+/*!
+ * Adjust the pinch move so that it will not be too jumpy
+ */
+int CPinchGestureRecogniser::adjustPinchMove(float& aPreviousDistance, float aNewDistance)
+{
+ float diff = aNewDistance - aPreviousDistance ;
+ float logdiff = diff ;
+ if (diff < 0) diff = -diff ; // Note that the next calculations need the positive diff value, but keep the original in logdiff
+ float changePercentage = (diff/aPreviousDistance)*100.f ;
+ if (changePercentage > 10.f)
+ {
+ // change more than 10%, make at most 10%
+ float newdiff = aPreviousDistance*0.1f ;
+ if (aPreviousDistance > aNewDistance) newdiff = -newdiff ;
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: adjustPinchMove from %f to %f : was, now %f %f",
+ double(logdiff), double(newdiff), double(aPreviousDistance), double(aNewDistance));
+ }
+
+ aPreviousDistance = aPreviousDistance + newdiff ;
+ diff = newdiff ;
+ }
+ else
+ {
+ if (m_loggingenabled)
+ {
+ LOGARG("CPinchGestureRecogniser: adjustPinchMove from %f to %f : was, now %f %f",
+ double(logdiff), double(diff), double(aPreviousDistance), double(aNewDistance));
+ }
+ aPreviousDistance = aNewDistance ; // accept the new value and update the new length
+ diff = logdiff ; // put the original back (this is why the logdiff can not be Abs(diff)!
+ }
+ return (int)diff ;
+}