2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/update/gestures/scene-graph-pan-gesture.h>
25 #include <dali/internal/update/gestures/pan-gesture-profiling.h>
26 #include <dali/integration-api/debug.h>
40 // TODO: Experimental - for changing in code only:
41 const bool TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION = false;
44 const int MAX_GESTURE_AGE = 200; ///< maximum age of a gesture before disallowing its use in algorithm TODO: Possibly make this configurable.
45 const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount.
46 const float OUTPUT_TIME_DIFFERENCE = ( 1000.0f / 60.0f ); ///< This is used to optionally override actual times if they make results worse.
47 const float ACCELERATION_SMOOTHING = 0.44f; ///< Smoothes acceleration changes from one frame to another.
48 const float ACCELERATION_CAP = 0.0004f;///< Limits acceleration changes from one frame to another.
50 // Defaults for Environment Variables:
53 const unsigned int DEFAULT_MAX_PREDICTION_AMOUNT = 32; ///< the upper bound of the range to clamp the prediction interpolation.
54 const unsigned int DEFAULT_MIN_PREDICTION_AMOUNT = 0; ///< the lower bound of the range to clamp the prediction interpolation.
55 const unsigned int DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT = 2; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes.
58 const bool DEFAULT_USE_ACTUAL_TIMES = false; ///< Disable to optionally override actual times if they make results worse.
59 const int DEFAULT_INTERPOLATION_TIME_RANGE = 255; ///< Time into past history (ms) to use points to interpolate the first point.
60 const bool DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED = false; ///< If enabled, prediction is done using velocity alone (no integration or acceleration).
61 const bool DEFAULT_TWO_POINT_PREDICTION_ENABLED = true; ///< If enabled, a second interpolated point is predicted and combined with the first to get more stable values.
62 const int DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME = 42; ///< The target time in the past to generate the second interpolated point.
63 const float DEFAULT_TWO_POINT_VELOCITY_BIAS = 0.35f; ///< The ratio of first and second interpolated points to use for velocity. 0.0f = 100% of first point. 1.0f = 100% of second point.
64 const float DEFAULT_TWO_POINT_ACCELERATION_BIAS = 0.10f; ///< The ratio of first and second interpolated points to use for acceleration. 0.0f = 100% of first point. 1.0f = 100% of second point.
65 const int DEFAULT_MULTITAP_SMOOTHING_RANGE = 34; ///< The range in time (ms) of points in the history to smooth the final output against.
67 // Prediction Modes 1 & 2.
68 const unsigned int DEFAULT_PREDICTION_AMOUNT[2] = { 5, 57 }; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
69 const float DEFAULT_SMOOTHING_AMOUNT[2] = { 0.25f, 0.23f }; ///< how much to smooth final result from last vsync time
71 } // unnamed namespace
73 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
74 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_2 + 1;
76 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
77 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_MULTI_TAP + 1;
79 PanGesture* PanGesture::New()
81 return new PanGesture();
84 PanGesture::~PanGesture()
89 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
91 Dali::Mutex::ScopedLock lock( mMutex );
92 mGestures[ mWritePosition ] = gesture;
94 // Update our write position.
96 mWritePosition %= PAN_GESTURE_HISTORY;
99 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
101 PanInfoHistoryConstIter endIter = panHistory.end();
102 PanInfoHistoryIter iter = panHistory.begin();
103 while( iter != endIter && panHistory.size() > minEvents)
105 PanInfo currentGesture = *iter;
106 if( currentTime < currentGesture.time + maxAge )
110 iter = panHistory.erase(iter);
111 endIter = panHistory.end();
114 // dont want more than 5 previous predictions for smoothing
115 iter = mPredictionHistory.begin();
116 while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
118 iter = mPredictionHistory.erase(iter);
122 void PanGesture::PredictionMode1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
124 RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
125 size_t panHistorySize = panHistory.size();
126 if( panHistorySize == 0 )
128 // cant do any prediction without a history
132 PanInfoHistoryConstIter endIter = panHistory.end();
133 PanInfoHistoryIter iter = panHistory.begin();
134 Vector2 screenVelocity = gestureOut.screen.velocity;
135 Vector2 localVelocity = gestureOut.local.velocity;
136 Vector2 screenDisplacement = gestureOut.screen.displacement;
137 Vector2 localDisplacement = gestureOut.local.displacement;
139 bool havePreviousAcceleration = false;
140 bool previousVelocity = false;
141 float previousAccel = 0.0f;
142 unsigned int lastTime(0);
144 unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
146 if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
148 interpolationTime -= gestureOut.time;
152 interpolationTime = 0u;
155 while( iter != endIter )
157 PanInfo currentGesture = *iter;
158 if( !previousVelocity )
160 // not yet set a previous velocity
161 screenVelocity = currentGesture.screen.velocity;
162 previousVelocity = true;
163 lastTime = currentGesture.time;
167 float previousValueWeight = ( static_cast<float>( MAX_GESTURE_AGE ) - static_cast<float>(lastVSyncTime - lastTime) ) / static_cast<float>( MAX_GESTURE_AGE );
168 float velMag = currentGesture.screen.velocity.Length();
169 float velDiff = velMag - screenVelocity.Length();
170 float acceleration = 0.0f;
173 if (currentGesture.time > lastTime) // Guard against invalid timestamps
175 time = static_cast<float>( currentGesture.time - lastTime );
177 if( time > Math::MACHINE_EPSILON_1 )
179 acceleration = velDiff / time;
182 float newVelMag = 0.0f;
183 int currentInterpolation = interpolationTime;
184 if( !havePreviousAcceleration )
187 havePreviousAcceleration = true;
191 newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * static_cast<float>( currentInterpolation ) );
194 if( velMag > Math::MACHINE_EPSILON_1 )
196 velMod = newVelMag / velMag;
198 gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
199 gestureOut.local.velocity = currentGesture.local.velocity * velMod;
200 screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * static_cast<float>( interpolationTime ) );
201 localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * static_cast<float>( interpolationTime ) );
202 screenVelocity = currentGesture.screen.velocity;
203 localVelocity = currentGesture.local.velocity;
204 previousAccel = acceleration;
207 // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
208 // add interpolated distance and position to current
209 // work out interpolated velocity
210 gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
211 gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
212 gestureOut.screen.displacement = screenDisplacement;
213 gestureOut.local.displacement = localDisplacement;
214 gestureOut.time += interpolationTime;
217 void PanGesture::BlendPoints( PanInfo& gesture, PanInfo& lastGesture, float blendValue )
219 gesture.screen.position -= ( gesture.screen.position - lastGesture.screen.position ) * 0.5f * ( 1.0f - blendValue );
220 gesture.local.position -= ( gesture.local.position - lastGesture.local.position ) * 0.5f * ( 1.0f - blendValue );
221 // Make current displacement relative to previous update-frame now.
222 gesture.screen.displacement = gesture.screen.position - lastGesture.screen.position;
223 gesture.local.displacement = gesture.local.position - lastGesture.local.position;
224 // Calculate velocity relative to previous update-frame
225 float timeDifference = static_cast<float>( gesture.time - lastGesture.time );
226 gesture.screen.velocity = gesture.screen.displacement / timeDifference;
227 gesture.local.velocity = gesture.local.displacement / timeDifference;
230 bool PanGesture::ReadGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
232 unsigned int previousReadPosition = 0;
233 bool eventFound = false;
234 info.frameGesture = mLastUnmodifiedGesture;
236 while( mReadPosition != mWritePosition )
238 // Copy the gesture first
239 PanInfo currentGesture( mGestures[mReadPosition] );
243 mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) );
245 info.frameGesture.local.position = currentGesture.local.position;
246 info.frameGesture.local.velocity = currentGesture.local.velocity;
247 info.frameGesture.screen.position = currentGesture.screen.position;
248 info.frameGesture.screen.velocity = currentGesture.screen.velocity;
250 if( info.eventsThisFrame > 0 )
252 info.acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
257 info.frameGesture.local.displacement = currentGesture.local.displacement;
258 info.frameGesture.screen.displacement = currentGesture.screen.displacement;
263 info.frameGesture.local.displacement += currentGesture.local.displacement;
264 info.frameGesture.screen.displacement += currentGesture.screen.displacement;
266 info.frameGesture.time = currentGesture.time;
268 // add event to history
269 mPanHistory.push_back( currentGesture );
270 if( currentGesture.state == Gesture::Started )
272 info.justStarted = true;
273 // clear just finished as we have started new pan
274 info.justFinished = false;
276 info.justFinished |= ( currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled );
278 // Update our read position.
279 previousReadPosition = mReadPosition;
280 ++info.eventsThisFrame;
282 mReadPosition %= PAN_GESTURE_HISTORY;
284 // This code does not determine if the data will be used.
288 bool PanGesture::ReadAndResampleGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
290 PanInfo lastReadGesture;
291 Dali::Mutex::ScopedLock lock( mMutex );
292 while( mReadPosition != mWritePosition )
294 // Copy the gesture first
295 lastReadGesture = mGestures[mReadPosition];
298 mProfiling->mRawData.push_back( PanGestureProfiling::Position( lastReadGesture.time, lastReadGesture.screen.position,
299 lastReadGesture.screen.displacement, lastReadGesture.screen.velocity, lastReadGesture.state ) );
302 info.frameGesture.screen.position += lastReadGesture.screen.position;
303 info.frameGesture.local.position += lastReadGesture.local.position;
304 info.frameGesture.screen.velocity += lastReadGesture.screen.velocity;
305 info.frameGesture.local.velocity += lastReadGesture.local.velocity;
307 if( lastReadGesture.state == Gesture::Started )
309 // Clear just finished as we have started new pan.
310 info.justFinished = false;
311 info.justStarted = true;
315 info.justFinished |= ( lastReadGesture.state == Gesture::Finished || lastReadGesture.state == Gesture::Cancelled );
318 // Add event to history
319 mPanHistory.push_back( lastReadGesture );
321 // Update our read position.
322 ++info.eventsThisFrame;
324 mReadPosition %= PAN_GESTURE_HISTORY;
327 bool updateProperties = false;
328 if( info.eventsThisFrame > 0 )
330 // Some events were read this frame.
331 mTargetGesture = lastReadGesture;
333 if( info.eventsThisFrame > 1 )
335 const float eventsThisFrame = static_cast<float>( info.eventsThisFrame );
336 info.frameGesture.screen.position /= eventsThisFrame;
337 info.frameGesture.local.position /= eventsThisFrame;
338 info.frameGesture.screen.velocity /= eventsThisFrame;
339 info.frameGesture.local.velocity /= eventsThisFrame;
341 info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position;
342 info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position;
348 info.frameGesture.screen.displacement = lastReadGesture.screen.displacement;
349 info.frameGesture.local.displacement = lastReadGesture.local.displacement;
352 info.frameGesture.time = currentTimestamp;
354 updateProperties = true;
358 // 0 Events this frame.
361 mNotAtTarget = false;
362 info.frameGesture = mTargetGesture;
363 updateProperties = true;
367 info.frameGesture = mLastGesture;
371 return updateProperties;
374 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
376 if( mPredictionMode == PREDICTION_2 )
378 // TODO: Have the two prediction modes share more behavior so some parts of mode 2 can
379 // be used with mode 1 etc. Needs code moving and more importantly testing.
380 return NewAlgorithm( lastVSyncTime, nextVSyncTime );
385 // clear current pan history
387 mPredictionHistory.clear();
390 FrameGestureInfo frameInfo;
391 bool updateProperties = false;
394 // If we are using a form of prediction, read all the input as-is.
395 if( mPredictionMode != PREDICTION_NONE )
397 // Read input required for prediction algorithms.
398 updateProperties = ReadGestures( frameInfo, lastVSyncTime );
402 // Read and resample input.
403 updateProperties = ReadAndResampleGestures( frameInfo, lastVSyncTime );
406 PanInfo frameGesture = frameInfo.frameGesture;
407 PanInfo unmodifiedGesture = frameGesture;
409 // Process input data.
410 mInGesture |= frameInfo.justStarted;
416 mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, frameGesture.screen.position,
417 frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
420 // Perform prediction.
421 if( mPredictionMode == PREDICTION_1 )
423 // Dynamically change the prediction amount according to the pan velocity acceleration.
424 if( !frameInfo.justStarted )
426 if( frameInfo.eventsThisFrame <= 1 )
428 frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length();
431 // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
432 if( fabsf( frameInfo.acceleration ) > ACCELERATION_THRESHOLD )
434 mCurrentPredictionAmount += static_cast<unsigned int>( static_cast<float>( mPredictionAmountAdjustment ) * ( frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f ) );
435 if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
437 mCurrentPredictionAmount = 0;
443 if( !mPredictionAmountOverridden )
445 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
446 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[0];
448 mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
451 mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
453 // Calculate the delta of positions before the prediction
454 Vector2 deltaPosition = frameGesture.screen.position - mLastUnmodifiedGesture.screen.position;
456 // Make latest gesture equal to current gesture before interpolation
457 PredictionMode1( frameInfo.eventsThisFrame, frameGesture, mPanHistory, lastVSyncTime, nextVSyncTime );
459 // Calculate the delta of positions after the prediction.
460 Vector2 deltaPredictedPosition = frameGesture.screen.position - mLastGesture.screen.position;
462 // If the change in the prediction has a different sign than the change in the actual position,
463 // there is overshot (i.e. the current prediction is too large). Return the previous prediction
464 // to give the user's finger a chance to catch up with where we have panned to.
465 bool overshotXAxis = false;
466 bool overshotYAxis = false;
467 if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 )
468 || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) )
470 overshotXAxis = true;
471 frameGesture.screen.position.x = mLastGesture.screen.position.x;
474 if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 )
475 || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) )
477 overshotYAxis = true;
478 frameGesture.screen.position.y = mLastGesture.screen.position.y;
481 // If there is overshot in one axis, reduce the possible overshot in the other axis,
482 // and reduce the prediction amount so that it doesn't overshoot as easily next time.
483 if(overshotXAxis || overshotYAxis)
485 mCurrentPredictionAmount -= mPredictionAmountAdjustment;
486 if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
488 mCurrentPredictionAmount = 0;
490 mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
492 if( overshotXAxis && !overshotYAxis )
494 frameGesture.screen.position.y = ( mLastGesture.screen.position.y + frameGesture.screen.position.y ) * 0.5f;
497 if( overshotYAxis && !overshotXAxis )
499 frameGesture.screen.position.x = ( mLastGesture.screen.position.x + frameGesture.screen.position.x ) * 0.5f;
503 updateProperties = true;
506 // Perform smoothing.
507 switch( mSmoothingMode )
510 case SMOOTHING_MULTI_TAP:
513 // TODO: Old algorithm to be able to use multitap smoothing.
516 case SMOOTHING_LAST_VALUE:
518 if( !frameInfo.justStarted )
520 if( !mSmoothingAmountOverridden )
522 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
523 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[0];
525 BlendPoints( frameGesture, mLastGesture, mSmoothingAmount );
531 if( updateProperties )
533 // only update properties if event received
534 // set latest gesture to raw pan info with unchanged time
535 mPanning.Set( mInGesture & !frameInfo.justFinished );
536 mScreenPosition.Set( frameGesture.screen.position );
537 mScreenDisplacement.Set( frameGesture.screen.displacement );
538 mScreenVelocity.Set( frameGesture.screen.velocity );
539 mLocalPosition.Set( frameGesture.local.position );
540 mLocalDisplacement.Set( frameGesture.local.displacement );
541 mLocalVelocity.Set( frameGesture.local.velocity );
546 mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( frameGesture.time, frameGesture.screen.position,
547 frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
551 mLastGesture = frameGesture;
552 mLastUnmodifiedGesture = unmodifiedGesture;
554 mInGesture = mInGesture && !frameInfo.justFinished;
555 if( mProfiling && frameInfo.justFinished )
557 mProfiling->PrintData();
558 mProfiling->ClearData();
561 return updateProperties;
564 const GesturePropertyBool& PanGesture::GetPanningProperty() const
569 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
571 return mScreenPosition;
574 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
576 return mScreenVelocity;
579 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
581 return mScreenDisplacement;
584 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
586 return mLocalPosition;
589 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
591 return mLocalDisplacement;
594 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
596 return mLocalVelocity;
599 void PanGesture::SetPredictionMode(PredictionMode mode)
601 mPredictionMode = mode;
604 void PanGesture::SetPredictionAmount(unsigned int amount)
606 mPredictionAmount = amount;
607 mPredictionAmountOverridden = true;
610 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
612 mMaxPredictionAmount = amount;
615 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
617 mMinPredictionAmount = amount;
620 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
622 mPredictionAmountAdjustment = amount;
625 void PanGesture::SetSmoothingMode(SmoothingMode mode)
627 mSmoothingMode = mode;
630 void PanGesture::SetSmoothingAmount(float amount)
632 mSmoothingAmount = amount;
633 mSmoothingAmountOverridden = true;
636 void PanGesture::SetUseActualTimes( bool value )
638 mUseActualTimes = value;
641 void PanGesture::SetInterpolationTimeRange( int value )
643 mInterpolationTimeRange = value;
646 void PanGesture::SetScalarOnlyPredictionEnabled( bool value )
648 mScalarOnlyPredictionEnabled = value;
651 void PanGesture::SetTwoPointPredictionEnabled( bool value )
653 mTwoPointPredictionEnabled = value;
656 void PanGesture::SetTwoPointInterpolatePastTime( int value )
658 mTwoPointPastInterpolateTime = value;
661 void PanGesture::SetTwoPointVelocityBias( float value )
663 mTwoPointVelocityBias = value;
666 void PanGesture::SetTwoPointAccelerationBias( float value )
668 mTwoPointAccelerationBias = value;
671 void PanGesture::SetMultitapSmoothingRange( int value )
673 mMultiTapSmoothingRange = value;
676 void PanGesture::EnableProfiling()
680 mProfiling = new PanGestureProfiling();
684 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
686 mScreenPosition.Reset();
687 mScreenDisplacement.Reset();
688 mLocalPosition.Reset();
689 mLocalDisplacement.Reset();
693 PanGesture::PanGesture()
698 mNotAtTarget( false ),
700 mPredictionAmountOverridden( false ),
701 mSmoothingAmountOverridden( false ),
704 // Set environment variable defaults:
705 mPredictionMode( DEFAULT_PREDICTION_MODE ),
706 mPredictionAmount( DEFAULT_PREDICTION_AMOUNT[0] ),
707 mCurrentPredictionAmount( DEFAULT_PREDICTION_AMOUNT[0] ),
708 mMaxPredictionAmount( DEFAULT_MAX_PREDICTION_AMOUNT ),
709 mMinPredictionAmount( DEFAULT_MIN_PREDICTION_AMOUNT ),
710 mPredictionAmountAdjustment( DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT ),
711 mSmoothingMode( DEFAULT_SMOOTHING_MODE ),
712 mSmoothingAmount( DEFAULT_SMOOTHING_AMOUNT[0] ),
713 mUseActualTimes( DEFAULT_USE_ACTUAL_TIMES ),
714 mInterpolationTimeRange( DEFAULT_INTERPOLATION_TIME_RANGE ),
715 mScalarOnlyPredictionEnabled( DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED ),
716 mTwoPointPredictionEnabled( DEFAULT_TWO_POINT_PREDICTION_ENABLED ),
717 mTwoPointPastInterpolateTime( DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME ),
718 mTwoPointVelocityBias( DEFAULT_TWO_POINT_VELOCITY_BIAS ),
719 mTwoPointAccelerationBias( DEFAULT_TWO_POINT_ACCELERATION_BIAS ),
720 mMultiTapSmoothingRange( DEFAULT_MULTITAP_SMOOTHING_RANGE )
725 // Prediction mode 2 related code and functions follow:
727 unsigned int PanGesture::ReadFrameEvents()
729 unsigned int eventsThisFrame;
730 // Copy the events into a linear buffer while holding the mutex.
731 // This is so the lock is not held while any processing is done.
732 Dali::Mutex::ScopedLock lock( mMutex );
733 for( eventsThisFrame = 0; mReadPosition != mWritePosition; ++eventsThisFrame )
735 mReadGestures[ eventsThisFrame ] = mGestures[ mReadPosition ];
737 mReadPosition %= PAN_GESTURE_HISTORY;
739 return eventsThisFrame;
742 // TODO: eventsThisFrame parameter can be removed if we use a smarter container.
743 bool PanGesture::InputRateConversion( PanInfo& rateConvertedGesture, unsigned int eventsThisFrame,
744 unsigned int currentFrameTime, unsigned int lastFrameTime, bool& justStarted, bool& justFinished )
746 // TODO: Lots of variables on the stack. Needs optimizing.
748 PanInfo firstReadGesture;
749 unsigned int eventsKeptThisFrame = 0;
751 for( unsigned int readPosition = 0; readPosition < eventsThisFrame; ++readPosition )
753 // Copy the gesture first
754 readGesture = mReadGestures[ readPosition ];
758 mProfiling->mRawData.push_back( PanGestureProfiling::Position( readGesture.time, readGesture.screen.position,
759 readGesture.screen.displacement, readGesture.screen.velocity, readGesture.state ) );
762 if( readGesture.state == Gesture::Started )
766 mPredictionHistory.clear();
767 mLastAcceleration.local = Vector2::ZERO;
768 mLastAcceleration.screen = Vector2::ZERO;
769 mLastInterpolatedAcceleration.local = Vector2::ZERO;
770 mLastInterpolatedAcceleration.screen = Vector2::ZERO;
771 mLastInitialAcceleration.local = Vector2::ZERO;
772 mLastInitialAcceleration.screen = Vector2::ZERO;
774 mLastGesture = startInfo;
775 mLastSecondInterpolatedPoint = startInfo;
776 mLastPredictedPoint = startInfo;
777 mLastFrameReadGesture = startInfo;
778 rateConvertedGesture = startInfo;
779 firstReadGesture = readGesture;
780 eventsKeptThisFrame = 0;
781 mNotAtTarget = false;
782 justFinished = false;
786 if( !mPredictionAmountOverridden )
788 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
789 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[1];
791 mCurrentPredictionAmount = mPredictionAmount;
795 justFinished |= ( readGesture.state == Gesture::Finished || readGesture.state == Gesture::Cancelled );
798 rateConvertedGesture.screen.position += readGesture.screen.position;
799 rateConvertedGesture.local.position += readGesture.local.position;
800 rateConvertedGesture.screen.velocity += readGesture.screen.velocity;
801 rateConvertedGesture.local.velocity += readGesture.local.velocity;
802 rateConvertedGesture.screen.displacement += readGesture.screen.displacement;
803 rateConvertedGesture.local.displacement += readGesture.local.displacement;
805 ++eventsKeptThisFrame;
808 bool storeGesture = false;
809 if( eventsKeptThisFrame > 0 )
811 // Some events were read this frame.
812 if( eventsKeptThisFrame > 1 )
814 const float eventDivisor = static_cast<float>( eventsKeptThisFrame );
815 rateConvertedGesture.screen.position /= eventDivisor;
816 rateConvertedGesture.local.position /= eventDivisor;
817 rateConvertedGesture.screen.velocity /= eventDivisor;
818 rateConvertedGesture.local.velocity /= eventDivisor;
819 rateConvertedGesture.screen.displacement /= eventDivisor;
820 rateConvertedGesture.local.displacement /= eventDivisor;
822 mTargetGesture = readGesture;
827 mNotAtTarget = false;
830 rateConvertedGesture.time = currentFrameTime;
835 // We did not get any event this frame.
836 // If we just started (or aren't in a gesture), exit.
837 if( !mInGesture || justStarted )
839 // We cannot guess what the event could be as we have no other events to base the guess from.
843 // As we are currently in a gesture, we can estimate an event.
844 readGesture = mLastFrameReadGesture;
845 readGesture.time = currentFrameTime;
847 // Take the last event, halve the acceleration, and use that.
848 const float accelerationDegrade = 2.0f;
849 Vector2 degradedAccelerationLocal( mLastAcceleration.local /= accelerationDegrade );
850 Vector2 degradedAccelerationScreen( mLastAcceleration.screen /= accelerationDegrade );
852 float outputTimeGranularity( GetDivisibleTimeDifference( currentFrameTime, lastFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
854 readGesture.local.velocity = degradedAccelerationLocal * outputTimeGranularity;
855 readGesture.local.displacement = readGesture.local.velocity * outputTimeGranularity;
856 readGesture.local.position = mLastFrameReadGesture.local.position + readGesture.local.displacement;
857 readGesture.screen.velocity = degradedAccelerationScreen * outputTimeGranularity;
858 readGesture.screen.displacement = readGesture.screen.velocity * outputTimeGranularity;
859 readGesture.screen.position = mLastFrameReadGesture.screen.position + readGesture.screen.displacement;
861 rateConvertedGesture = readGesture;
862 eventsKeptThisFrame = 1;
866 if( eventsKeptThisFrame > 0 )
868 // Store last read gesture.
869 readGesture.time = currentFrameTime;
870 mLastFrameReadGesture = readGesture;
872 if( eventsKeptThisFrame > 2 )
874 DALI_LOG_WARNING( "Got events this frame:%d (more than 2 will compromise result)\n", eventsKeptThisFrame );
880 // Store final converted result.
881 mPanHistory.push_back( rateConvertedGesture );
886 bool PanGesture::InterpolatePoint( PanInfoHistory& history, unsigned int currentTime, unsigned int targetTime, unsigned int range,
887 PanInfo& outPoint, RelativeVectors& acceleration, int outputTimeGranularity, bool eraseUnused )
889 unsigned int maxHistoryTime = targetTime - range;
890 unsigned int tapsUsed = 0;
891 outPoint.time = targetTime;
892 float divisor = 0.0f;
893 float accelerationDivisor = 0.0f;
894 PanInfoHistoryIter historyBegin = history.begin();
895 PanInfoHistoryIter lastIt = history.end();
896 bool pointGenerated = false;
897 bool havePreviousPoint = false;
898 RelativeVectors newAcceleration;
900 // Iterate through point history to perform interpolation.
901 for( PanInfoHistoryIter it = historyBegin; it != history.end(); )
903 unsigned int gestureTime = it->time;
905 if( gestureTime < maxHistoryTime )
907 // Too far in the past, discard.
908 // Clean history as we go (if requested).
911 it = history.erase( it );
921 float timeDelta( static_cast<float>( abs( int( targetTime - gestureTime ) ) ) );
922 // Handle low time deltas.
923 if( timeDelta < 1.0f )
928 outPoint.local.position += it->local.position / timeDelta;
929 outPoint.screen.position += it->screen.position / timeDelta;
930 outPoint.local.velocity += it->local.velocity / timeDelta;
931 outPoint.screen.velocity += it->screen.velocity / timeDelta;
932 outPoint.local.displacement += it->local.displacement / timeDelta;
933 outPoint.screen.displacement += it->screen.displacement / timeDelta;
935 divisor += 1.0f / timeDelta;
937 // Acceleration requires a previous point.
938 if( havePreviousPoint )
940 // Time delta of input.
941 float timeDifference( GetDivisibleTimeDifference( it->time, lastIt->time, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
943 newAcceleration.local += ( ( it->local.velocity - lastIt->local.velocity ) / timeDifference ) / timeDelta;
944 newAcceleration.screen += ( ( it->screen.velocity - lastIt->screen.velocity ) / timeDifference ) / timeDelta;
946 accelerationDivisor += 1.0f / timeDelta;
950 havePreviousPoint = true;
959 // Divide results by their respective divisors.
964 outPoint.local.position /= divisor;
965 outPoint.screen.position /= divisor;
966 outPoint.local.velocity /= divisor;
967 outPoint.screen.velocity /= divisor;
968 outPoint.local.displacement /= divisor;
969 outPoint.screen.displacement /= divisor;
974 if( accelerationDivisor > 0.0f )
976 newAcceleration.local /= accelerationDivisor;
977 newAcceleration.screen /= accelerationDivisor;
980 float accelerationSmoothing( ACCELERATION_SMOOTHING );
981 newAcceleration.local = ( acceleration.local * accelerationSmoothing ) + ( newAcceleration.local * ( 1.0f - accelerationSmoothing ) );
982 newAcceleration.screen = ( acceleration.screen * accelerationSmoothing ) + ( newAcceleration.screen * ( 1.0f - accelerationSmoothing ) );
986 // If we just started, last velocity was 0. So difference of zero to current velocity over time gives acceleration of the first point.
987 newAcceleration.local = outPoint.local.velocity / static_cast<float>( outputTimeGranularity );
988 newAcceleration.screen = outPoint.screen.velocity / static_cast<float>( outputTimeGranularity );
990 pointGenerated = true;
993 acceleration.local = newAcceleration.local;
994 acceleration.screen = newAcceleration.screen;
995 return pointGenerated;
998 float PanGesture::GetDivisibleTimeDifference( int timeA, int timeB, float minimumDelta, float overrideDifference )
1000 float timeDifference( overrideDifference );
1001 if( mUseActualTimes )
1003 timeDifference = static_cast<float>( abs( timeA - timeB ) );
1004 if( timeDifference < minimumDelta )
1006 timeDifference = minimumDelta;
1009 return timeDifference;
1012 void PanGesture::LimitAccelerationChange( RelativeVectors& currentAcceleration, RelativeVectors& lastAcceleration, float changeLimit )
1014 // We don't use the float parameter version of clamp here, as that will create the capping vectors twice in total.
1015 Vector2 capMinimum( -changeLimit, -changeLimit );
1016 Vector2 capMaximum( changeLimit, changeLimit );
1017 Vector2 accelerationDeltaLocal( currentAcceleration.local - lastAcceleration.local );
1018 Vector2 accelerationDeltaScreen( currentAcceleration.screen - lastAcceleration.screen );
1019 accelerationDeltaLocal.Clamp( capMinimum, capMaximum );
1020 accelerationDeltaScreen.Clamp( capMinimum, capMaximum );
1021 currentAcceleration.local = lastAcceleration.local + accelerationDeltaLocal;
1022 currentAcceleration.screen = lastAcceleration.screen + accelerationDeltaScreen;
1025 void PanGesture::PredictionMode2( PanInfo& startPoint, RelativeVectors& accelerationToUse,
1026 PanInfo& predictedPoint, unsigned int currentFrameTime, unsigned int previousFrameTime, bool noPreviousData )
1028 // Do the prediction (based on mode).
1029 if( mScalarOnlyPredictionEnabled )
1031 // We are doing scalar based prediction.
1032 // This divisor is to help tuning by giving the scalar only result
1033 // a similar prediction amount to the integrated result.
1034 float scalarVelocityMultiplier = static_cast<float>( mCurrentPredictionAmount ) / 1.364f;
1035 predictedPoint.local.position = startPoint.local.position + ( startPoint.local.velocity * scalarVelocityMultiplier );
1036 predictedPoint.screen.position = startPoint.screen.position + ( startPoint.screen.velocity * scalarVelocityMultiplier );
1040 // We are doing integration based prediction.
1041 float predictionDelta = static_cast<float>( mCurrentPredictionAmount );
1043 predictedPoint.local.position = startPoint.local.position + ( startPoint.local.velocity * predictionDelta ) +
1044 ( accelerationToUse.local * ( predictionDelta * predictionDelta * 0.5f ) );
1045 predictedPoint.screen.position = startPoint.screen.position + ( startPoint.screen.velocity * predictionDelta ) +
1046 ( accelerationToUse.screen * ( predictionDelta * predictionDelta * 0.5f ) );
1049 // Calculate remaining gesture data from the result.
1050 float timeDifference( GetDivisibleTimeDifference( currentFrameTime, previousFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1051 if( noPreviousData )
1053 predictedPoint.local.displacement = predictedPoint.local.position - startPoint.local.position;
1054 predictedPoint.screen.displacement = predictedPoint.screen.position - startPoint.screen.position;
1058 predictedPoint.local.displacement = predictedPoint.local.position - mLastPredictedPoint.local.position;
1059 predictedPoint.screen.displacement = predictedPoint.screen.position - mLastPredictedPoint.screen.position;
1061 predictedPoint.local.velocity = predictedPoint.local.displacement / timeDifference;
1062 predictedPoint.screen.velocity = predictedPoint.screen.displacement / timeDifference;
1064 // TODO: Experimental - not used at run time. Left in code for reference only.
1065 if( TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION )
1067 // Overshoot protection
1068 if( !noPreviousData )
1070 if( ( mLastPredictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0 )
1071 || ( mLastPredictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0 ) )
1073 predictedPoint.local.position.x = mLastPredictedPoint.local.position.x;
1074 predictedPoint.screen.position.x = mLastPredictedPoint.screen.position.x;
1075 mPredictionHistory.clear();
1077 if( ( mLastPredictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0 )
1078 || ( mLastPredictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0 ) )
1080 predictedPoint.local.position.y = mLastPredictedPoint.local.position.y;
1081 predictedPoint.screen.position.y = mLastPredictedPoint.screen.position.y;
1082 mPredictionHistory.clear();
1087 predictedPoint.time = currentFrameTime;
1088 mLastPredictedPoint = predictedPoint;
1091 // TODO: This needs a better name! It is called this instead of prediction mode 2 because:
1092 // 1) It is the entire workflow, not just prediction.
1093 // 2) To make it less confusing as there is a function that does prediction alone called PerformPredictionMode2.
1094 // Ultimately we need to combine the old and new code modularly so there is one code path that can optionally run different functions based on configuration.
1095 // At the moment, the differences between the inputs & outputs of these different functions prevent that, but this can be resolved.
1096 bool PanGesture::NewAlgorithm( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
1100 // clear current pan history
1101 mPanHistory.clear();
1102 mPredictionHistory.clear();
1105 /*#########################################################################################
1106 #### Read in all gestures received this frame first (holding a lock for a short time)
1107 #########################################################################################*/
1109 unsigned int eventsThisFrame = ReadFrameEvents();
1111 /*#########################################################################################
1112 #### Perform input rate-conversion on all gestures received this frame.
1113 #### This also populates the pan history.
1114 #########################################################################################*/
1116 bool justStarted = false;
1117 bool justFinished = false;
1118 PanInfo rateConvertedGesture;
1119 if( !InputRateConversion( rateConvertedGesture, eventsThisFrame, nextVSyncTime, lastVSyncTime, justStarted, justFinished ) )
1121 // There's nothing we can do with the input, exit.
1125 /*#########################################################################################
1126 #### If we are in gesture, Get first interpolated point with: target time = current time
1127 #########################################################################################*/
1129 bool performUpdate = false;
1130 RelativeVectors currentAcceleration;
1131 currentAcceleration.local = mLastInitialAcceleration.local;
1132 currentAcceleration.screen = mLastInitialAcceleration.screen;
1134 if( mInGesture || justStarted )
1136 // Get first interpolated point.
1137 // TODO: Erase time should be maximum of both interpolated point ranges in past.
1138 PanInfo targetPoint;
1139 float outputTimeGranularity( GetDivisibleTimeDifference( nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1140 bool pointGenerated = InterpolatePoint( mPanHistory, nextVSyncTime, nextVSyncTime, mInterpolationTimeRange,
1141 targetPoint, currentAcceleration, static_cast<int>( outputTimeGranularity ), true ); // truncated
1142 if( pointGenerated )
1144 mLastInitialAcceleration.local = currentAcceleration.local;
1145 mLastInitialAcceleration.screen = currentAcceleration.screen;
1146 performUpdate = true;
1150 targetPoint = rateConvertedGesture;
1151 currentAcceleration.local = mLastInitialAcceleration.local;
1152 currentAcceleration.screen = mLastInitialAcceleration.screen;
1153 // TODO: Potentially do something to substitute lack of generated point (and perform update).
1156 /*#########################################################################################
1157 #### Limit the change of acceleration of the first interpolated point since last time
1158 #########################################################################################*/
1162 LimitAccelerationChange( currentAcceleration, mLastAcceleration, ACCELERATION_CAP );
1164 mLastAcceleration.local = currentAcceleration.local;
1165 mLastAcceleration.screen = currentAcceleration.screen;
1167 /*#########################################################################################
1168 #### Get second interpolated point, and blend the resultant velocity and acceleration (optional)
1169 #########################################################################################*/
1172 RelativeVectors interpolatedAcceleration;
1173 if( mTwoPointPredictionEnabled )
1175 // Get second interpolated point with target time = current time - past interpolate time.
1176 unsigned int pastInterpolateTime = nextVSyncTime - mTwoPointPastInterpolateTime;
1178 RelativeVectors interpolatedAcceleration;
1179 interpolatedAcceleration.local = mLastInterpolatedAcceleration.local;
1180 interpolatedAcceleration.screen = mLastInterpolatedAcceleration.screen;
1181 if( !InterpolatePoint( mPanHistory, nextVSyncTime, pastInterpolateTime, mTwoPointPastInterpolateTime,
1182 outPoint, interpolatedAcceleration, static_cast<int>( outputTimeGranularity ), false ) ) // truncated
1186 outPoint = targetPoint;
1190 outPoint = mLastSecondInterpolatedPoint;
1193 mLastInterpolatedAcceleration.local = interpolatedAcceleration.local;
1194 mLastInterpolatedAcceleration.screen = interpolatedAcceleration.screen;
1195 mLastSecondInterpolatedPoint = outPoint;
1197 // Combine the first interpolated point and the second interpolated point.
1198 // by mixing them with the configured amount. This is done for acceleration and velocity.
1199 // It could be optionally done for position too, but this typically is worse as it means we have to predict further ahead.
1200 float currentVelocityMultiplier( 1.0f - mTwoPointVelocityBias );
1201 float lastVelocityMultiplier( mTwoPointVelocityBias );
1202 targetPoint.local.velocity = ( outPoint.local.velocity * lastVelocityMultiplier ) + ( targetPoint.local.velocity * currentVelocityMultiplier );
1203 targetPoint.screen.velocity = ( outPoint.screen.velocity * lastVelocityMultiplier ) + ( targetPoint.screen.velocity * currentVelocityMultiplier );
1204 float currentAccelerationMultiplier( 1.0f - mTwoPointAccelerationBias );
1205 float lastAccelerationMultiplier( mTwoPointAccelerationBias );
1206 currentAcceleration.local = ( interpolatedAcceleration.local * lastAccelerationMultiplier ) + ( currentAcceleration.local * currentAccelerationMultiplier );
1207 currentAcceleration.screen = ( interpolatedAcceleration.screen * lastAccelerationMultiplier ) + ( currentAcceleration.screen * currentAccelerationMultiplier );
1210 /*#########################################################################################
1211 #### Perform prediction
1212 #########################################################################################*/
1214 PanInfo predictedPoint;
1215 PredictionMode2( targetPoint, currentAcceleration, predictedPoint, nextVSyncTime, lastVSyncTime, justStarted );
1216 targetPoint = predictedPoint;
1218 /*#########################################################################################
1220 #########################################################################################*/
1222 // If we are using multi-tap smoothing, keep a history of predicted results.
1223 if( mSmoothingMode == SMOOTHING_MULTI_TAP )
1225 mPredictionHistory.push_back( targetPoint );
1230 float outputTimeGranularity( GetDivisibleTimeDifference( nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1231 if( mSmoothingMode == SMOOTHING_MULTI_TAP )
1233 // Perform Multi-tap Smoothing.
1234 RelativeVectors blank;
1235 InterpolatePoint( mPredictionHistory, nextVSyncTime, nextVSyncTime, mMultiTapSmoothingRange,
1236 targetPoint, blank, static_cast<int>( outputTimeGranularity ), true ); // truncated
1240 // Perform Single-tap Smoothing.
1241 if( !mSmoothingAmountOverridden )
1243 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
1244 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[1];
1246 BlendPoints( targetPoint, mLastGesture, mSmoothingAmount );
1249 /*#########################################################################################
1250 #### Finalize other point data (from position)
1251 #########################################################################################*/
1253 targetPoint.local.displacement = targetPoint.local.position - mLastGesture.local.position;
1254 targetPoint.local.velocity = targetPoint.local.displacement / outputTimeGranularity;
1255 targetPoint.screen.displacement = targetPoint.screen.position - mLastGesture.screen.position;
1256 targetPoint.screen.velocity = targetPoint.screen.displacement / outputTimeGranularity;
1259 /*#########################################################################################
1260 #### Send out the new point, by setting the properties
1261 #### (Constraints will automatically react to this)
1262 #########################################################################################*/
1266 mPanning.Set( mInGesture & !justFinished );
1267 mScreenPosition.Set( targetPoint.screen.position );
1268 mScreenDisplacement.Set( targetPoint.screen.displacement );
1269 mScreenVelocity.Set( targetPoint.screen.velocity );
1270 mLocalPosition.Set( targetPoint.local.position );
1271 mLocalDisplacement.Set( targetPoint.local.displacement );
1272 mLocalVelocity.Set( targetPoint.local.velocity );
1274 mLastGesture = targetPoint;
1278 mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( targetPoint.time, targetPoint.screen.position,
1279 targetPoint.screen.displacement, targetPoint.screen.velocity, targetPoint.state ) );
1284 mInGesture = mInGesture && !justFinished;
1286 return performUpdate;
1290 } // namespace SceneGraph
1292 } // namespace Internal