(PanGesture) Added environment variable "PAN_PREDICTION_MODE" for selecting gesture... 56/19956/1
authorJulien Heanley <j.heanley@partner.samsung.com>
Fri, 4 Apr 2014 10:24:37 +0000 (11:24 +0100)
committerAndrew Cox <andrew.cox@partner.samsung.com>
Wed, 23 Apr 2014 18:39:21 +0000 (19:39 +0100)
Change-Id: I726d7567c67f7ecdec18c102d788d932500d1b43
Signed-off-by: Andrew Cox <andrew.cox@partner.samsung.com>
dali/integration-api/profiling.cpp
dali/integration-api/profiling.h
dali/internal/event/events/gesture-event-processor.cpp
dali/internal/event/events/gesture-event-processor.h
dali/internal/event/events/pan-gesture-processor.cpp
dali/internal/event/events/pan-gesture-processor.h
dali/internal/update/gestures/scene-graph-pan-gesture.cpp
dali/internal/update/gestures/scene-graph-pan-gesture.h

index 5642215..3ee01c5 100644 (file)
@@ -49,6 +49,12 @@ void EnableProfiling( ProfilingType type )
   }
 }
 
+void SetPanGesturePredictionMode( int mode )
+{
+  GestureEventProcessor& eventProcessor = ThreadLocalStorage::Get().GetGestureEventProcessor();
+  eventProcessor.SetPanGesturePredictionMode(mode);
+}
+
 } // namespace Integration
 
 } // namespace Dali
index e63adeb..3bd3d94 100644 (file)
@@ -43,6 +43,15 @@ enum ProfilingType
  */
 DALI_IMPORT_API void EnableProfiling( ProfilingType type );
 
+/**
+ * @brief Called by adaptor to set the pan gesture prediction mode from
+ * an environment variable
+ *
+ * @pre Should be called after Core creation.
+ * @param mode The pan gesture prediction mode.
+ */
+DALI_IMPORT_API void SetPanGesturePredictionMode( int mode );
+
 } // namespace Integration
 
 } // namespace Dali
index 1b6c4c6..4b1f4c5 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/integration-api/render-controller.h>
 #include <dali/internal/event/common/stage-impl.h>
 #include <dali/internal/event/events/pinch-gesture-detector-impl.h>
+#include <dali/internal/update/gestures/scene-graph-pan-gesture.h>
 #include <dali/public-api/events/pan-gesture.h>
 
 namespace Dali
@@ -228,6 +229,11 @@ void GestureEventProcessor::EnablePanGestureProfiling()
   mPanGestureProcessor.EnableProfiling();
 }
 
+void GestureEventProcessor::SetPanGesturePredictionMode(int mode)
+{
+  mPanGestureProcessor.SetPredictionMode(mode);
+}
+
 } // namespace Internal
 
 } // namespace Dali
index 1ba7fa4..4b944cb 100644 (file)
@@ -121,6 +121,13 @@ public: // Called by Core
    */
   void EnablePanGestureProfiling();
 
+  /**
+   * @brief Called to set how pan gestures predict and smooth input
+   *
+   * @param[in] mode The prediction mode to use
+   */
+  void SetPanGesturePredictionMode( int mode );
+
 private:
 
   // Undefined
index 6995854..1644f7b 100644 (file)
@@ -422,6 +422,17 @@ void PanGestureProcessor::EnableProfiling()
   mSceneObject->EnableProfiling();
 }
 
