{
const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm
const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 0; ///< how much to interpolate pan position and displacement from last vsync time
+const float DEFAULT_SMOOTHING_AMOUNT = 1.0f; ///< how much to interpolate pan position and displacement from last vsync time
} // unnamed namespace
-const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::AVERAGE;
-const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_2 + 1;
+const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
+const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_1 + 1;
+
+const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
+const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_LAST_VALUE + 1;
PanGesture* PanGesture::New()
{
iter = panHistory.erase(iter);
endIter = panHistory.end();
}
-}
-void PanGesture::SimpleAverageAlgorithm(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
-{
- if( mInGesture )
+ // dont want more than 5 previous predictions for smoothing
+ iter = mPredictionHistory.begin();
+ while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
{
- if( !justStarted )
- {
- gestureOut.screen.position += mLastEventGesture.screen.position;
- gestureOut.local.position += mLastEventGesture.local.position;
- gestureOut.screen.position *= 0.5f;
- gestureOut.local.position *= 0.5f;
- // make current displacement relative to previous update-frame now.
- gestureOut.screen.displacement = gestureOut.screen.position - mLastEventGesture.screen.position;
- gestureOut.local.displacement = gestureOut.local.position - mLastEventGesture.local.position;
- // calculate velocity relative to previous update-frame
- unsigned int timeDiff( lastVSyncTime - mLastEventGesture.time );
- gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
- gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
- }
-
- gestureOut.time = lastVSyncTime;
+ iter = mPredictionHistory.erase(iter);
}
}
PanInfoHistoryIter iter = panHistory.begin();
Vector2 screenVelocity = gestureOut.screen.velocity;
Vector2 localVelocity = gestureOut.local.velocity;
+ Vector2 screenDisplacement = gestureOut.screen.displacement;
+ Vector2 localDisplacement = gestureOut.local.displacement;
bool havePreviousAcceleration = false;
bool previousVelocity = false;
float previousAccel = 0.0f;
unsigned int lastTime(0);
+ unsigned int interpolationTime = (lastVSyncTime + mPredictionAmount) - gestureOut.time;
while( iter != endIter )
{
PanInfo currentGesture = *iter;
++iter;
continue;
}
+ float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
float velMag = currentGesture.screen.velocity.Length();
float velDiff = velMag - screenVelocity.Length();
float acceleration = 0.0f;
{
acceleration = velDiff / time;
}
- float interpolationTime = (float)((int)lastVSyncTime - (int)currentGesture.time);
float newVelMag = 0.0f;
+ int currentInterpolation = interpolationTime; //(lastVSyncTime + mPredictionAmount) - currentGesture.time;
if( !havePreviousAcceleration )
{
- newVelMag = velMag + (acceleration * interpolationTime);
+ newVelMag = velMag;
havePreviousAcceleration = true;
}
else
{
- newVelMag = velMag + (((acceleration + previousAccel) * 0.5f) * interpolationTime);
+ newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
}
float velMod = 1.0f;
if( velMag > Math::MACHINE_EPSILON_1 )
}
gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
gestureOut.local.velocity = currentGesture.local.velocity * velMod;
+ screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
+ localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
screenVelocity = currentGesture.screen.velocity;
localVelocity = currentGesture.local.velocity;
previousAccel = acceleration;
}
// 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 += (lastVSyncTime - gestureOut.time);
+ gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
+ gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
+ gestureOut.screen.displacement = screenDisplacement;
+ gestureOut.local.displacement = localDisplacement;
+ gestureOut.time += interpolationTime;
}
-void PanGesture::PredictiveAlgorithm2(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
+void PanGesture::SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
{
- // TODO - adapt PredictiveAlgorithm1 with better smoothing, still under development
- RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
- size_t panHistorySize = panHistory.size();
- if( panHistorySize == 0 )
+ if( !justStarted )
{
- // cant do any prediction without a history
- return;
+ gestureOut.screen.position -= (gestureOut.screen.position - mLastGesture.screen.position) * 0.5f * (1.0f - mSmoothingAmount);
+ gestureOut.local.position -= (gestureOut.local.position - mLastGesture.local.position) * 0.5f * (1.0f - mSmoothingAmount);
+ // make current displacement relative to previous update-frame now.
+ gestureOut.screen.displacement = gestureOut.screen.position - mLastGesture.screen.position;
+ gestureOut.local.displacement = gestureOut.local.position - mLastGesture.local.position;
+ // calculate velocity relative to previous update-frame
+ unsigned int timeDiff( gestureOut.time - mLastGesture.time );
+ gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
+ gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
}
+}
- PanInfoHistoryConstIter endIter = panHistory.end();
- PanInfoHistoryIter iter = panHistory.begin();
- Vector2 screenVelocity = gestureOut.screen.velocity;
- Vector2 localVelocity = gestureOut.local.velocity;
- Vector2 screenDisplacement = gestureOut.screen.displacement;
- Vector2 localDisplacement = gestureOut.local.displacement;
+void PanGesture::SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
+{
+ // push back result
+ mPredictionHistory.push_back(gestureOut);
- bool havePreviousAcceleration = false;
- bool previousVelocity = false;
- float previousAccel = 0.0f;
- unsigned int lastTime(0);
- unsigned int interpolationTime = (lastVSyncTime + mPredictionAmount) - gestureOut.time;
+ // now smooth current pan event
+ PanInfoHistoryConstIter endIter = mPredictionHistory.end() - 1;
+ PanInfoHistoryIter iter = mPredictionHistory.begin();
+
+ float distanceMod = 1.0f;
+ float weight = 0.8f;
while( iter != endIter )
{
PanInfo currentGesture = *iter;
- if( !previousVelocity )
- {
- // not yet set a previous velocity
- screenVelocity = currentGesture.screen.velocity;
- previousVelocity = true;
- lastTime = currentGesture.time;
- ++iter;
- continue;
- }
- float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
- float velMag = currentGesture.screen.velocity.Length();
- float velDiff = velMag - screenVelocity.Length();
- float acceleration = 0.0f;
- float time = (float)(currentGesture.time - lastTime);
- if( time > Math::MACHINE_EPSILON_1 )
- {
- acceleration = velDiff / time;
- }
- float newVelMag = 0.0f;
- int currentInterpolation = (lastVSyncTime + mPredictionAmount) - currentGesture.time;
- if( !havePreviousAcceleration )
- {
- newVelMag = velMag + (acceleration * currentInterpolation);
- havePreviousAcceleration = true;
- }
- else
- {
- newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * interpolationTime);
- }
- float velMod = 1.0f;
- if( velMag > Math::MACHINE_EPSILON_1 )
- {
- velMod = newVelMag / velMag;
- }
- gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
- gestureOut.local.velocity = currentGesture.local.velocity * velMod;
- screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
- localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
- screenVelocity = currentGesture.screen.velocity;
- localVelocity = currentGesture.local.velocity;
- previousAccel = acceleration;
+ float newDistanceMod = currentGesture.screen.displacement.Length() / gestureOut.screen.displacement.Length();
+ distanceMod = ((distanceMod * weight) + (newDistanceMod * (1.0f - weight)));
+ weight -= 0.15f;
++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
- // work out interpolated velocity
- gestureOut.screen.displacement = screenDisplacement;
- gestureOut.local.displacement = localDisplacement;
- gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
- gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
- gestureOut.time += interpolationTime;
+ gestureOut.screen.position -= gestureOut.screen.displacement;
+ gestureOut.local.position -= gestureOut.local.displacement;
+ gestureOut.screen.displacement *= distanceMod;
+ gestureOut.local.displacement *= distanceMod;
+ gestureOut.screen.position += gestureOut.screen.displacement;
+ gestureOut.local.position += gestureOut.local.displacement;
}
bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
{
// clear current pan history
mPanHistory.clear();
+ mPredictionHistory.clear();
}
// create an event for this frame
int eventsThisFrame = 0;
// create PanInfo to pass into prediction method
- PanInfo nextGesture = mEventGesture;
+ mLastEventGesture = mEventGesture;
+ mLastGesture = mLatestGesture;
// add new gestures and work out one full gesture for the frame
while(mReadPosition != mWritePosition)
{
{
mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position ) );
}
- nextGesture.local.position = currentGesture.local.position;
- nextGesture.local.velocity = currentGesture.local.velocity;
- nextGesture.screen.position = currentGesture.screen.position;
- nextGesture.screen.velocity = currentGesture.screen.velocity;
+ 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 )
{
- nextGesture.local.displacement = currentGesture.local.displacement;
- nextGesture.screen.displacement = currentGesture.screen.displacement;
+ mEventGesture.local.displacement = currentGesture.local.displacement;
+ mEventGesture.screen.displacement = currentGesture.screen.displacement;
}
else
{
- nextGesture.local.displacement += currentGesture.local.displacement;
- nextGesture.screen.displacement += currentGesture.screen.displacement;
+ mEventGesture.local.displacement += currentGesture.local.displacement;
+ mEventGesture.screen.displacement += currentGesture.screen.displacement;
}
eventFound = true;
- nextGesture.time = currentGesture.time;
+ mEventGesture.time = currentGesture.time;
// add event to history
mPanHistory.push_back(currentGesture);
- justStarted |= (currentGesture.state == Gesture::Started);
if( currentGesture.state == Gesture::Started )
{
justStarted = true;
++mReadPosition;
mReadPosition %= PAN_GESTURE_HISTORY;
}
- // set nextGesture to last gesture so it's position is correct and velocity is same as last frame
- mEventGesture = nextGesture;
+ mLatestGesture = mEventGesture;
mInGesture |= justStarted;
switch( mPredictionMode )
{
- case NONE:
+ case PREDICTION_NONE:
{
updateProperties = eventFound;
+ // dont want event time
+ unsigned int time = mLastGesture.time;
+ mLastGesture = mLastEventGesture;
+ mLastGesture.time = time;
+ mLatestGesture.time = lastVSyncTime;
break;
}
- case AVERAGE:
+ case PREDICTION_1:
{
- SimpleAverageAlgorithm(justStarted, nextGesture, lastVSyncTime);
- // make latest gesture equal to current gesture after averaging
+ // make latest gesture equal to current gesture before interpolation
+ PredictiveAlgorithm1(eventsThisFrame, mLatestGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
updateProperties = true;
break;
}
- case PREDICTION_1:
+ }
+
+ switch( mSmoothingMode )
+ {
+ case SMOOTHING_NONE:
{
- // make latest gesture equal to current gesture before interpolation
- PredictiveAlgorithm1(eventsThisFrame, nextGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
- updateProperties = true;
+ // no smoothing
break;
}
- case PREDICTION_2:
+ case SMOOTHING_LAST_VALUE:
{
- // make latest gesture equal to current gesture before interpolation
- PredictiveAlgorithm2(eventsThisFrame, nextGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
- updateProperties = true;
+ SmoothingAlgorithm1(justStarted, mLatestGesture, lastVSyncTime);
break;
}
}
- // always keep latest gesture up to date with event gesture
- mLatestGesture = nextGesture;
-
if( updateProperties )
{
// only update properties if event received
// set latest gesture to raw pan info with unchanged time
mPanning.Set( mInGesture & !justFinished );
- mScreenPosition.Set( nextGesture.screen.position );
- mScreenDisplacement.Set( nextGesture.screen.displacement );
- mScreenVelocity.Set( nextGesture.screen.velocity );
- mLocalPosition.Set( nextGesture.local.position );
- mLocalDisplacement.Set( nextGesture.local.displacement );
- mLocalVelocity.Set( nextGesture.local.velocity );
+ mScreenPosition.Set( mLatestGesture.screen.position );
+ mScreenDisplacement.Set( mLatestGesture.screen.displacement );
+ mScreenVelocity.Set( mLatestGesture.screen.velocity );
+ mLocalPosition.Set( mLatestGesture.local.position );
+ mLocalDisplacement.Set( mLatestGesture.local.displacement );
+ mLocalVelocity.Set( mLatestGesture.local.velocity );
}
if( mProfiling )
{
- mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( nextGesture.time, nextGesture.screen.position ) );
+ mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( mLatestGesture.time, mLatestGesture.screen.position ) );
}
}
- mLastEventGesture = mEventGesture;
mInGesture &= ~justFinished;
mPredictionAmount = amount;
}
+void PanGesture::SetSmoothingMode(SmoothingMode mode)
+{
+ mSmoothingMode = mode;
+}
+
+void PanGesture::SetSmoothingAmount(float amount)
+{
+ mSmoothingAmount = amount;
+}
+
void PanGesture::EnableProfiling()
{
if( !mProfiling )
mInGesture( false ),
mPredictionMode(DEFAULT_PREDICTION_MODE),
mPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
+ mSmoothingMode(DEFAULT_SMOOTHING_MODE),
+ mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT),
mProfiling( NULL )
{
}
enum PredictionMode
{
- NONE,
- AVERAGE,
- PREDICTION_1,
- PREDICTION_2
+ PREDICTION_NONE = 0,
+ PREDICTION_1
+ };
+
+ enum SmoothingMode
+ {
+ SMOOTHING_NONE, // no smoothing
+ SMOOTHING_LAST_VALUE, // smooth between last value and latest value
};
static const PredictionMode DEFAULT_PREDICTION_MODE;
static const int NUM_PREDICTION_MODES;
+ static const SmoothingMode DEFAULT_SMOOTHING_MODE;
+ static const int NUM_SMOOTHING_MODES;
+
// Latest Pan Information
/**
void RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents);
/**
- * USes last two gestures
+ * Uses elapsed time and time stamps
+ */
+ void PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime);
+
+ /**
+ * Uses last two gestures
*
* @param[in] justStarted Whether the pan has just started.
* @param[out] gestureOut Output gesture using average values from last two gestures
* @param[in] lastVSyncTime The time to set on gestureOut.
*/
- void SimpleAverageAlgorithm(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime);
-
- /**
- * Uses elapsed time and time stamps
- */
- void PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime);
+ void SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime);
/**
- * Uses elapsed time, time stamps and future render time
+ * Future smoothing method, implementation not complete
*/
- void PredictiveAlgorithm2(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime);
+ void SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime);
/**
* Called by the update manager so that we can update the value of our properties.
void SetPredictionAmount(unsigned int amount);
/**
+ * @brief Sets the prediction mode of the pan gesture
+ *
+ * @param[in] mode The prediction mode
+ */
+ void SetSmoothingMode(SmoothingMode mode);
+
+ /**
+ * @brief Sets the amount of smoothing to apply for the current smoothing mode
+ *
+ * @param[in] amount The amount of smoothing [0.0f,1.0f]
+ */
+ void SetSmoothingAmount(float amount);
+
+ /**
* Called to provide pan-gesture profiling information.
*/
void EnableProfiling();
PanInfo mGestures[PAN_GESTURE_HISTORY]; ///< Circular buffer storing the 4 most recent gestures.
PanInfoHistory mPanHistory;
+ PanInfoHistory mPredictionHistory;
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 this frame
PanInfo mLastEventGesture; ///< The last frame's event gesture.
+ PanInfo mLastGesture; ///< The latest gesture. (this update frame)
PanInfo mLatestGesture; ///< The latest gesture. (this update frame)
bool mInGesture; ///< True if the gesture is currently being handled i.e. between Started <-> Finished/Cancelled
PredictionMode mPredictionMode; ///< The pan gesture prediction mode
unsigned int mPredictionAmount; ///< how far into future to predict in milliseconds
+ SmoothingMode mSmoothingMode; ///< The pan gesture prediction mode
+ float mSmoothingAmount; ///< How much smoothing to apply [0.0f,1.0f]
PanGestureProfiling* mProfiling; ///< NULL unless pan-gesture profiling information is required.
};