2 * Copyright (c) 2014 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>
38 const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm TODO: Possibly make this configurable.
39 const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount
40 const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 5; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
41 const unsigned int DEFAULT_MAX_PREDICTION_INTERPOLATION = 32; ///< the upper bound of the range to clamp the prediction interpolation
42 const unsigned int DEFAULT_MIN_PREDICTION_INTERPOLATION = 0; ///< the lower bound of the range to clamp the prediction interpolation
43 const unsigned int DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT = 2; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes
44 const float DEFAULT_SMOOTHING_AMOUNT = 0.25f; ///< how much to interpolate pan position and displacement from last vsync time
45 } // unnamed namespace
47 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
48 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_1 + 1;
50 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
51 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_LAST_VALUE + 1;
53 PanGesture* PanGesture::New()
55 return new PanGesture();
58 PanGesture::~PanGesture()
63 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
65 Dali::Mutex::ScopedLock lock( mMutex );
66 mGestures[ mWritePosition ] = gesture;
68 // Update our write position.
70 mWritePosition %= PAN_GESTURE_HISTORY;
73 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
75 PanInfoHistoryConstIter endIter = panHistory.end();
76 PanInfoHistoryIter iter = panHistory.begin();
77 while( iter != endIter && panHistory.size() > minEvents)
79 PanInfo currentGesture = *iter;
80 if( currentTime < currentGesture.time + maxAge )
84 iter = panHistory.erase(iter);
85 endIter = panHistory.end();
88 // dont want more than 5 previous predictions for smoothing
89 iter = mPredictionHistory.begin();
90 while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
92 iter = mPredictionHistory.erase(iter);
96 void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
98 RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
99 size_t panHistorySize = panHistory.size();
100 if( panHistorySize == 0 )
102 // cant do any prediction without a history
106 PanInfoHistoryConstIter endIter = panHistory.end();
107 PanInfoHistoryIter iter = panHistory.begin();
108 Vector2 screenVelocity = gestureOut.screen.velocity;
109 Vector2 localVelocity = gestureOut.local.velocity;
110 Vector2 screenDisplacement = gestureOut.screen.displacement;
111 Vector2 localDisplacement = gestureOut.local.displacement;
113 bool havePreviousAcceleration = false;
114 bool previousVelocity = false;
115 float previousAccel = 0.0f;
116 unsigned int lastTime(0);
118 unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
120 if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
122 interpolationTime -= gestureOut.time;
126 interpolationTime = 0u;
129 while( iter != endIter )
131 PanInfo currentGesture = *iter;
132 if( !previousVelocity )
134 // not yet set a previous velocity
135 screenVelocity = currentGesture.screen.velocity;
136 previousVelocity = true;
137 lastTime = currentGesture.time;
141 float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
142 float velMag = currentGesture.screen.velocity.Length();
143 float velDiff = velMag - screenVelocity.Length();
144 float acceleration = 0.0f;
147 if (currentGesture.time > lastTime) // Guard against invalid timestamps
149 time = static_cast<float>( currentGesture.time - lastTime );
151 if( time > Math::MACHINE_EPSILON_1 )
153 acceleration = velDiff / time;
156 float newVelMag = 0.0f;
157 int currentInterpolation = interpolationTime;
158 if( !havePreviousAcceleration )
161 havePreviousAcceleration = true;
165 newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
168 if( velMag > Math::MACHINE_EPSILON_1 )
170 velMod = newVelMag / velMag;
172 gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
173 gestureOut.local.velocity = currentGesture.local.velocity * velMod;
174 screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
175 localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
176 screenVelocity = currentGesture.screen.velocity;
177 localVelocity = currentGesture.local.velocity;
178 previousAccel = acceleration;
181 // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
182 // add interpolated distance and position to current
183 // work out interpolated velocity
184 gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
185 gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
186 gestureOut.screen.displacement = screenDisplacement;
187 gestureOut.local.displacement = localDisplacement;
188 gestureOut.time += interpolationTime;
191 void PanGesture::SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
195 gestureOut.screen.displacement = Vector2::ZERO;
196 gestureOut.screen.velocity = Vector2::ZERO;
197 gestureOut.local.displacement = Vector2::ZERO;
198 gestureOut.local.velocity = Vector2::ZERO;
202 gestureOut.screen.position -= (gestureOut.screen.position - mLastGesture.screen.position) * 0.5f * (1.0f - mSmoothingAmount);
203 gestureOut.local.position -= (gestureOut.local.position - mLastGesture.local.position) * 0.5f * (1.0f - mSmoothingAmount);
204 // make current displacement relative to previous update-frame now.
205 gestureOut.screen.displacement = gestureOut.screen.position - mLastGesture.screen.position;
206 gestureOut.local.displacement = gestureOut.local.position - mLastGesture.local.position;
207 // calculate velocity relative to previous update-frame
208 float timeDiff( gestureOut.time - mLastGesture.time );
209 gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
210 gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
214 void PanGesture::SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
217 mPredictionHistory.push_back(gestureOut);
219 // now smooth current pan event
220 PanInfoHistoryConstIter endIter = mPredictionHistory.end() - 1;
221 PanInfoHistoryIter iter = mPredictionHistory.begin();
223 float distanceMod = 1.0f;
225 while( iter != endIter )
227 PanInfo currentGesture = *iter;
228 float newDistanceMod = currentGesture.screen.displacement.Length() / gestureOut.screen.displacement.Length();
229 distanceMod = ((distanceMod * weight) + (newDistanceMod * (1.0f - weight)));
233 gestureOut.screen.position -= gestureOut.screen.displacement;
234 gestureOut.local.position -= gestureOut.local.displacement;
235 gestureOut.screen.displacement *= distanceMod;
236 gestureOut.local.displacement *= distanceMod;
237 gestureOut.screen.position += gestureOut.screen.displacement;
238 gestureOut.local.position += gestureOut.local.displacement;
241 bool PanGesture::ReadGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
243 unsigned int previousReadPosition = 0;
244 bool eventFound = false;
245 info.frameGesture = mLastUnmodifiedGesture;
247 while( mReadPosition != mWritePosition )
249 // Copy the gesture first
250 PanInfo currentGesture( mGestures[mReadPosition] );
254 mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) );
256 info.frameGesture.local.position = currentGesture.local.position;
257 info.frameGesture.local.velocity = currentGesture.local.velocity;
258 info.frameGesture.screen.position = currentGesture.screen.position;
259 info.frameGesture.screen.velocity = currentGesture.screen.velocity;
261 if( info.eventsThisFrame > 0 )
263 info.acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
268 info.frameGesture.local.displacement = currentGesture.local.displacement;
269 info.frameGesture.screen.displacement = currentGesture.screen.displacement;
274 info.frameGesture.local.displacement += currentGesture.local.displacement;
275 info.frameGesture.screen.displacement += currentGesture.screen.displacement;
277 info.frameGesture.time = currentGesture.time;
279 // add event to history
280 mPanHistory.push_back( currentGesture );
281 if( currentGesture.state == Gesture::Started )
283 info.justStarted = true;
284 // clear just finished as we have started new pan
285 info.justFinished = false;
287 info.justFinished |= ( currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled );
289 // Update our read position.
290 previousReadPosition = mReadPosition;
291 ++info.eventsThisFrame;
293 mReadPosition %= PAN_GESTURE_HISTORY;
295 // This code does not determine if the data will be used.
299 bool PanGesture::ReadAndResampleGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
301 PanInfo lastReadGesture;
302 Dali::Mutex::ScopedLock lock( mMutex );
303 while( mReadPosition != mWritePosition )
305 // Copy the gesture first
306 lastReadGesture = mGestures[mReadPosition];
309 mProfiling->mRawData.push_back( PanGestureProfiling::Position( lastReadGesture.time, lastReadGesture.screen.position,
310 lastReadGesture.screen.displacement, lastReadGesture.screen.velocity, lastReadGesture.state ) );
313 info.frameGesture.screen.position += lastReadGesture.screen.position;
314 info.frameGesture.local.position += lastReadGesture.local.position;
315 info.frameGesture.screen.velocity += lastReadGesture.screen.velocity;
316 info.frameGesture.local.velocity += lastReadGesture.local.velocity;
318 if( lastReadGesture.state == Gesture::Started )
320 // Clear just finished as we have started new pan.
321 info.justFinished = false;
322 info.justStarted = true;
326 info.justFinished |= ( lastReadGesture.state == Gesture::Finished || lastReadGesture.state == Gesture::Cancelled );
329 // Add event to history
330 mPanHistory.push_back( lastReadGesture );
332 // Update our read position.
333 ++info.eventsThisFrame;
335 mReadPosition %= PAN_GESTURE_HISTORY;
338 bool updateProperties = false;
339 if( info.eventsThisFrame > 0 )
341 // Some events were read this frame.
342 mTargetGesture = lastReadGesture;
344 if( info.eventsThisFrame > 1 )
346 info.frameGesture.screen.position /= info.eventsThisFrame;
347 info.frameGesture.local.position /= info.eventsThisFrame;
348 info.frameGesture.screen.velocity /= info.eventsThisFrame;
349 info.frameGesture.local.velocity /= info.eventsThisFrame;
351 info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position;
352 info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position;
358 info.frameGesture.screen.displacement = lastReadGesture.screen.displacement;
359 info.frameGesture.local.displacement = lastReadGesture.local.displacement;
362 info.frameGesture.time = currentTimestamp;
364 updateProperties = true;
368 // 0 Events this frame.
371 mNotAtTarget = false;
372 info.frameGesture = mTargetGesture;
373 updateProperties = true;
377 info.frameGesture = mLastGesture;
381 return updateProperties;
384 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
388 // clear current pan history
390 mPredictionHistory.clear();
393 FrameGestureInfo frameInfo;
394 bool updateProperties = false;
397 // If we are using a form of prediction, read all the input as-is.
398 if( mPredictionMode != PREDICTION_NONE )
400 // Read input required for prediction algorithms.
401 updateProperties = ReadGestures( frameInfo, lastVSyncTime );
405 // Read and resample input.
406 updateProperties = ReadAndResampleGestures( frameInfo, lastVSyncTime );
409 PanInfo frameGesture = frameInfo.frameGesture;
410 mLastUnmodifiedGesture = frameGesture;
412 // Process input data.
413 mInGesture |= frameInfo.justStarted;
419 mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, frameGesture.screen.position,
420 frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
423 // Perform prediction.
424 switch( mPredictionMode )
426 case PREDICTION_NONE:
432 // Dynamically change the prediction amount according to the pan velocity acceleration.
433 if( !frameInfo.justStarted )
435 if( frameInfo.eventsThisFrame <= 1 )
437 frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length();
440 // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
441 if( fabsf( frameInfo.acceleration ) > ACCELERATION_THRESHOLD )
443 mCurrentPredictionAmount += mPredictionAmountAdjustment * ( frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f );
444 if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
446 mCurrentPredictionAmount = 0;
452 mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
455 mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
457 // Calculate the delta of positions before the prediction
458 Vector2 deltaPosition = frameGesture.screen.position - mLastUnmodifiedGesture.screen.position;
460 // Make latest gesture equal to current gesture before interpolation
461 PredictiveAlgorithm1( frameInfo.eventsThisFrame, frameGesture, mPanHistory, lastVSyncTime, nextVSyncTime );
463 // Calculate the delta of positions after the prediction.
464 Vector2 deltaPredictedPosition = frameGesture.screen.position - mLastGesture.screen.position;
466 // If the change in the prediction has a different sign than the change in the actual position,
467 // there is overshot (i.e. the current prediction is too large). Return the previous prediction
468 // to give the user's finger a chance to catch up with where we have panned to.
469 bool overshotXAxis = false;
470 bool overshotYAxis = false;
471 if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 )
472 || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) )
474 overshotXAxis = true;
475 frameGesture.screen.position.x = mLastGesture.screen.position.x;
478 if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 )
479 || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) )
481 overshotYAxis = true;
482 frameGesture.screen.position.y = mLastGesture.screen.position.y;
485 // If there is overshot in one axis, reduce the possible overshot in the other axis,
486 // and reduce the prediction amount so that it doesn't overshoot as easily next time.
487 if(overshotXAxis || overshotYAxis)
489 mCurrentPredictionAmount -= mPredictionAmountAdjustment;
490 if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
492 mCurrentPredictionAmount = 0;
494 mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
496 if( overshotXAxis && !overshotYAxis )
498 frameGesture.screen.position.y = ( mLastGesture.screen.position.y + frameGesture.screen.position.y ) * 0.5f;
501 if( overshotYAxis && !overshotXAxis )
503 frameGesture.screen.position.x = ( mLastGesture.screen.position.x + frameGesture.screen.position.x ) * 0.5f;
507 updateProperties = true;
512 // Perform smoothing.
513 switch( mSmoothingMode )
520 case SMOOTHING_LAST_VALUE:
522 SmoothingAlgorithm1( frameInfo.justStarted, frameGesture, lastVSyncTime );
527 if( updateProperties )
529 // only update properties if event received
530 // set latest gesture to raw pan info with unchanged time
531 mPanning.Set( mInGesture & !frameInfo.justFinished );
532 mScreenPosition.Set( frameGesture.screen.position );
533 mScreenDisplacement.Set( frameGesture.screen.displacement );
534 mScreenVelocity.Set( frameGesture.screen.velocity );
535 mLocalPosition.Set( frameGesture.local.position );
536 mLocalDisplacement.Set( frameGesture.local.displacement );
537 mLocalVelocity.Set( frameGesture.local.velocity );
542 mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( frameGesture.time, frameGesture.screen.position,
543 frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
547 mLastGesture = frameGesture;
549 mInGesture &= ~frameInfo.justFinished;
550 if( mProfiling && frameInfo.justFinished )
552 mProfiling->PrintData();
553 mProfiling->ClearData();
556 return updateProperties;
559 const GesturePropertyBool& PanGesture::GetPanningProperty() const
564 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
566 return mScreenPosition;
569 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
571 return mScreenVelocity;
574 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
576 return mScreenDisplacement;
579 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
581 return mLocalPosition;
584 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
586 return mLocalDisplacement;
589 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
591 return mLocalVelocity;
594 void PanGesture::SetPredictionMode(PredictionMode mode)
596 mPredictionMode = mode;
599 void PanGesture::SetPredictionAmount(unsigned int amount)
601 mPredictionAmount = amount;
604 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
606 mMaxPredictionAmount = amount;
609 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
611 mMinPredictionAmount = amount;
614 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
616 mPredictionAmountAdjustment = amount;
619 void PanGesture::SetSmoothingMode(SmoothingMode mode)
621 mSmoothingMode = mode;
624 void PanGesture::SetSmoothingAmount(float amount)
626 mSmoothingAmount = amount;
629 void PanGesture::EnableProfiling()
633 mProfiling = new PanGestureProfiling();
637 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
639 mScreenPosition.Reset();
640 mScreenDisplacement.Reset();
641 mLocalPosition.Reset();
642 mLocalDisplacement.Reset();
646 PanGesture::PanGesture()
650 mNotAtTarget( false ),
652 mPredictionMode( DEFAULT_PREDICTION_MODE ),
653 mPredictionAmount( DEFAULT_PREDICTION_INTERPOLATION ),
654 mCurrentPredictionAmount( DEFAULT_PREDICTION_INTERPOLATION ),
655 mMaxPredictionAmount( DEFAULT_MAX_PREDICTION_INTERPOLATION ),
656 mMinPredictionAmount( DEFAULT_MIN_PREDICTION_INTERPOLATION ),
657 mPredictionAmountAdjustment( DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT ),
658 mSmoothingMode( DEFAULT_SMOOTHING_MODE ),
659 mSmoothingAmount( DEFAULT_SMOOTHING_AMOUNT ),
665 } // namespace SceneGraph
667 } // namespace Internal