+void PanGestureProcessor::SetPredictionMode(int mode)
+{
+  if( (mode < 0)
+      || (mode >= SceneGraph::PanGesture::NUM_PREDICTION_MODES) )
+  {
+    mode = SceneGraph::PanGesture::DEFAULT_PREDICTION_MODE;
+  }
+  SceneGraph::PanGesture::PredictionMode modeEnum = static_cast<SceneGraph::PanGesture::PredictionMode>(mode);
+  mSceneObject->SetPredictionMode(modeEnum);
+}
+
 void PanGestureProcessor::UpdateDetection()
 {
   DALI_ASSERT_DEBUG(!mGestureDetectors.empty());
index ddbbdab..a75362d 100644 (file)
@@ -112,6 +112,19 @@ public: // To be called by GestureEventProcessor
    */
   void EnableProfiling();
 
+  /**
+   * Called to set the prediction mode for pan gestures
+   *
+   * @param[in] mode The prediction mode
+   *
+   * Valid modes:
+   * 0 - No prediction
+   * 1 - Average Smoothing (no actual prediction)
+   * 2 - Interpolation using last vsync time and event time
+   * 3 - Same as 2 for now, in progress
+   */
+  void SetPredictionMode(int mode);
+
 private:
 
   // Undefined
index 5de7928..1b9c71f 100644 (file)
@@ -30,17 +30,16 @@ namespace Internal
 
 namespace SceneGraph
 {
-
 namespace
 {
-
-const unsigned int ARRAY_SIZE( 4u );
-
 const unsigned int UPDATES_BETWEEN_PRINT( 120u );
 unsigned int UPDATE_COUNT( 0u );
-
+const int MAX_GESTURE_AGE = 100; ///< maximum age of a gesture before disallowing its use in algorithm
 } // unnamed namespace
 
+const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::AVERAGE;
+const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_2 + 1;
+
 PanGesture* PanGesture::New()
 {
   return new PanGesture();
@@ -55,91 +54,280 @@ void PanGesture::AddGesture( const Dali::PanGesture& gesture )
 {
   mGestures[ mWritePosition ] = gesture;
 
-  // Update our write position (so now this new value can be read)
-  // Note: we want to overwrite oldest gesture information even if it hasn't been read.
+  // Update our write position.
   ++mWritePosition;
-  mWritePosition %= ARRAY_SIZE;
+  mWritePosition %= PAN_GESTURE_HISTORY;
+}
+
+void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, uint currentTime, uint maxAge, uint minEvents)
+{
+  PanInfoHistoryConstIter endIter = panHistory.end();
+  PanInfoHistoryIter iter = panHistory.begin();
+  while( iter != endIter && panHistory.size() > minEvents)
+  {
+    PanInfo currentGesture = *iter;
+    if( currentTime < currentGesture.time + maxAge )
+    {
+      break;
+    }
+    panHistory.erase(iter);
+    endIter = panHistory.end();
+  }
+}
+
+void PanGesture::SimpleAverageAlgorithm(bool justStarted, PanInfo& gestureOut)
+{
+  if( mInGesture )
+  {
+    if( !justStarted )
+    {
+      gestureOut.screen.position += mLatestGesture.screen.position;
+      gestureOut.local.position += mLatestGesture.local.position;
+      gestureOut.screen.position *= 0.5f;
+      gestureOut.local.position *= 0.5f;
+      // make current displacement relative to previous update-frame now.
+      gestureOut.screen.displacement -= mLatestGesture.screen.displacement;
+      gestureOut.local.displacement -= mLatestGesture.local.displacement;
+    }
+  }
+}
+
+void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
+{
+  RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 1);
+  size_t panHistorySize = panHistory.size();
+
+  PanInfoHistoryConstIter endIter = panHistory.end();
+  PanInfoHistoryIter iter = panHistory.end() - (panHistorySize > 2 ? 2 : panHistorySize);
+  if( panHistorySize >= 2 )
+  {
+    // create average velocity and acceleration
+    // gestureOut is the combination of gesture events from this frame
+    PanInfo lastGesture = *iter;
+    ++iter;
+    float previousAccel = 0.0f;
+    while( iter != endIter )
+    {
+      PanInfo currentGesture = *iter;
+      lastGesture = *(iter - 1);
+      float velMag = currentGesture.screen.velocity.Length();
+      float velDiff = velMag - lastGesture.screen.velocity.Length();
+      float acceleration = 0.0f;
+      float time = (float)(currentGesture.time - lastGesture.time);
+      if( time >= Math::MACHINE_EPSILON_1 )
+      {
+        acceleration = velDiff / time;
+      }
+      float interpolationTime = (float)((int)lastVSyncTime - (int)currentGesture.time);
+      float newVelMag = velMag + (((acceleration + previousAccel) * 0.5f) * interpolationTime);
+      float velMod = 1.0f;
+      if( velMag > Math::MACHINE_EPSILON_1 )
+      {
+        velMod = newVelMag / velMag;
+      }
+      gestureOut.screen.velocity *= velMod;
+      gestureOut.local.velocity *= velMod;
+      lastGesture = *iter;
+      previousAccel = acceleration;
+      ++iter;
+    }
+  }
+  // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
+  // add interpolated distance and position to current
+  float interpolationTime = (float)((int)lastVSyncTime - (int)gestureOut.time);
+  // work out interpolated velocity
+  gestureOut.screen.displacement = (gestureOut.screen.velocity * interpolationTime);
+  gestureOut.local.displacement = (gestureOut.local.velocity * interpolationTime);
+  gestureOut.screen.position += gestureOut.screen.displacement;
+  gestureOut.local.position += gestureOut.local.displacement;
+  gestureOut.time += interpolationTime;
+}
+
+void PanGesture::PredictiveAlgorithm2(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
+{
+  // TODO - adapt PredictiveAlgorithm1 with better smoothing, still under development
+  RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 1);
+  size_t panHistorySize = panHistory.size();
+
+  PanInfoHistoryConstIter endIter = panHistory.end();
+  PanInfoHistoryIter iter = panHistory.end() - (panHistorySize > 2 ? 2 : panHistorySize);
+  if( panHistorySize >= 2 )
+  {
+    // create average velocity and acceleration
+    // gestureOut is the combination of gesture events from this frame
+    PanInfo lastGesture = *iter;
+    ++iter;
+    float previousAccel = 0.0f;
+    while( iter != endIter )
+    {
+      PanInfo currentGesture = *iter;
+      lastGesture = *(iter - 1);
+      float velMag = currentGesture.screen.velocity.Length();
+      float velDiff = velMag - lastGesture.screen.velocity.Length();
+      float acceleration = 0.0f;
+      float time = (float)(currentGesture.time - lastGesture.time);
+      if( time >= Math::MACHINE_EPSILON_1 )
+      {
+        acceleration = velDiff / time;
+      }
+      float interpolationTime = (float)((int)lastVSyncTime - (int)currentGesture.time);
+      float newVelMag = velMag + (((acceleration + previousAccel) * 0.5f) * interpolationTime);
+      float velMod = 1.0f;
+      if( velMag > Math::MACHINE_EPSILON_1 )
+      {
+        velMod = newVelMag / velMag;
+      }
+      gestureOut.screen.velocity *= velMod;
+      gestureOut.local.velocity *= velMod;
+      lastGesture = *iter;
+      previousAccel = acceleration;
+      ++iter;
+    }
+  }
+  // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
+  // add interpolated distance and position to current
+  float interpolationTime = (float)((int)lastVSyncTime - (int)gestureOut.time);
+  // work out interpolated velocity
+  gestureOut.screen.displacement = (gestureOut.screen.velocity * interpolationTime);
+  gestureOut.local.displacement = (gestureOut.local.velocity * interpolationTime);
+  gestureOut.screen.position += gestureOut.screen.displacement;
+  gestureOut.local.position += gestureOut.local.displacement;
+  gestureOut.time += interpolationTime;
 }
 
 void PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
 {
-  unsigned int time( 0u );
+  if( !mInGesture )
+  {
+    // clear current pan history
+    mPanHistory.clear();
+  }
+
+  // create an event for this frame
   bool justStarted ( false );
   bool justFinished ( false );
-
-  // If our read position is not same as write position, then there
-  // must be new values on circular buffer to read.
+  bool eventFound( false );
+
+  // Not going through array from the beginning, using it as a circular buffer and only using unread
+  // values.
+  int eventsThisFrame = 0;
+  // set nextGesture to last gesture so it's position is correct and velocity is same as last frame
+  mEventGesture = mLatestGesture;
+  // add new gestures and work out one full gesture for the frame
   while(mReadPosition != mWritePosition)
   {
-    const PanInfo& currentGesture = mGestures[mReadPosition];
+    // Copy the gesture first
+    PanInfo currentGesture(mGestures[mReadPosition]);
 
     if( mProfiling )
     {
       mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position ) );
     }
 
-    if ( currentGesture.time time )
+    if ( currentGesture.time < mEventGesture.time )
     {
-      justStarted |= (currentGesture.state == Gesture::Started);
-      justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled);
-
-      // use position values direct from gesture.
-      mLatestGesture.screen.position = currentGesture.screen.position;
-      mLatestGesture.local.position = currentGesture.local.position;
+      break;
+    }
 
