mulwidgets/gesturehelper/src/gesture.cpp
branchRCL_3
changeset 19 4ea6f81c838a
parent 17 514d98f21c43
child 20 0e9bb658ef58
equal deleted inserted replaced
17:514d98f21c43 19:4ea6f81c838a
     1 /*
       
     2 * Copyright (c) 2008-2008 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:  Gesture class
       
    15 *
       
    16 */
       
    17 
       
    18 #include "gesture.h"
       
    19 
       
    20 #include <e32math.h>
       
    21 
       
    22 #include "gesturedefs.h"
       
    23 #include "utils.h"
       
    24 
       
    25 using namespace GestureHelper;
       
    26 
       
    27 /** 
       
    28  * Point array for which only x axis is relevant
       
    29  */
       
    30 class TXAxisPointArray : public TPointArray
       
    31     {
       
    32 public:
       
    33     TXAxisPointArray( const RArray< TPointEntry >& aPoints )
       
    34             : TPointArray( aPoints ) {}
       
    35         
       
    36     // from TPointArray
       
    37     TPoint operator[]( TInt aIndex ) const 
       
    38         {
       
    39         return TPoint( Raw( aIndex ).iX, 0 );
       
    40         }
       
    41     };
       
    42 
       
    43 /** 
       
    44  * Point array for which only y axis is relevant
       
    45  */
       
    46 class TYAxisPointArray : public TPointArray
       
    47     {
       
    48 public:
       
    49     TYAxisPointArray( const RArray< TPointEntry >& aPoints )
       
    50             : TPointArray( aPoints ) {}
       
    51     
       
    52     // from TPointArray
       
    53     TPoint operator[]( TInt aIndex ) const 
       
    54         {
       
    55         return TPoint( 0, Raw( aIndex ).iY );
       
    56         }
       
    57     };
       
    58 
       
    59 namespace 
       
    60     {
       
    61     /** @return the current time */
       
    62     TTime CurrentTime()
       
    63         {
       
    64         TTime time;
       
    65         time.UniversalTime();
       
    66         return time;
       
    67         }
       
    68 
       
    69     /**
       
    70      * @param aRelevantAxis See @ref MGestureEvent::Code
       
    71      * @return gesture code by analysing the sequence of points
       
    72      */
       
    73     TGestureCode CodeFromPoints( const RArray< TPointEntry >& aPoints, 
       
    74             MGestureEvent::TAxis aRelevantAxis ) 
       
    75         {
       
    76         // select the correct filter based on aRelevantAxis
       
    77         // these filter_ objects are array decorators that will eliminate either 
       
    78         // x, y or neither coordinate of each point
       
    79         TXAxisPointArray filterY( aPoints );
       
    80         TYAxisPointArray filterX( aPoints );
       
    81         TPointArray filterNone( aPoints );
       
    82         TPointArray& filter = 
       
    83             aRelevantAxis == MGestureEvent::EAxisHorizontal ? static_cast< TPointArray& >( filterY ) : 
       
    84             aRelevantAxis == MGestureEvent::EAxisVertical   ? static_cast< TPointArray& >( filterX ) :
       
    85             /* otherwise EAxisBoth */                         filterNone;
       
    86                             
       
    87         // currently the gesture recogniser does not have any state, so it is fast
       
    88         // to instantiate. The call is not static however, to allow the recogniser
       
    89         // to be replaced by a more complicated implementation that has state.
       
    90         // then it may make sense to make the recogniser a member variable.
       
    91         return TGestureRecogniser().GestureCode( filter, aRelevantAxis );
       
    92         }
       
    93 
       
    94     /**
       
    95      * @param aPoints Sequence of points representing 1st pointer movement
       
    96      * @param aSecondaryPoints Sequence of points representing 2nd pointer movement
       
    97      * @param aPreviousDistance contains the previous distance aftre this function execution 
       
    98      * @return gesture code by analysing the sequence of points
       
    99      */
       
   100     TGestureCode MultiTouchCode( const RArray< TPointEntry >& aPoints, 
       
   101             const RArray< TPointEntry >& aSecondaryPoints, TInt aPreviousDistance, TBool aIsFirstPinch ) 
       
   102         {
       
   103         TPointArray filter(aPoints);
       
   104         TPointArray secondaryFilter(aSecondaryPoints);
       
   105         return TGestureRecogniser().MultiTouchGestureCode( filter, secondaryFilter, aPreviousDistance, aIsFirstPinch );
       
   106         }
       
   107     } // unnamed namespace
       
   108 
       
   109 // ----------------------------------------------------------------------------
       
   110 // constructor
       
   111 // ----------------------------------------------------------------------------
       
   112 //
       
   113 CGesture::CGesture()
       
   114     {
       
   115     iPinchStartDistance = -1;
       
   116     iPinchEndDistance = -1;
       
   117     iPinchDetected = EFalse ; 
       
   118     }
       
   119 
       
   120 // ----------------------------------------------------------------------------
       
   121 // destructor
       
   122 // ----------------------------------------------------------------------------
       
   123 //
       
   124 CGesture::~CGesture()
       
   125     {
       
   126     iPoints.Close();
       
   127     iSecondaryPoints.Close();
       
   128     }
       
   129 
       
   130 // ----------------------------------------------------------------------------
       
   131 // AsStartEventL
       
   132 // ----------------------------------------------------------------------------
       
   133 //
       
   134 CGesture* CGesture::AsStartEventLC() const
       
   135     {
       
   136     __ASSERT_DEBUG( 0 < iPoints.Count(), Panic( EGesturePanicIllegalLogic ) );
       
   137     CGesture* gesture = new ( ELeave ) CGesture;
       
   138     CleanupStack::PushL( gesture );
       
   139     User::LeaveIfError( gesture->AddPoint( iPoints[0].iPos ) );
       
   140     return gesture;
       
   141     }
       
   142 
       
   143 // ----------------------------------------------------------------------------
       
   144 // ResetToLastPoint
       
   145 // ----------------------------------------------------------------------------
       
   146 //
       
   147 void CGesture::ResetToLastPoint(TBool aSetPointerZero,TBool aSetToZero)
       
   148     {
       
   149     TPointEntry lastEntry(TPoint(0,0),TTime());
       
   150     if( aSetToZero )
       
   151         {
       
   152         __ASSERT_DEBUG( 0 < iPoints.Count(), Panic( EGesturePanicIllegalLogic ) );
       
   153         lastEntry = LastPoint( iPoints);
       
   154         }
       
   155     else
       
   156         {
       
   157         __ASSERT_DEBUG( 0 < iSecondaryPoints.Count(), Panic( EGesturePanicIllegalLogic ) );
       
   158         lastEntry = LastPoint( iSecondaryPoints);
       
   159         }
       
   160     Reset();
       
   161     if( aSetPointerZero )
       
   162         {
       
   163         iPoints.Append(lastEntry);
       
   164         }
       
   165     else
       
   166         {
       
   167         iSecondaryPoints.Append(lastEntry);  
       
   168         }     
       
   169     }
       
   170 
       
   171 // ----------------------------------------------------------------------------
       
   172 // LastPoint
       
   173 // ----------------------------------------------------------------------------
       
   174 //
       
   175 inline const TPointEntry CGesture::LastPoint( const RArray< TPointEntry >& aPoints ) const
       
   176     {
       
   177     return aPoints[aPoints.Count() - 1];
       
   178     }
       
   179 
       
   180 // ----------------------------------------------------------------------------
       
   181 // IsPinchToleranceHigh
       
   182 // ----------------------------------------------------------------------------
       
   183 //
       
   184 inline TBool CGesture::IsHighPinchTolerance() const
       
   185     {
       
   186   
       
   187     if( iPinchEndDistance == -1 )
       
   188         {
       
   189         return ETrue;
       
   190         }
       
   191     else
       
   192         {
       
   193         TInt currentDistance = TGestureRecogniser().Length( LastPoint(iPoints).iPos, LastPoint(iSecondaryPoints).iPos );
       
   194         // if previously zooming out and current distance corresponds to zooming in or viceversa
       
   195         if( ( iZoomState == EZoomOut && currentDistance < iPinchStartDistance) || 
       
   196                 ( iZoomState == EZoomIn && currentDistance > iPinchStartDistance))
       
   197             {
       
   198             return ETrue;
       
   199             }
       
   200         }
       
   201     return EFalse;
       
   202     }
       
   203 // ----------------------------------------------------------------------------
       
   204 // Reset
       
   205 // ----------------------------------------------------------------------------
       
   206 //
       
   207 void CGesture::Reset()
       
   208     {
       
   209     iPoints.Reset();
       
   210     iSecondaryPoints.Reset();
       
   211     iHoldingState = ENotHolding;
       
   212     iState = ENotActive;
       
   213     iHoldingPointIndex = 0;
       
   214     iVisual = NULL;
       
   215     iIsDoubleTap = EFalse;
       
   216     iPinchStartDistance = -1;
       
   217     iPinchEndDistance = -1;
       
   218     iPinchDetected = EFalse;
       
   219     iZoomState = ENoZoom;
       
   220     }
       
   221     
       
   222 // ----------------------------------------------------------------------------
       
   223 // IsEmpty
       
   224 // ----------------------------------------------------------------------------
       
   225 //
       
   226 TBool CGesture::IsEmpty() const
       
   227     {
       
   228     return iPoints.Count() == 0;
       
   229     }
       
   230 
       
   231 // ----------------------------------------------------------------------------
       
   232 // IsMultiTouch
       
   233 // ----------------------------------------------------------------------------
       
   234 //
       
   235 TBool CGesture::IsMultiTouch() const
       
   236     {
       
   237     return iSecondaryPoints.Count() == 0;
       
   238     }
       
   239 
       
   240 // ----------------------------------------------------------------------------
       
   241 // Add a point to the sequence of points that together make up the gesture
       
   242 // ----------------------------------------------------------------------------
       
   243 //
       
   244 TInt CGesture::AddPoint( const TPoint& aPoint )
       
   245     {
       
   246     if ( !IsLatestPoint( aPoint ) )
       
   247         {
       
   248         return iPoints.Append( TPointEntry( aPoint, CurrentTime() ) );
       
   249         }
       
   250     return KErrNone;
       
   251     }
       
   252 
       
   253 // ----------------------------------------------------------------------------
       
   254 // AddSecondaryPoint
       
   255 // ----------------------------------------------------------------------------
       
   256 //
       
   257 TInt CGesture::AddSecondaryPoint( const TPoint& aPoint )
       
   258     {
       
   259     if ( !IsLatestSecondaryPoint( aPoint ) )
       
   260         {
       
   261         return iSecondaryPoints.Append( TPointEntry( aPoint, CurrentTime() ) );
       
   262         }
       
   263     return KErrNone;
       
   264     }
       
   265 
       
   266 // ----------------------------------------------------------------------------
       
   267 // SetVisual
       
   268 // ----------------------------------------------------------------------------
       
   269 //
       
   270 void CGesture::SetVisual( CAlfVisual* aVisual )
       
   271     {
       
   272     iVisual = aVisual;
       
   273     }
       
   274 
       
   275 // ----------------------------------------------------------------------------
       
   276 // IsNearHoldingPoint
       
   277 // ----------------------------------------------------------------------------
       
   278 //
       
   279 TBool CGesture::IsNearHoldingPoint( const TPoint& aPoint ) const
       
   280     {
       
   281     return ToleranceRect( iPoints[ iHoldingPointIndex ].iPos ).Contains( aPoint );
       
   282     }
       
   283 
       
   284 // ----------------------------------------------------------------------------
       
   285 // IsLatestPoint
       
   286 // ----------------------------------------------------------------------------
       
   287 //
       
   288 TBool CGesture::IsLatestPoint( const TPoint& aPoint ) const
       
   289     {
       
   290     if ( iPoints.Count() > 0 )
       
   291         {
       
   292         return aPoint == CurrentPos();
       
   293         }
       
   294     return EFalse;
       
   295     }
       
   296 // ----------------------------------------------------------------------------
       
   297 // IsLatestSecondaryPoint
       
   298 // ----------------------------------------------------------------------------
       
   299 //
       
   300 TBool CGesture::IsLatestSecondaryPoint( const TPoint& aPoint ) const
       
   301     {
       
   302     if ( iSecondaryPoints.Count() > 0 )
       
   303         {
       
   304         return aPoint == iSecondaryPoints[ iSecondaryPoints.Count() - 1 ].iPos;
       
   305         }
       
   306     return EFalse;
       
   307     }
       
   308 
       
   309 // ----------------------------------------------------------------------------
       
   310 // StartHolding
       
   311 // ----------------------------------------------------------------------------
       
   312 //
       
   313 void CGesture::StartHolding()
       
   314     {
       
   315     iHoldingState = EHoldStarting;
       
   316     
       
   317     // remove all points that were introduced after holding started
       
   318     for ( TInt i = iPoints.Count() - 1; i > iHoldingPointIndex; i-- )
       
   319         {
       
   320         iPoints.Remove( i );
       
   321         }
       
   322     }
       
   323 
       
   324 // ----------------------------------------------------------------------------
       
   325 // SetHoldingPoint
       
   326 // ----------------------------------------------------------------------------
       
   327 //
       
   328 void CGesture::SetHoldingPoint()
       
   329     {
       
   330     iHoldingPointIndex = iPoints.Count() - 1;
       
   331     }
       
   332 
       
   333 // ----------------------------------------------------------------------------
       
   334 // ContinueHolding
       
   335 // ----------------------------------------------------------------------------
       
   336 //
       
   337 void CGesture::ContinueHolding()
       
   338     {
       
   339     iHoldingState = EHolding;
       
   340     }
       
   341 
       
   342 // ----------------------------------------------------------------------------
       
   343 // SetSingleTouchActive
       
   344 // ----------------------------------------------------------------------------
       
   345 //
       
   346 void CGesture::SetSingleTouchActive()
       
   347     {
       
   348     __ASSERT_DEBUG( ENotActive == iState, Panic( EGesturePanicIllegalLogic ) );
       
   349     iState = ESingleTouchActive;
       
   350     }
       
   351 // ----------------------------------------------------------------------------
       
   352 // SetMultiTouchActive
       
   353 // ----------------------------------------------------------------------------
       
   354 //
       
   355 void CGesture::SetMultiTouchActive()
       
   356     {
       
   357     iState = EMultiTouchActive;
       
   358     iIsMultiTouched = ETrue;
       
   359     }
       
   360 // ----------------------------------------------------------------------------
       
   361 // SetSingleTouchReleased
       
   362 // ----------------------------------------------------------------------------
       
   363 //
       
   364 void CGesture::SetSingleTouchReleased()
       
   365     {
       
   366     // IsMovementStopped expects corresponding SetComplete to be called before SetRelease
       
   367     __ASSERT_DEBUG( ESingleTouchComplete == iState, Panic( EGesturePanicIllegalLogic ) );
       
   368     iState = ESingleTouchReleased;
       
   369     iIsMultiTouched = EFalse;
       
   370     }
       
   371 // ----------------------------------------------------------------------------
       
   372 // SetMultiTouchReleased
       
   373 // ----------------------------------------------------------------------------
       
   374 //
       
   375 void CGesture::SetMultiTouchReleased()
       
   376     {
       
   377     // IsMovementStopped expects corresponding SetComplete to be called before SetRelease
       
   378     __ASSERT_DEBUG( EMultiTouchComplete == iState, Panic( EGesturePanicIllegalLogic ) );
       
   379     iState = EMultiTouchReleased;
       
   380     }
       
   381 
       
   382 /**
       
   383  * @return elapsed time between aStartTime and aEndTime
       
   384  */
       
   385 inline TTimeIntervalMicroSeconds32 Elapsed( const TTime& aStartTime, 
       
   386                                             const TTime& aEndTime )
       
   387     {
       
   388     return aEndTime.MicroSecondsFrom( aStartTime ).Int64();
       
   389     }
       
   390 
       
   391 // ----------------------------------------------------------------------------
       
   392 // SetSingleTouchComplete
       
   393 // ----------------------------------------------------------------------------
       
   394 //
       
   395 void CGesture::SetSingleTouchComplete()
       
   396     {
       
   397     __ASSERT_DEBUG( iPoints.Count() > 0, Panic( EGesturePanicIllegalLogic ) );
       
   398     iState = ESingleTouchComplete;
       
   399     iCompletionTime = CurrentTime();
       
   400     }
       
   401 
       
   402 // ----------------------------------------------------------------------------
       
   403 // SetMultiTouchComplete
       
   404 // ----------------------------------------------------------------------------
       
   405 //
       
   406 void CGesture::SetMultiTouchComplete()
       
   407     {
       
   408     __ASSERT_DEBUG( iSecondaryPoints.Count() > 0, Panic( EGesturePanicIllegalLogic ) );
       
   409     iState = EMultiTouchComplete;
       
   410     }
       
   411 
       
   412 // ----------------------------------------------------------------------------
       
   413 // SetCancelled
       
   414 // ----------------------------------------------------------------------------
       
   415 //
       
   416 void CGesture::SetCancelled()
       
   417     {
       
   418     iState = ECancelled;
       
   419     }
       
   420 
       
   421 // ----------------------------------------------------------------------------
       
   422 // SetDoubleTap
       
   423 // ----------------------------------------------------------------------------
       
   424 //
       
   425 void CGesture::SetDoubleTap() 
       
   426     { 
       
   427     iIsDoubleTap = ETrue; 
       
   428     }
       
   429 
       
   430 // ----------------------------------------------------------------------------
       
   431 // IsTap
       
   432 // ----------------------------------------------------------------------------
       
   433 //
       
   434 TBool CGesture::IsTap() const
       
   435     {
       
   436     if(iIsMultiTouched)
       
   437         {
       
   438         return EFalse;
       
   439         }
       
   440     return CodeFromPoints( iPoints, EAxisBoth ) == EGestureTap;
       
   441     }
       
   442 
       
   443 // ----------------------------------------------------------------------------
       
   444 // IsPinch
       
   445 // ----------------------------------------------------------------------------
       
   446 //
       
   447 TBool CGesture::IsPinch()
       
   448     {
       
   449     if (iPinchStartDistance  == -1 )
       
   450         {
       
   451         iPinchStartDistance = TGestureRecogniser().Length( iPoints[0].iPos, iSecondaryPoints[0].iPos ) ;
       
   452         }
       
   453     // if there is a pinch detected in the last attempt then update the start distance.
       
   454     // henceforth this new distance is used as reference for calculating the pinch.
       
   455     
       
   456     if( iPinchDetected )
       
   457         {
       
   458         iPinchStartDistance = iPinchEndDistance;
       
   459         }
       
   460     iPinchDetected = MultiTouchCode( iPoints, iSecondaryPoints, iPinchStartDistance,IsHighPinchTolerance()) == EGesturePinch;
       
   461     if( iPinchDetected )
       
   462         {
       
   463         // This end distance is updated the first time the pinch is detected.
       
   464         // This is done the save the value of pinch end distnce for further refernce to
       
   465         // update the pinch start distance next time any pointer position changes.
       
   466         iPinchEndDistance = TGestureRecogniser().Length( 
       
   467                 LastPoint(iPoints).iPos,LastPoint(iSecondaryPoints).iPos );
       
   468         iZoomState = iPinchEndDistance > iPinchStartDistance ? EZoomOut : EZoomIn;
       
   469         }
       
   470     return iPinchDetected;
       
   471     }
       
   472 
       
   473 /**
       
   474  * Translates a non-holding code into a holding code
       
   475  * @param aCode original gesture code
       
   476  * @return a gesture code with hold flag applied
       
   477  */
       
   478 inline TGestureCode Hold( TGestureCode aCode )
       
   479     {
       
   480     if ( aCode != EGestureStart && 
       
   481          aCode != EGestureDrag && 
       
   482          aCode != EGestureReleased && 
       
   483          aCode != EGestureUnknown )
       
   484         {
       
   485         return static_cast< TGestureCode >( aCode | EFlagHold );
       
   486         }
       
   487     return aCode;
       
   488     }
       
   489     
       
   490 // ----------------------------------------------------------------------------
       
   491 // Code
       
   492 // ----------------------------------------------------------------------------
       
   493 //
       
   494 TGestureCode CGesture::Code( TAxis aRelevantAxis ) const
       
   495     {
       
   496     switch ( iState )
       
   497         {
       
   498         case ESingleTouchActive:           
       
   499             // "start" event if only first point received
       
   500             // need to check that not holding, in case user pressed stylus
       
   501             // down, and activated holding without moving the stylus
       
   502             if ( iPoints.Count() == 1 && !IsHolding() )
       
   503                 {
       
   504                 return EGestureStart;
       
   505                 }
       
   506             // "drag" event if holding not started or holding started earlier
       
   507             else if ( iHoldingState != EHoldStarting )
       
   508                 {
       
   509                 // select the correct filter based on aRelevantAxis
       
   510                 // these filter_ objects are array decorators that will eliminate either 
       
   511                 // x, y or neither coordinate of each point
       
   512                 TXAxisPointArray filterY( iPoints );
       
   513                 TYAxisPointArray filterX( iPoints );
       
   514                 TPointArray filterNone( iPoints );
       
   515                 TPointArray& filter = 
       
   516                     aRelevantAxis == MGestureEvent::EAxisHorizontal ? static_cast< TPointArray& >( filterY ) : 
       
   517                     aRelevantAxis == MGestureEvent::EAxisVertical   ? static_cast< TPointArray& >( filterX ) :
       
   518                     /* otherwise EAxisBoth */                         filterNone;
       
   519             
       
   520                 return TGestureRecogniser().ValidateDrag( filter, aRelevantAxis );
       
   521                 }
       
   522             // holding was just started
       
   523             else 
       
   524                 {
       
   525                 return Hold( CodeFromPoints( iPoints, aRelevantAxis ) );
       
   526                 }
       
   527             
       
   528         case EMultiTouchActive:
       
   529             // Only if there are some points in secondary array
       
   530             if ( iSecondaryPoints.Count() == 1 && iPoints.Count() == 1 )
       
   531                 {
       
   532                 return EGestureMultiTouchStart;
       
   533                 }
       
   534             else
       
   535                 {
       
   536                 return MultiTouchCode( iPoints, iSecondaryPoints,  iPinchStartDistance,IsHighPinchTolerance() );
       
   537                 }
       
   538 
       
   539         case ESingleTouchComplete:
       
   540 			{
       
   541             if ( iIsDoubleTap )
       
   542                 {
       
   543                 return EGestureDoubleTap;
       
   544                 }
       
   545             
       
   546             // If there was a mulitouch within the last gesture then ignore the tap
       
   547 			TGestureCode gestureCode = CodeFromPoints( iPoints, aRelevantAxis );
       
   548             if( gestureCode == EGestureTap && iIsMultiTouched)
       
   549                 {
       
   550                 return EGestureUnknown;
       
   551                 }
       
   552             return gestureCode;
       
   553             }
       
   554         case EMultiTouchComplete:
       
   555             return MultiTouchCode( iPoints, iSecondaryPoints, iPinchStartDistance,IsHighPinchTolerance());
       
   556             
       
   557         case ESingleTouchReleased:
       
   558             return EGestureReleased;
       
   559         case EMultiTouchReleased:
       
   560             return EGestureMultiTouchReleased;         
       
   561             
       
   562         case ECancelled: // fallthrough
       
   563         case ENotActive:
       
   564         default: 
       
   565             return EGestureUnknown;
       
   566         }
       
   567     }
       
   568 
       
   569 // ----------------------------------------------------------------------------
       
   570 // IsHolding
       
   571 // ----------------------------------------------------------------------------
       
   572 //
       
   573 TBool CGesture::IsHolding() const
       
   574     {
       
   575     return iHoldingState >= EHoldStarting;
       
   576     }
       
   577 
       
   578 // ----------------------------------------------------------------------------
       
   579 // StartPos
       
   580 // ----------------------------------------------------------------------------
       
   581 //
       
   582 TPoint CGesture::StartPos() const
       
   583     {
       
   584     // at least one point will be in the array during callback (pointer down pos)
       
   585     return iPoints[ 0 ].iPos;
       
   586     }
       
   587 
       
   588 // ----------------------------------------------------------------------------
       
   589 // CurrentPos
       
   590 // ----------------------------------------------------------------------------
       
   591 //
       
   592 TPoint CGesture::CurrentPos() const
       
   593     {
       
   594     // at least on point will be in the array during callback (pointer down pos)
       
   595     return iPoints[ iPoints.Count() - 1 ].iPos;
       
   596     }
       
   597 
       
   598 // ----------------------------------------------------------------------------
       
   599 // IsMovementStopped
       
   600 // ----------------------------------------------------------------------------
       
   601 //
       
   602 inline TBool CGesture::IsMovementStopped() const
       
   603     {
       
   604     // iCompletionTime is only only valid if client has called SetComplete 
       
   605     if ( iState >= ESingleTouchComplete )
       
   606         {
       
   607         return Elapsed( NthLastEntry( 1 ).iTime, iCompletionTime ).Int() > KSpeedStopTime;
       
   608         }
       
   609     return EFalse;
       
   610     }
       
   611 
       
   612 namespace 
       
   613     {
       
   614     const TInt KFloatingPointAccuracy = 0.000001;
       
   615     
       
   616     /** @return percentage (0.0-1.0) how far aPos is from aEdge1 towards aEdge2 */
       
   617     inline TReal32 Proportion( TReal32 aPos, TReal32 aEdge1, TReal32 aEdge2 )
       
   618         {
       
   619         if ( Abs( aEdge2 - aEdge1 ) > KFloatingPointAccuracy )
       
   620             {
       
   621             return ( aPos - aEdge1 ) / ( aEdge2 - aEdge1 );
       
   622             }
       
   623         return 0; // avoid division by zero 
       
   624         }
       
   625     
       
   626     /** Edges (pixels) at which speed should be -100% or 100% */
       
   627     NONSHARABLE_STRUCT( TEdges )
       
   628         {
       
   629         TReal32 iMin;
       
   630         TReal32 iMax;
       
   631         };
       
   632         
       
   633     /** 
       
   634      * scale which allows different (coordinate -> percentage) mapping
       
   635      * between -100% to 0% and 0 and 100%
       
   636      */
       
   637     NONSHARABLE_STRUCT( TScale )
       
   638         {
       
   639         TScale( TInt aZero, const TEdges& aEdges )
       
   640                 : iMin( aEdges.iMin ), iZero( aZero ), iMax( aEdges.iMax )
       
   641             {
       
   642             }
       
   643         
       
   644         /** @return aPos as a percentage between -100% and 100% in aScale */
       
   645         TReal32 Percent( TReal32 aPos ) const;
       
   646 
       
   647         /// coordinate where speed is -100%
       
   648         TReal32 iMin;
       
   649         /// coordinate where speed is 0%
       
   650         TReal32 iZero;
       
   651         /// coordinate where speed is 100%
       
   652         TReal32 iMax;
       
   653         };
       
   654         
       
   655     /** @convert aPos into a percentage between -100% and 100% in aScale */
       
   656     TReal32 TScale::Percent( TReal32 aPos ) const
       
   657         {
       
   658         TReal32 percent;
       
   659         if ( aPos < iZero )
       
   660             {
       
   661             // return negative percentages on the lower side of zero point
       
   662             percent = -1 * Proportion( aPos, iZero, iMin );
       
   663             }
       
   664         else 
       
   665             {
       
   666             percent = Proportion( aPos, iZero, iMax );
       
   667             }
       
   668         // constrain between -100% and 100%
       
   669         return Min( Max( percent, -1.0F ), 1.0F );
       
   670         }
       
   671     
       
   672     /** Scale in x and y dimensions */
       
   673     NONSHARABLE_STRUCT( TScale2D )
       
   674         {
       
   675         TRealPoint Percent( const TPoint& aPos ) const
       
   676             {
       
   677             return TRealPoint( iX.Percent( aPos.iX ),
       
   678                                iY.Percent( aPos.iY ) );
       
   679             }
       
   680                 
       
   681         TScale iX;
       
   682         TScale iY;
       
   683         };
       
   684             
       
   685     enum TDirection { ESmaller, ELarger };
       
   686     
       
   687     /** @return the direction of pos compared to the previous pos */
       
   688     inline TDirection Direction( TInt aPos, TInt aPreviousPos )
       
   689         {
       
   690         return aPos < aPreviousPos ? ESmaller : ELarger;    
       
   691         }
       
   692 
       
   693     /** Direction in x and y dimensions */
       
   694     NONSHARABLE_STRUCT( TDirection2D )
       
   695         {
       
   696         TDirection iX;
       
   697         TDirection iY;
       
   698         };
       
   699 
       
   700     /** Return the direction (up/down) of signal at aIndex */
       
   701     inline TDirection2D Direction( TInt aIndex, const RArray< TPointEntry >& aPoints )
       
   702         {
       
   703         const TPoint& pos = aPoints[ aIndex ].iPos;
       
   704         const TPoint& prevPos = aPoints[ aIndex - 1 ].iPos;
       
   705         TDirection2D dir = { Direction( pos.iX, prevPos.iX ),
       
   706                              Direction( pos.iY, prevPos.iY ) };
       
   707         return dir;
       
   708         }   
       
   709     /** 
       
   710      * @return a position in the aLow and aHigh, so that it aProportion of
       
   711      *         of length is above the pos 
       
   712      */
       
   713     TReal32 ProportionalLength( TReal32 aLow, TReal32 aHigh, TReal32 aProportion )
       
   714         {
       
   715         return ( aHigh - aLow ) * aProportion / ( 1 + aProportion );
       
   716         }
       
   717     
       
   718     /** 
       
   719      * @return aVariableEdge scaled to new position, when the other edge changes
       
   720      *         from aOldEdge to aNewEdge, so that aOrigin maintains the *same relative
       
   721      *         position* between aVariableEdge and the other edge 
       
   722      */
       
   723     inline TReal32 ScaledEdge( TReal32 aOrigin, TReal32 aVariableEdge, 
       
   724             TReal32 aOldEdge, TReal aNewEdge )
       
   725         {
       
   726         TReal32 proportion = Proportion( aOrigin, aVariableEdge, aOldEdge );
       
   727         return ( proportion * aNewEdge - aOrigin ) / ( proportion - 1 );
       
   728         }
       
   729     
       
   730     TScale Rescale( TReal32 aPos, TDirection aDir, TDirection aPrevDir, 
       
   731             const TScale& aPrevScale, const TEdges& aEdges )
       
   732         {
       
   733         TScale scale( aPrevScale );
       
   734         if ( aPrevDir != aDir )
       
   735             {
       
   736             // the code duplication is accepted here, since it is difficult to factor out
       
   737             // while maintaining the understandability of this anyway complex algorithm
       
   738             if ( aDir == ESmaller )
       
   739                 {
       
   740                 scale.iMin = aEdges.iMin;
       
   741                 if ( aPrevScale.iZero < aPos )
       
   742                     {
       
   743                     TReal32 proportionAboveZero = Proportion( aPos, aPrevScale.iZero, aPrevScale.iMax );
       
   744                     scale.iZero = aPos - ProportionalLength( aEdges.iMin, aPos, proportionAboveZero );
       
   745                     }
       
   746                 else 
       
   747                     {
       
   748                     // adjust zero pos so that proportion between aPos, Min, and Zero pos 
       
   749                     // stay the same (Min will move to 0, aPos stays the same)
       
   750                     scale.iZero = ScaledEdge( aPos, aPrevScale.iZero, 
       
   751                         aPrevScale.iMin, aEdges.iMin );
       
   752                     }
       
   753                 // adjust the upper edge to take into account the movement of zero pos
       
   754                 scale.iMax = ScaledEdge( aPos, aPrevScale.iMax, 
       
   755                     aPrevScale.iZero, scale.iZero );
       
   756                 }
       
   757             else // ELarger
       
   758                 {
       
   759                 scale.iMax = aEdges.iMax;
       
   760                 if ( aPos < aPrevScale.iZero )
       
   761                     {
       
   762                     TReal32 proportionBelowZero = Proportion( aPos, aPrevScale.iZero, aPrevScale.iMin );
       
   763                     scale.iZero = aPos + ProportionalLength( aPos, aEdges.iMax, proportionBelowZero );
       
   764                     }
       
   765                 else
       
   766                     {
       
   767                     // adjust zero pos so that proportion between aPos, Max, and Zero pos 
       
   768                     // stay the same (Max will move edge, aPos stays the same)
       
   769                     scale.iZero = ScaledEdge( aPos, aPrevScale.iZero, 
       
   770                         aPrevScale.iMax, aEdges.iMax );
       
   771                     }
       
   772                 // adjust the lower edge to take into account the movement of zero pos
       
   773                 scale.iMin = ScaledEdge( aPos, aPrevScale.iMin, 
       
   774                     aPrevScale.iZero, scale.iZero );
       
   775                 }
       
   776             }
       
   777         return scale;
       
   778         }
       
   779      
       
   780     /** Edges in x and y dimensions */
       
   781     NONSHARABLE_STRUCT( TEdges2D )
       
   782         {
       
   783         TEdges iX;
       
   784         TEdges iY;
       
   785         };
       
   786     
       
   787     /** 
       
   788      * @param aEdges edges of the area in which gesture points are accepted
       
   789      * @return the scale of latest point in the list of points 
       
   790      */
       
   791     TScale2D Scale( const RArray< TPointEntry >& aPoints, const TEdges2D& aEdges )
       
   792         {
       
   793         TScale2D scale = { TScale( aPoints[0].iPos.iX, aEdges.iX ),
       
   794                            TScale( aPoints[0].iPos.iY, aEdges.iY ) };
       
   795         TInt count = aPoints.Count();
       
   796         if ( count > 1 )
       
   797             {
       
   798             // iterate the whole point list to arrive to the current scale
       
   799             TDirection2D dir( Direction( 1, aPoints ) );
       
   800             for ( TInt i = 1; i < count; i++ )
       
   801                 {
       
   802                 // get direction at i
       
   803                 TDirection2D newDir( Direction( i, aPoints ) );
       
   804                 // get new scale at i
       
   805                 scale.iX = Rescale( aPoints[i - 1].iPos.iX, newDir.iX, dir.iX, scale.iX, aEdges.iX );
       
   806                 scale.iY = Rescale( aPoints[i - 1].iPos.iY, newDir.iY, dir.iY, scale.iY, aEdges.iY );
       
   807                 dir = newDir;
       
   808                 }
       
   809             }
       
   810         return scale;
       
   811         }
       
   812     } // unnamed namespace
       
   813 
       
   814 TRealPoint CGesture::SpeedPercent( const TRect& aEdges ) const
       
   815     {
       
   816     // x and y coordinates are easier to handle separately, extract from TRect:
       
   817     // ((iMinX, iMinY), (iMaxX, iMaxY)) -> ((iMinX, iMaxX), (iMinY, iMaxY))
       
   818     TEdges2D edges = { { aEdges.iTl.iX, aEdges.iBr.iX },
       
   819                        { aEdges.iTl.iY, aEdges.iBr.iY } };
       
   820     // work out the current scale (coordinate -> percentage mapping) from 
       
   821     // the history of points (i.e., points of current gesture). Then
       
   822     // calculate the percentage of the current position.
       
   823     return Scale( iPoints, edges ).Percent( CurrentPos() );
       
   824     }
       
   825     
       
   826 // ----------------------------------------------------------------------------
       
   827 // Speed
       
   828 // ----------------------------------------------------------------------------
       
   829 //
       
   830 TRealPoint CGesture::Speed() const
       
   831     {
       
   832     const TReal32 KMicroSecondsInSecond = 1000000;
       
   833     
       
   834     // Speed is only evaluated at the end of the swipe
       
   835     // if user stops at the end of the swipe before lifting stylus,
       
   836     // speed is zero. If time is zero, return 0 speed (infinite does 
       
   837     // not make sense either). Will need to consider also earlier points 
       
   838     // and their times or start time, if this zero-speed behavior is a problem
       
   839     TRealPoint speed;
       
   840     TReal32 time = static_cast<TReal32>( TimeFromPreviousPoint().Int() ) 
       
   841         / KMicroSecondsInSecond;
       
   842     if ( !IsMovementStopped() && time > 0 )
       
   843         {
       
   844         TPoint distance = CurrentPos() - PreviousPos();
       
   845         speed.iX = static_cast<TReal32>( distance.iX ) / time;
       
   846         speed.iY = static_cast<TReal32>( distance.iY ) / time;
       
   847         }
       
   848     return speed;
       
   849     }
       
   850 
       
   851 // ----------------------------------------------------------------------------
       
   852 // Distance
       
   853 // ----------------------------------------------------------------------------
       
   854 //
       
   855 TPoint CGesture::Distance() const
       
   856     {
       
   857     return CurrentPos() - StartPos();
       
   858     }
       
   859 
       
   860 // ----------------------------------------------------------------------------
       
   861 // Visual
       
   862 // ----------------------------------------------------------------------------
       
   863 //
       
   864 CAlfVisual* CGesture::Visual() const
       
   865     {
       
   866     return iVisual;
       
   867     }
       
   868 
       
   869 // ----------------------------------------------------------------------------
       
   870 // TimeFromPreviousPoint
       
   871 // ----------------------------------------------------------------------------
       
   872 //
       
   873 inline TTimeIntervalMicroSeconds32 CGesture::TimeFromPreviousPoint() const
       
   874     {
       
   875     const TInt KLatestEntryOffset = 1;
       
   876     return Elapsed( PreviousEntry().iTime, NthLastEntry( KLatestEntryOffset ).iTime );
       
   877     }
       
   878 
       
   879 // ----------------------------------------------------------------------------
       
   880 // return nth point from the end of the points array
       
   881 // ----------------------------------------------------------------------------
       
   882 //
       
   883 inline const TPointEntry& CGesture::NthLastEntry( TInt aOffset ) const
       
   884     {
       
   885     return iPoints[ Max( iPoints.Count() - aOffset, 0 ) ];
       
   886     }
       
   887 
       
   888 // ----------------------------------------------------------------------------
       
   889 // PreviousEntry
       
   890 // ----------------------------------------------------------------------------
       
   891 //
       
   892 inline const TPointEntry& CGesture::PreviousEntry() const
       
   893     {
       
   894     return NthLastEntry( KPreviousPointOffset );
       
   895     }
       
   896 
       
   897 // ----------------------------------------------------------------------------
       
   898 // PreviousPos
       
   899 // ----------------------------------------------------------------------------
       
   900 //
       
   901 inline TPoint CGesture::PreviousPos() const
       
   902     {
       
   903     return PreviousEntry().iPos;
       
   904     }
       
   905 
       
   906 // ----------------------------------------------------------------------------
       
   907 // PinchPercent
       
   908 // ----------------------------------------------------------------------------
       
   909 //
       
   910 TInt CGesture::PinchPercent() const
       
   911     {
       
   912 	// Added 0.5 to avoid 5.7 getting rounded off to 5.
       
   913     return (iPinchEndDistance*100/iPinchStartDistance) + 0.5;	
       
   914     }
       
   915 
       
   916 // ----------------------------------------------------------------------------
       
   917 // PinchCentrePoint
       
   918 // ----------------------------------------------------------------------------
       
   919 //
       
   920 TPoint CGesture::PinchCentrePoint() const
       
   921     {
       
   922     if( iPoints.Count() <= 0 || iSecondaryPoints.Count() <= 0 )
       
   923         {
       
   924         return TPoint(0,0);
       
   925         }
       
   926     return TPoint( (iPoints[0].iPos.iX + iSecondaryPoints[0].iPos.iX)/2, (iPoints[0].iPos.iY + iSecondaryPoints[0].iPos.iY)/2);
       
   927     }
       
   928 
       
   929 // end of file
       
   930