/*
* 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 "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 "xngesturerecogniser.h"
#include "xngesturedefs.h"
#include "xnpointarray.h"
using namespace XnGestureHelper;
/**
* Vector class (math)
*/
NONSHARABLE_CLASS( TVector )
{
public:
/**
* Constructor
* @param aFrom starting point of the vector
* @param aTo ending point of the vector
*/
TVector( const TPoint& aFrom, const TPoint& aTo )
: iX( aTo.iX - aFrom.iX ),
iY( aTo.iY - aFrom.iY )
{
}
/** @return angle of the vector */
TReal Angle() const
{
TReal angle = 0;
TReal length = Length();
if ( length != 0 )
{
Math::ACos( angle, iX / Length() );
if ( iY < 0 )
{
angle = 2 * KPi - angle;
}
}
return Degrees( angle );
}
/** @return length of the vector */
TReal Length() const
{
TReal length = 0;
Math::Sqrt( length, iX * iX + iY * iY );
return length;
}
private:
/** @return radians in degrees */
inline TReal Degrees( TReal aRadians ) const
{
return aRadians * 180 / KPi;
}
public:
/// x coordinate that represent the vector
TReal iX;
/// y coordinate that represent the vector
TReal iY;
};
/**
* @return ETrue if points for a tap event
*/
inline TBool IsTap( const TXnPointArray& aPoints )
{
// with tap, the pointer is not allowed to leave the tap area and come back
// therefore, gesture length is not an acceptable test for tap, as it tests
// only the last point. therefore, check if *any* point is outside tap area.
TInt i = aPoints.Count(); // latest point if most likely to be outside tap area
while ( --i >= 0 )
{
// use the raw point (from which no axis has been filtered out)
// because tap should consider both axes even when Code() ignores one axis
if ( KSamePointTolerance < Abs( aPoints.Raw( i ).iX - aPoints.Raw( 0 ).iX ) ||
KSamePointTolerance < Abs( aPoints.Raw( i ).iY - aPoints.Raw( 0 ).iY ) )
{
return EFalse;
}
}
return ETrue;
}
/**
* @return Length of the gesture in points
*/
inline TReal GestureLength( const TXnPointArray& aPoints )
{
return TVector( aPoints[0], aPoints[aPoints.Count() - 1] ).Length();
}
/**
* @return ETrue if aAngleUnderTest is almost aAngle
* Closeness of the angles is controlled by KAngleTolerance
*/
static TBool IsNear( TReal aAngleUnderTest, TReal aAngle )
{
return aAngle - KAngleTolerance <= aAngleUnderTest &&
aAngleUnderTest <= aAngle + KAngleTolerance;
}
/**
* @return the angle as a direction flags of TGesture
*/
inline TXnGestureCode Direction( TReal aAngle )
{
TXnGestureCode direction = EGestureUnknown;
if ( IsNear( aAngle, 90.0 ) )
{
direction = EGestureSwipeDown;
}
else if ( IsNear( aAngle, 180.0 ) )
{
direction = EGestureSwipeLeft;
}
else if ( IsNear( aAngle, 270.0 ) )
{
direction = EGestureSwipeUp;
}
else if ( 360.0 - KAngleTolerance <= aAngle || aAngle <= KAngleTolerance )
{
direction = EGestureSwipeRight;
}
else // for lint warning
{
// unknown angle
}
return direction;
}
/** @return direction between points */
inline TXnGestureCode Direction( const TPoint& aFromPoint, const TPoint& aToPoint )
{
return Direction( TVector( aFromPoint, aToPoint ).Angle() );
}
/** @return overall direction between points */
static TXnGestureCode GeneralDirection( const TXnPointArray& aPoints )
{
return Direction( aPoints[0], aPoints[aPoints.Count() - 1]);
}
/**
* @return the last received point that is different that the latest point,
* or first point if no point is different than latest
*/
inline TPoint PreviousPoint( const TXnPointArray& aPoints )
{
TPoint latestPoint = aPoints[aPoints.Count() - 1];
TInt i = aPoints.Count() - 1;
while ( --i >= 0 )
{
if ( latestPoint != aPoints[i] )
{
return aPoints[i];
}
}
return aPoints[0];
}
/** @return direction between last two points */
inline TXnGestureCode LastDirection( const TXnPointArray& aPoints )
{
if ( aPoints.Count() > 1 )
{
// return direction between latest and previous points.
// pick the previous point that is different than the last point
// because while using an x or y filter array, more than one
// sequential points may look like the same point because
// the differing coordinate coordinate is filtered out. For example,
// if dragging left and slightly up, many y coordinates will have the
// same value, while only x differs.
return Direction( PreviousPoint( aPoints ), aPoints[aPoints.Count() - 1] );
}
return EGestureUnknown;
}
// ----------------------------------------------------------------------------
// Return gesture code of a gesture formed by a sequence of points
// ----------------------------------------------------------------------------
//
TXnGestureCode TXnGestureRecogniser::GestureCode( const TXnPointArray& aPoints ) const
{
__ASSERT_DEBUG( aPoints.Count() > 0, Panic( EGesturePanicIllegalLogic ) );
if (aPoints.Count() <= 0)
return EGestureUnknown;
if ( IsTap( aPoints ) )
{
return EGestureTap;
}
if ( GestureLength( aPoints ) >= KMinSwipeLength )
{
TXnGestureCode direction = GeneralDirection( aPoints );
if ( direction != LastDirection( aPoints ) )
{
direction = EGestureUnknown;
}
return direction;
}
// the pointer was moved but was either not moved far enough, or was
// brought back to close to the starting point
return EGestureUnknown;
}