-      // change displacement to be relative to initial touch, instead of relative to last touch-frame.
-      if(currentGesture.state == Gesture::Started)
-      {
-        mLatestGesture.screen.displacement = currentGesture.screen.displacement;
-        mLatestGesture.local.displacement = currentGesture.local.displacement;
-      }
-      else
-      {
-        mLatestGesture.screen.displacement += currentGesture.screen.displacement;
-        mLatestGesture.local.displacement += currentGesture.local.displacement;
-      }
+    mEventGesture.local.position = currentGesture.local.position;
+    mEventGesture.local.velocity = currentGesture.local.velocity;
+    mEventGesture.screen.position = currentGesture.screen.position;
+    mEventGesture.screen.velocity = currentGesture.screen.velocity;
+    if( !eventFound )
+    {
+      mEventGesture.local.displacement = currentGesture.local.displacement;
+      mEventGesture.screen.displacement = currentGesture.screen.displacement;
+      eventFound = true;
+    }
+    else
+    {
+      mEventGesture.local.displacement += currentGesture.local.displacement;
+      mEventGesture.screen.displacement += currentGesture.screen.displacement;
+    }
+    mEventGesture.time = currentGesture.time;
 
-      time = currentGesture.time;
+    // add event to history
+    mPanHistory.push_back(currentGesture);
+    justStarted |= (currentGesture.state == Gesture::Started);
+    if( currentGesture.state == Gesture::Started )
+    {
+      justStarted = true;
+      // clear just finished as we have started new pan
+      justFinished = false;
     }
