X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Fupdate%2Fgestures%2Fscene-graph-pan-gesture.cpp;h=6218102eca43860101f5152e289544ab5a8bea4e;hb=091424324901c46a18959bfc0dd52f7ce8a0a811;hp=4a476ba74d14e1e8c34104c059046985688edf4c;hpb=88bd662fb52606caf11c306ea4c4bd7b312361de;p=platform%2Fcore%2Fuifw%2Fdali-core.git diff --git a/dali/internal/update/gestures/scene-graph-pan-gesture.cpp b/dali/internal/update/gestures/scene-graph-pan-gesture.cpp index 4a476ba..6218102 100644 --- a/dali/internal/update/gestures/scene-graph-pan-gesture.cpp +++ b/dali/internal/update/gestures/scene-graph-pan-gesture.cpp @@ -1,23 +1,25 @@ -// -// Copyright (c) 2014 Samsung Electronics Co., Ltd. -// -// Licensed under the Flora License, Version 1.0 (the License); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://floralicense.org/license/ -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an AS IS BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ // CLASS HEADER #include // EXTERNAL INCLUDES +#include // INTERNAL INCLUDES #include @@ -30,14 +32,23 @@ namespace Internal namespace SceneGraph { + namespace { -const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm -const float DEFAULT_PREDICTION_INTERPOLATION = 0.0f; ///< how much to interpolate pan position and displacement from last vsync time +const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm TODO: Possibly make this configurable. +const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount +const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 5; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds) +const unsigned int DEFAULT_MAX_PREDICTION_INTERPOLATION = 32; ///< the upper bound of the range to clamp the prediction interpolation +const unsigned int DEFAULT_MIN_PREDICTION_INTERPOLATION = 0; ///< the lower bound of the range to clamp the prediction interpolation +const unsigned int DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT = 2; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes +const float DEFAULT_SMOOTHING_AMOUNT = 0.25f; ///< how much to interpolate pan position and displacement from last vsync time } // unnamed namespace -const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_2; -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() { @@ -58,7 +69,7 @@ void PanGesture::AddGesture( const Dali::PanGesture& gesture ) mWritePosition %= PAN_GESTURE_HISTORY; } -void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, uint currentTime, uint maxAge, uint minEvents) +void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents) { PanInfoHistoryConstIter endIter = panHistory.end(); PanInfoHistoryIter iter = panHistory.begin(); @@ -72,22 +83,12 @@ void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, uint currentTime, iter = panHistory.erase(iter); endIter = panHistory.end(); } -} -void PanGesture::SimpleAverageAlgorithm(bool justStarted, PanInfo& gestureOut) -{ - 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; - } + iter = mPredictionHistory.erase(iter); } } @@ -105,11 +106,25 @@ void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, 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 + mCurrentPredictionAmount; + + if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount) + { + interpolationTime -= gestureOut.time; + } + else + { + interpolationTime = 0u; + } + while( iter != endIter ) { PanInfo currentGesture = *iter; @@ -122,24 +137,31 @@ void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, ++iter; continue; } + float previousValueWeight = ( static_cast< float >( MAX_GESTURE_AGE ) - (lastVSyncTime - lastTime) ) / static_cast< 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); + + float time(0.f); + if (currentGesture.time > lastTime) // Guard against invalid timestamps + { + time = static_cast( currentGesture.time - lastTime ); + } if( time > Math::MACHINE_EPSILON_1 ) { acceleration = velDiff / time; } - float interpolationTime = (float)((int)lastVSyncTime - (int)currentGesture.time); + float newVelMag = 0.0f; + int currentInterpolation = interpolationTime; 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 ) @@ -148,6 +170,8 @@ void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, } 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; @@ -155,231 +179,379 @@ void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, } // 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.displacement = Vector2::ZERO; + gestureOut.screen.velocity = Vector2::ZERO; + gestureOut.local.displacement = Vector2::ZERO; + gestureOut.local.velocity = Vector2::ZERO; } + else + { + 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 + float 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; +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); + // 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 interpolationTime = (float)((int)lastVSyncTime - (int)currentGesture.time); - float newVelMag = 0.0f; - if( !havePreviousAcceleration ) - { - newVelMag = velMag + (acceleration * interpolationTime); - 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; - 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 - unsigned int timeAdvance = ((nextVSyncTime - lastVSyncTime) * mPredictionAmount); - unsigned int interpolationTime = (lastVSyncTime + timeAdvance) - 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.screen.displacement *= distanceMod; + gestureOut.local.displacement *= distanceMod; gestureOut.screen.position += gestureOut.screen.displacement; gestureOut.local.position += gestureOut.local.displacement; - gestureOut.time += interpolationTime; } -bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime ) +bool PanGesture::ReadGestures( FrameGestureInfo& info, unsigned int currentTimestamp ) { - bool propertiesUpdated( false ); - - if( !mInGesture ) - { - // clear current pan history - mPanHistory.clear(); - } - - // create an event for this frame - bool justStarted ( false ); - bool justFinished ( false ); - bool eventFound( false ); + unsigned int previousReadPosition = 0; + bool eventFound = false; + info.frameGesture = mLastUnmodifiedGesture; - // Not going through array from the beginning, using it as a circular buffer and only using unread - // values. - int eventsThisFrame = 0; - - // create PanInfo to pass into prediction method - PanInfo nextGesture = mEventGesture; - // add new gestures and work out one full gesture for the frame - while(mReadPosition != mWritePosition) + while( mReadPosition != mWritePosition ) { // Copy the gesture first - PanInfo currentGesture(mGestures[mReadPosition]); + PanInfo currentGesture( mGestures[mReadPosition] ); if( mProfiling ) { - mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position ) ); + mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) ); } - nextGesture.local.position = currentGesture.local.position; - nextGesture.local.velocity = currentGesture.local.velocity; - nextGesture.screen.position = currentGesture.screen.position; - nextGesture.screen.velocity = currentGesture.screen.velocity; + info.frameGesture.local.position = currentGesture.local.position; + info.frameGesture.local.velocity = currentGesture.local.velocity; + info.frameGesture.screen.position = currentGesture.screen.position; + info.frameGesture.screen.velocity = currentGesture.screen.velocity; + + if( info.eventsThisFrame > 0 ) + { + info.acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length(); + } + if( !eventFound ) { - nextGesture.local.displacement = currentGesture.local.displacement; - nextGesture.screen.displacement = currentGesture.screen.displacement; + info.frameGesture.local.displacement = currentGesture.local.displacement; + info.frameGesture.screen.displacement = currentGesture.screen.displacement; + eventFound = true; } else { - nextGesture.local.displacement += currentGesture.local.displacement; - nextGesture.screen.displacement += currentGesture.screen.displacement; + info.frameGesture.local.displacement += currentGesture.local.displacement; + info.frameGesture.screen.displacement += currentGesture.screen.displacement; } - eventFound = true; - nextGesture.time = currentGesture.time; + info.frameGesture.time = currentGesture.time; // add event to history - mPanHistory.push_back(currentGesture); - justStarted |= (currentGesture.state == Gesture::Started); + mPanHistory.push_back( currentGesture ); if( currentGesture.state == Gesture::Started ) { - justStarted = true; + info.justStarted = true; // clear just finished as we have started new pan - justFinished = false; + info.justFinished = false; } - justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled); + info.justFinished |= ( currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled ); // Update our read position. - ++eventsThisFrame; + previousReadPosition = mReadPosition; + ++info.eventsThisFrame; ++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; + // This code does not determine if the data will be used. + return false; +} + +bool PanGesture::ReadAndResampleGestures( FrameGestureInfo& info, unsigned int currentTimestamp ) +{ + PanInfo lastReadGesture; + while( mReadPosition != mWritePosition ) + { + // Copy the gesture first + lastReadGesture = mGestures[mReadPosition]; + if( mProfiling ) + { + mProfiling->mRawData.push_back( PanGestureProfiling::Position( lastReadGesture.time, lastReadGesture.screen.position, + lastReadGesture.screen.displacement, lastReadGesture.screen.velocity, lastReadGesture.state ) ); + } + + info.frameGesture.screen.position += lastReadGesture.screen.position; + info.frameGesture.local.position += lastReadGesture.local.position; + info.frameGesture.screen.velocity += lastReadGesture.screen.velocity; + info.frameGesture.local.velocity += lastReadGesture.local.velocity; - mInGesture |= justStarted; + if( lastReadGesture.state == Gesture::Started ) + { + // Clear just finished as we have started new pan. + info.justFinished = false; + info.justStarted = true; + } + else + { + info.justFinished |= ( lastReadGesture.state == Gesture::Finished || lastReadGesture.state == Gesture::Cancelled ); + } + + // Add event to history + mPanHistory.push_back( lastReadGesture ); + + // Update our read position. + ++info.eventsThisFrame; + ++mReadPosition; + mReadPosition %= PAN_GESTURE_HISTORY; + } + + bool updateProperties = false; + if( info.eventsThisFrame > 0 ) + { + // Some events were read this frame. + mTargetGesture = lastReadGesture; + + if( info.eventsThisFrame > 1 ) + { + info.frameGesture.screen.position /= info.eventsThisFrame; + info.frameGesture.local.position /= info.eventsThisFrame; + info.frameGesture.screen.velocity /= info.eventsThisFrame; + info.frameGesture.local.velocity /= info.eventsThisFrame; + + info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position; + info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position; + + mNotAtTarget = true; + } + else + { + info.frameGesture.screen.displacement = lastReadGesture.screen.displacement; + info.frameGesture.local.displacement = lastReadGesture.local.displacement; + } + + info.frameGesture.time = currentTimestamp; + + updateProperties = true; + } + else + { + // 0 Events this frame. + if( mNotAtTarget ) + { + mNotAtTarget = false; + info.frameGesture = mTargetGesture; + updateProperties = true; + } + else + { + info.frameGesture = mLastGesture; + } + } + + return updateProperties; +} + +bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime ) +{ + if( !mInGesture ) + { + // clear current pan history + mPanHistory.clear(); + mPredictionHistory.clear(); + } + FrameGestureInfo frameInfo; bool updateProperties = false; - if ( mInGesture ) + // Read input data. + // If we are using a form of prediction, read all the input as-is. + if( mPredictionMode != PREDICTION_NONE ) { + // Read input required for prediction algorithms. + updateProperties = ReadGestures( frameInfo, lastVSyncTime ); + } + else + { + // Read and resample input. + updateProperties = ReadAndResampleGestures( frameInfo, lastVSyncTime ); + } + + PanInfo frameGesture = frameInfo.frameGesture; + mLastUnmodifiedGesture = frameGesture; + + // Process input data. + mInGesture |= frameInfo.justStarted; + if( mInGesture ) + { + // Profiling. if( mProfiling ) { - mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, mEventGesture.screen.position ) ); + mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, frameGesture.screen.position, + frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) ); } + // Perform prediction. switch( mPredictionMode ) { - case NONE: + case PREDICTION_NONE: { - updateProperties = eventFound; break; } - case AVERAGE: + case PREDICTION_1: { - SimpleAverageAlgorithm(justStarted, nextGesture); - // make latest gesture equal to current gesture after averaging + // Dynamically change the prediction amount according to the pan velocity acceleration. + if( !frameInfo.justStarted ) + { + if( frameInfo.eventsThisFrame <= 1 ) + { + frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length(); + } + + // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change + if( fabsf( frameInfo.acceleration ) > ACCELERATION_THRESHOLD ) + { + mCurrentPredictionAmount += mPredictionAmountAdjustment * ( frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f ); + if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow + { + mCurrentPredictionAmount = 0; + } + } + } + else + { + mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture + } + + mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) ); + + // Calculate the delta of positions before the prediction + Vector2 deltaPosition = frameGesture.screen.position - mLastUnmodifiedGesture.screen.position; + + // Make latest gesture equal to current gesture before interpolation + PredictiveAlgorithm1( frameInfo.eventsThisFrame, frameGesture, mPanHistory, lastVSyncTime, nextVSyncTime ); + + // Calculate the delta of positions after the prediction. + Vector2 deltaPredictedPosition = frameGesture.screen.position - mLastGesture.screen.position; + + // If the change in the prediction has a different sign than the change in the actual position, + // there is overshot (i.e. the current prediction is too large). Return the previous prediction + // to give the user's finger a chance to catch up with where we have panned to. + bool overshotXAxis = false; + bool overshotYAxis = false; + if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 ) + || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) ) + { + overshotXAxis = true; + frameGesture.screen.position.x = mLastGesture.screen.position.x; + } + + if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 ) + || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) ) + { + overshotYAxis = true; + frameGesture.screen.position.y = mLastGesture.screen.position.y; + } + + // If there is overshot in one axis, reduce the possible overshot in the other axis, + // and reduce the prediction amount so that it doesn't overshoot as easily next time. + if(overshotXAxis || overshotYAxis) + { + mCurrentPredictionAmount -= mPredictionAmountAdjustment; + if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow + { + mCurrentPredictionAmount = 0; + } + mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) ); + + if( overshotXAxis && !overshotYAxis ) + { + frameGesture.screen.position.y = ( mLastGesture.screen.position.y + frameGesture.screen.position.y ) * 0.5f; + } + + if( overshotYAxis && !overshotXAxis ) + { + frameGesture.screen.position.x = ( mLastGesture.screen.position.x + frameGesture.screen.position.x ) * 0.5f; + } + } + updateProperties = true; break; } - case PREDICTION_1: + } + + // Perform smoothing. + 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( frameInfo.justStarted, frameGesture, 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 ); - mLocalPosition.Set( nextGesture.local.position ); - mLocalDisplacement.Set( nextGesture.local.displacement ); - - propertiesUpdated = true; + mPanning.Set( mInGesture & !frameInfo.justFinished ); + mScreenPosition.Set( frameGesture.screen.position ); + mScreenDisplacement.Set( frameGesture.screen.displacement ); + mScreenVelocity.Set( frameGesture.screen.velocity ); + mLocalPosition.Set( frameGesture.local.position ); + mLocalDisplacement.Set( frameGesture.local.displacement ); + mLocalVelocity.Set( frameGesture.local.velocity ); } if( mProfiling ) { - mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( nextGesture.time, nextGesture.screen.position ) ); + mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( frameGesture.time, frameGesture.screen.position, + frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) ); } } - mLastEventGesture = mEventGesture; - mInGesture &= ~justFinished; + mLastGesture = frameGesture; - if( mProfiling && justFinished ) + mInGesture &= ~frameInfo.justFinished; + if( mProfiling && frameInfo.justFinished ) { mProfiling->PrintData(); mProfiling->ClearData(); } - return propertiesUpdated; + return updateProperties; } const GesturePropertyBool& PanGesture::GetPanningProperty() const @@ -392,6 +564,11 @@ const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const return mScreenPosition; } +const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const +{ + return mScreenVelocity; +} + const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const { return mScreenDisplacement; @@ -407,16 +584,46 @@ const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const return mLocalDisplacement; } +const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const +{ + return mLocalVelocity; +} + void PanGesture::SetPredictionMode(PredictionMode mode) { mPredictionMode = mode; } -void PanGesture::SetPredictionAmount(float amount) +void PanGesture::SetPredictionAmount(unsigned int amount) { mPredictionAmount = amount; } +void PanGesture::SetMaximumPredictionAmount(unsigned int amount) +{ + mMaxPredictionAmount = amount; +} + +void PanGesture::SetMinimumPredictionAmount(unsigned int amount) +{ + mMinPredictionAmount = amount; +} + +void PanGesture::SetPredictionAmountAdjustment(unsigned int amount) +{ + mPredictionAmountAdjustment = amount; +} + +void PanGesture::SetSmoothingMode(SmoothingMode mode) +{ + mSmoothingMode = mode; +} + +void PanGesture::SetSmoothingAmount(float amount) +{ + mSmoothingAmount = amount; +} + void PanGesture::EnableProfiling() { if( !mProfiling ) @@ -435,16 +642,25 @@ void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex ) } PanGesture::PanGesture() -: mGestures(), +: mPanning(), + mGestures(), mWritePosition( 0 ), mReadPosition( 0 ), + mNotAtTarget( false ), mInGesture( false ), - mPredictionMode(DEFAULT_PREDICTION_MODE), - mPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION), + mPredictionMode( DEFAULT_PREDICTION_MODE ), + mPredictionAmount( DEFAULT_PREDICTION_INTERPOLATION ), + mCurrentPredictionAmount( DEFAULT_PREDICTION_INTERPOLATION ), + mMaxPredictionAmount( DEFAULT_MAX_PREDICTION_INTERPOLATION ), + mMinPredictionAmount( DEFAULT_MIN_PREDICTION_INTERPOLATION ), + mPredictionAmountAdjustment( DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT ), + mSmoothingMode( DEFAULT_SMOOTHING_MODE ), + mSmoothingAmount( DEFAULT_SMOOTHING_AMOUNT ), mProfiling( NULL ) { } + } // namespace SceneGraph } // namespace Internal