+    justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled);
 
+    // Update our read position.
     ++mReadPosition;
-    mReadPosition %= ARRAY_SIZE;
+    ++eventsThisFrame;
+    mReadPosition %= PAN_GESTURE_HISTORY;
   }
 
+  // create PanInfo to pass into prediction method
+  PanInfo nextGesture(mEventGesture);
+
   mInGesture |= justStarted;
 
+  bool updateProperties = false;
+
   if ( mInGesture )
   {
-    PanInfo gesture(mLatestGesture);
-
     if( mProfiling )
     {
-      mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, gesture.screen.position ) );
+      mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, mLatestGesture.screen.position ) );
     }
 
-    if( !justStarted ) // only use previous frame if this is the continuing.
+    switch( mPredictionMode )
     {
-      // If previous gesture exists, then produce position as 50/50 interpolated blend of these two points.
-      gesture.screen.position += mPreviousGesture.screen.position;
-      gesture.local.position += mPreviousGesture.local.position;
-      gesture.screen.position *= 0.5f;
-      gesture.local.position *= 0.5f;
-      // make current displacement relative to previous update-frame now.
-      gesture.screen.displacement -= mPreviousGesture.screen.displacement;
-      gesture.local.displacement -= mPreviousGesture.local.displacement;
+      case NONE:
+      {
+        updateProperties = eventFound;
+        // no prediction, just using latest event info
+        mLatestGesture = mEventGesture;
+        break;
+      }
+      case AVERAGE:
+      {
+        SimpleAverageAlgorithm(justStarted, nextGesture);
+        // make latest gesture equal to current gesture after averaging
+        updateProperties = true;
+        break;
+      }
+      case PREDICTION_1:
+      {
+        // make latest gesture equal to current gesture before interpolation
+        mLatestGesture = nextGesture;
+        PredictiveAlgorithm1(eventsThisFrame, nextGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
+        updateProperties = true;
+        break;
+      }
+      case PREDICTION_2:
+      {
+        // make latest gesture equal to current gesture before interpolation
+        mLatestGesture = nextGesture;
+        PredictiveAlgorithm2(eventsThisFrame, nextGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
+        updateProperties = true;
+        break;
+      }
     }
 
-    if( mProfiling )
+    // always keep latest gesture up to date with event gesture
+    mLatestGesture = mEventGesture;
+
+    if( updateProperties )
     {
-      mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( lastVSyncTime, gesture.screen.position ) );
+      // only update properties if event received
+      // set latest gesture to raw pan info with unchanged time
+      mScreenPosition.Set( nextGesture.screen.position );
+      mScreenDisplacement.Set( nextGesture.screen.displacement );
+      mLocalPosition.Set( nextGesture.local.position );
+      mLocalDisplacement.Set( nextGesture.local.displacement );
     }
 
-    mPreviousGesture = mLatestGesture;
-
-    mScreenPosition.Set( gesture.screen.position );
-    mScreenDisplacement.Set( gesture.screen.displacement );
-    mLocalPosition.Set( gesture.local.position );
-    mLocalDisplacement.Set( gesture.local.displacement );
+    if( mProfiling )
+    {
+      mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( nextGesture.time, nextGesture.screen.position ) );
+    }
   }
 
   mInGesture &= ~justFinished;
@@ -194,6 +382,7 @@ PanGesture::PanGesture()
   mWritePosition( 0 ),
   mReadPosition( 0 ),
   mInGesture( false ),
+  mPredictionMode(DEFAULT_PREDICTION_MODE),
   mProfiling( NULL )
 {
 }
index b32e2f0..1d0d6a0 100644 (file)
@@ -43,81 +43,16 @@ class PanGesture : public PropertyOwner
 {
 public:
 
-  /**
-   * Create a new PanGesture
-   */
-  static PanGesture* New();
-
-  /**
-   * Virtual destructor
-   */
-  virtual ~PanGesture();
-
-  /**
-   * Adds a PanGesture to the internal circular-buffer waiting to be handled by UpdateProperties.
-   * @param[in]  gesture  The latest pan gesture.
-   */
-  void AddGesture( const Dali::PanGesture& gesture );
-
-  /**
-   * Called by the update manager so that we can update the value of our properties.
-   * @param[in]  lastVSyncTime  The last VSync time (in milliseconds).
-   * @param[in]  nextVSyncTime  The estimated time of the next VSync (in milliseconds).
-   */
-  virtual void UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime );
-
-  /**
-   * Retrieves a reference to the screen position property.
-   * @return The screen position property.
-   */
-  const GesturePropertyVector2& GetScreenPositionProperty() const;
-
-  /**
-   * Retrieves a reference to the screen displacement property.
-   * @return The screen displacement property.
-   */
-  const GesturePropertyVector2& GetScreenDisplacementProperty() const;
-
-  /**
-   * Retrieves a reference to the local position property.
-   * @return The local position property.
-   */
-  const GesturePropertyVector2& GetLocalPositionProperty() const;
-
-  /**
-   * Retrieves a reference to the local displacement property.
-   * @return The local displacement property.
-   */
-  const GesturePropertyVector2& GetLocalDisplacementProperty() const;
-
-  /**
-   * Called to provide pan-gesture profiling information.
-   */
-  void EnableProfiling();
-
-private:
-
-  /**
-   * Protected constructor.
-   */
-  PanGesture();
-
-  // Undefined
-  PanGesture(const PanGesture&);
-
-  // Undefined
-  PanGesture& operator=(const PanGesture&);
-
-  // PropertyOwner
-  virtual void ResetDefaultProperties( BufferIndex updateBufferIndex );
+  enum PredictionMode
+  {
+    NONE,
+    AVERAGE,
+    PREDICTION_1,
+    PREDICTION_2
+  };
 
-private:
-
-  // Properties
-  GesturePropertyVector2 mScreenPosition;     ///< screen-position
-  GesturePropertyVector2 mScreenDisplacement; ///< screen-displacement
-  GesturePropertyVector2 mLocalPosition;      ///< local-position
-  GesturePropertyVector2 mLocalDisplacement;  ///< local-displacement
+  static const PredictionMode DEFAULT_PREDICTION_MODE;
+  static const int NUM_PREDICTION_MODES;
 
   // Latest Pan Information
 
@@ -169,7 +104,8 @@ private:
      */
     PanInfo()
     : time( 0u ),
-      state( Gesture::Clear )
+      state( Gesture::Clear ),
+      read( true )
     {
     }
 
@@ -180,7 +116,8 @@ private:
     : time( rhs.time ),
       state( rhs.state ),
       local( rhs.local ),
-      screen( rhs.screen )
+      screen( rhs.screen ),
+      read( true )
     {
     }
 
@@ -218,21 +155,141 @@ private:
     }
 
     // Data
-
     unsigned int time;
     Gesture::State state;
     Info local;
     Info screen;
+    volatile bool read;
   };
 
-  PanInfo mGestures[4];         ///< Circular buffer storing the 4 most recent gestures.
+  typedef std::vector<PanInfo> PanInfoHistory;
+  typedef PanInfoHistory::iterator PanInfoHistoryIter;
+  typedef PanInfoHistory::const_iterator PanInfoHistoryConstIter;
+
+private:
+  static const unsigned int PAN_GESTURE_HISTORY = 4u;
+
+public:
+
+  /**
+   * Create a new PanGesture
+   */
+  static PanGesture* New();
+
+  /**
+   * Virtual destructor
+   */
+  virtual ~PanGesture();
+
+  /**
+   * Adds a PanGesture to the internal circular-buffer waiting to be handled by UpdateProperties.
+   * @param[in]  gesture  The latest pan gesture.
+   */
+  void AddGesture( const Dali::PanGesture& gesture );
+
+  /**
+   * @brief Removes pan events from the history that are older than maxAge, leaving at least minEvents
+   *
+   * @param[in] panHistory The pan event history container
+   * @param[in] currentTime The current frame time
+   * @param[in] maxAge Maximum age of an event before removing (in millis)
+   * @param[in] minEvents The minimum number of events to leave in history, oldest events are removed before newest
+   */
+  void RemoveOldHistory(PanInfoHistory& panHistory, uint currentTime, uint maxAge, uint minEvents);
+
+  /**
+   * USes last two gestures
+   *
+   * @param[out] gestureOut Output gesture using average values from last two gestures
+   */
+  void SimpleAverageAlgorithm(bool justStarted, PanInfo& gestureOut);
+
+  /**
+   * Uses elapsed time and time stamps
+   */
+  void PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime);
+
+  /**
+   * Uses elapsed time, time stamps and future render time
+   */
+  void PredictiveAlgorithm2(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime);
+
+  /**
+   * Called by the update manager so that we can update the value of our properties.
+   * @param[in]  nextRenderTime  The estimated time of the next render (in milliseconds).
+   */
+  virtual void UpdateProperties( unsigned int lastRenderTime, unsigned int nextRenderTime );
+
+  /**
+   * Retrieves a reference to the screen position property.
+   * @return The screen position property.
+   */
+  const GesturePropertyVector2& GetScreenPositionProperty() const;
+
+  /**
+   * Retrieves a reference to the screen displacement property.
+   * @return The screen displacement property.
+   */
+  const GesturePropertyVector2& GetScreenDisplacementProperty() const;
+
+  /**
+   * Retrieves a reference to the local position property.
+   * @return The local position property.
+   */
+  const GesturePropertyVector2& GetLocalPositionProperty() const;
+
+  /**
+   * Retrieves a reference to the local displacement property.
+   * @return The local displacement property.
+   */
+  const GesturePropertyVector2& GetLocalDisplacementProperty() const;
+
+  /**
+   * @brief Sets the prediction mode of the pan gesture
+   *
+   * @param[in] mode The prediction mode
+   */
+  void SetPredictionMode(PredictionMode mode) { mPredictionMode = mode; }
+
+  /**
+   * Called to provide pan-gesture profiling information.
+   */
+  void EnableProfiling();
+
+private:
+
+  /**
+   * Protected constructor.
+   */
+  PanGesture();
+
+  // Undefined
+  PanGesture(const PanGesture&);
+
+  // Undefined
+  PanGesture& operator=(const PanGesture&);
+
+  // PropertyOwner
+  virtual void ResetDefaultProperties( BufferIndex updateBufferIndex );
+
+private:
+
+  // Properties
+  GesturePropertyVector2 mScreenPosition;     ///< screen-position
+  GesturePropertyVector2 mScreenDisplacement; ///< screen-displacement
+  GesturePropertyVector2 mLocalPosition;      ///< local-position
+  GesturePropertyVector2 mLocalDisplacement;  ///< local-displacement
+
+  PanInfo mGestures[PAN_GESTURE_HISTORY];         ///< Circular buffer storing the 4 most recent gestures.
+  PanInfoHistory mPanHistory;
   unsigned int mWritePosition;  ///< The next PanInfo buffer to write to. (starts at 0)
   unsigned int mReadPosition;   ///< The next PanInfo buffer to read. (starts at 0)
 
+  PanInfo mEventGesture;        ///< Result of all pan events received since last frame
   PanInfo mLatestGesture;       ///< The latest gesture. (this update frame)
-  PanInfo mPreviousGesture;     ///< The previous gesture. (one update frame ago)
   bool mInGesture;              ///< True if the gesture is currently being handled i.e. between Started <-> Finished/Cancelled
 
+  PredictionMode mPredictionMode;  ///< The pan gesture prediction mode
   PanGestureProfiling* mProfiling; ///< NULL unless pan-gesture profiling information is required.
 };