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>
37 const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm
38 const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount
39 const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 0; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
40 const unsigned int DEFAULT_MAX_PREDICTION_INTERPOLATION = 32; ///< the upper bound of the range to clamp the prediction interpolation
41 const unsigned int DEFAULT_MIN_PREDICTION_INTERPOLATION = 0; ///< the lower bound of the range to clamp the prediction interpolation
42 const unsigned int DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT = 4; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes
43 const float DEFAULT_SMOOTHING_AMOUNT = 1.0f; ///< how much to interpolate pan position and displacement from last vsync time
44 } // unnamed namespace
46 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
47 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_1 + 1;
49 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
50 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_LAST_VALUE + 1;
52 PanGesture* PanGesture::New()
54 return new PanGesture();
57 PanGesture::~PanGesture()
62 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
64 mGestures[ mWritePosition ] = gesture;
66 // Update our write position.
68 mWritePosition %= PAN_GESTURE_HISTORY;
71 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
73 PanInfoHistoryConstIter endIter = panHistory.end();
74 PanInfoHistoryIter iter = panHistory.begin();
75 while( iter != endIter && panHistory.size() > minEvents)
77 PanInfo currentGesture = *iter;
78 if( currentTime < currentGesture.time + maxAge )
82 iter = panHistory.erase(iter);
83 endIter = panHistory.end();
86 // dont want more than 5 previous predictions for smoothing
87 iter = mPredictionHistory.begin();
88 while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
90 iter = mPredictionHistory.erase(iter);
94 void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
96 RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
97 size_t panHistorySize = panHistory.size();
98 if( panHistorySize == 0 )
100 // cant do any prediction without a history
104 PanInfoHistoryConstIter endIter = panHistory.end();
105 PanInfoHistoryIter iter = panHistory.begin();
106 Vector2 screenVelocity = gestureOut.screen.velocity;
107 Vector2 localVelocity = gestureOut.local.velocity;
108 Vector2 screenDisplacement = gestureOut.screen.displacement;
109 Vector2 localDisplacement = gestureOut.local.displacement;
111 bool havePreviousAcceleration = false;
112 bool previousVelocity = false;
113 float previousAccel = 0.0f;
114 unsigned int lastTime(0);
116 unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
117 if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
119 interpolationTime -= gestureOut.time;
123 interpolationTime = 0u;
126 while( iter != endIter )
128 PanInfo currentGesture = *iter;
129 if( !previousVelocity )
131 // not yet set a previous velocity
132 screenVelocity = currentGesture.screen.velocity;
133 previousVelocity = true;
134 lastTime = currentGesture.time;
138 float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
139 float velMag = currentGesture.screen.velocity.Length();
140 float velDiff = velMag - screenVelocity.Length();
141 float acceleration = 0.0f;
144 if (currentGesture.time > lastTime) // Guard against invalid timestamps
146 time = static_cast<float>( currentGesture.time - lastTime );
148 if( time > Math::MACHINE_EPSILON_1 )
150 acceleration = velDiff / time;
153 float newVelMag = 0.0f;
154 int currentInterpolation = interpolationTime;
155 if( !havePreviousAcceleration )
158 havePreviousAcceleration = true;
162 newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
165 if( velMag > Math::MACHINE_EPSILON_1 )
167 velMod = newVelMag / velMag;
169 gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
170 gestureOut.local.velocity = currentGesture.local.velocity * velMod;
171 screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
172 localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
173 screenVelocity = currentGesture.screen.velocity;
174 localVelocity = currentGesture.local.velocity;
175 previousAccel = acceleration;
178 // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
179 // add interpolated distance and position to current
180 // work out interpolated velocity
181 gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
182 gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
183 gestureOut.screen.displacement = screenDisplacement;
184 gestureOut.local.displacement = localDisplacement;
185 gestureOut.time += interpolationTime;
188 void PanGesture::SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
192 gestureOut.screen.position -= (gestureOut.screen.position - mLastGesture.screen.position) * 0.5f * (1.0f - mSmoothingAmount);
193 gestureOut.local.position -= (gestureOut.local.position - mLastGesture.local.position) * 0.5f * (1.0f - mSmoothingAmount);
194 // make current displacement relative to previous update-frame now.
195 gestureOut.screen.displacement = gestureOut.screen.position - mLastGesture.screen.position;
196 gestureOut.local.displacement = gestureOut.local.position - mLastGesture.local.position;
197 // calculate velocity relative to previous update-frame
198 unsigned int timeDiff( gestureOut.time - mLastGesture.time );
199 gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
200 gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
204 void PanGesture::SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
207 mPredictionHistory.push_back(gestureOut);
209 // now smooth current pan event
210 PanInfoHistoryConstIter endIter = mPredictionHistory.end() - 1;
211 PanInfoHistoryIter iter = mPredictionHistory.begin();
213 float distanceMod = 1.0f;
215 while( iter != endIter )
217 PanInfo currentGesture = *iter;
218 float newDistanceMod = currentGesture.screen.displacement.Length() / gestureOut.screen.displacement.Length();
219 distanceMod = ((distanceMod * weight) + (newDistanceMod * (1.0f - weight)));
223 gestureOut.screen.position -= gestureOut.screen.displacement;
224 gestureOut.local.position -= gestureOut.local.displacement;
225 gestureOut.screen.displacement *= distanceMod;
226 gestureOut.local.displacement *= distanceMod;
227 gestureOut.screen.position += gestureOut.screen.displacement;
228 gestureOut.local.position += gestureOut.local.displacement;
231 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
235 // clear current pan history
237 mPredictionHistory.clear();
240 // create an event for this frame
241 bool justStarted ( false );
242 bool justFinished ( false );
243 bool eventFound( false );
245 float acceleration = 0.0f;
247 // Not going through array from the beginning, using it as a circular buffer and only using unread
249 int eventsThisFrame = 0;
251 // create PanInfo to pass into prediction method
252 mLastEventGesture = mEventGesture;
253 mLastGesture = mLatestGesture;
254 // add new gestures and work out one full gesture for the frame
255 unsigned int previousReadPosition = 0;
256 while(mReadPosition != mWritePosition)
258 // Copy the gesture first
259 PanInfo currentGesture(mGestures[mReadPosition]);
263 mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) );
265 mEventGesture.local.position = currentGesture.local.position;
266 mEventGesture.local.velocity = currentGesture.local.velocity;
267 mEventGesture.screen.position = currentGesture.screen.position;
268 mEventGesture.screen.velocity = currentGesture.screen.velocity;
270 if(eventsThisFrame > 0)
272 acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
277 mEventGesture.local.displacement = currentGesture.local.displacement;
278 mEventGesture.screen.displacement = currentGesture.screen.displacement;
282 mEventGesture.local.displacement += currentGesture.local.displacement;
283 mEventGesture.screen.displacement += currentGesture.screen.displacement;
286 mEventGesture.time = currentGesture.time;
288 // add event to history
289 mPanHistory.push_back(currentGesture);
290 if( currentGesture.state == Gesture::Started )
293 // clear just finished as we have started new pan
294 justFinished = false;
296 justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled);
298 // Update our read position.
299 previousReadPosition = mReadPosition;
302 mReadPosition %= PAN_GESTURE_HISTORY;
304 mLatestGesture = mEventGesture;
306 mInGesture |= justStarted;
308 bool updateProperties = false;
314 mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, mEventGesture.screen.position, mEventGesture.screen.displacement, mEventGesture.screen.velocity, mEventGesture.state ) );
317 switch( mPredictionMode )
319 case PREDICTION_NONE:
321 updateProperties = eventFound;
322 // dont want event time
323 unsigned int time = mLastGesture.time;
324 mLastGesture = mLastEventGesture;
325 mLastGesture.time = time;
326 mLatestGesture.time = lastVSyncTime;
331 // Dynamically change the prediction amount according to the pan velocity acceleration.
334 if(eventsThisFrame <= 1)
336 acceleration = mEventGesture.screen.velocity.Length() - mLastEventGesture.screen.velocity.Length();
339 // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
340 if(fabsf(acceleration) > ACCELERATION_THRESHOLD)
342 mCurrentPredictionAmount += mPredictionAmountAdjustment * (acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f);
343 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
345 mCurrentPredictionAmount = 0;
351 mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
354 mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
356 // Calculate the delta of positions before the prediction
357 Vector2 deltaPosition = mLatestGesture.screen.position - mLastEventGesture.screen.position;
359 // Make latest gesture equal to current gesture before interpolation
360 PredictiveAlgorithm1(eventsThisFrame, mLatestGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
362 // Calculate the delta of positions after the prediction.
363 Vector2 deltaPredictedPosition = mLatestGesture.screen.position - mLastGesture.screen.position;
365 // If the change in the prediction has a different sign than the change in the actual position,
366 // there is overshot (i.e. the current prediction is too large). Return the previous prediction
367 // to give the user's finger a chance to catch up with where we have panned to.
368 bool overshotXAxis = false;
369 bool overshotYAxis = false;
370 if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 )
371 || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) )
373 overshotXAxis = true;
374 mLatestGesture.screen.position.x = mLastGesture.screen.position.x;
377 if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 )
378 || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) )
380 overshotYAxis = true;
381 mLatestGesture.screen.position.y = mLastGesture.screen.position.y;
384 // If there is overshot in one axis, reduce the possible overshot in the other axis,
385 // and reduce the prediction amount so that it doesn't overshoot as easily next time.
386 if(overshotXAxis || overshotYAxis)
388 mCurrentPredictionAmount -= mPredictionAmountAdjustment;
389 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
391 mCurrentPredictionAmount = 0;
393 mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
395 if(overshotXAxis && !overshotYAxis)
397 mLatestGesture.screen.position.y = (mLastGesture.screen.position.y + mLatestGesture.screen.position.y) * 0.5f;
400 if(overshotYAxis && !overshotXAxis)
402 mLatestGesture.screen.position.x = (mLastGesture.screen.position.x + mLatestGesture.screen.position.x) * 0.5f;
406 updateProperties = true;
411 switch( mSmoothingMode )
418 case SMOOTHING_LAST_VALUE:
420 SmoothingAlgorithm1(justStarted, mLatestGesture, lastVSyncTime);
425 if( updateProperties )
427 // only update properties if event received
428 // set latest gesture to raw pan info with unchanged time
429 mPanning.Set( mInGesture & !justFinished );
430 mScreenPosition.Set( mLatestGesture.screen.position );
431 mScreenDisplacement.Set( mLatestGesture.screen.displacement );
432 mScreenVelocity.Set( mLatestGesture.screen.velocity );
433 mLocalPosition.Set( mLatestGesture.local.position );
434 mLocalDisplacement.Set( mLatestGesture.local.displacement );
435 mLocalVelocity.Set( mLatestGesture.local.velocity );
440 mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( mLatestGesture.time, mLatestGesture.screen.position, mLatestGesture.screen.displacement, mLatestGesture.screen.velocity, mLatestGesture.state ) );
444 mInGesture &= ~justFinished;
446 if( mProfiling && justFinished )
448 mProfiling->PrintData();
449 mProfiling->ClearData();
452 return updateProperties;
455 const GesturePropertyBool& PanGesture::GetPanningProperty() const
460 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
462 return mScreenPosition;
465 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
467 return mScreenVelocity;
470 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
472 return mScreenDisplacement;
475 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
477 return mLocalPosition;
480 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
482 return mLocalDisplacement;
485 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
487 return mLocalVelocity;
490 void PanGesture::SetPredictionMode(PredictionMode mode)
492 mPredictionMode = mode;
495 void PanGesture::SetPredictionAmount(unsigned int amount)
497 mPredictionAmount = amount;
500 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
502 mMaxPredictionAmount = amount;
505 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
507 mMinPredictionAmount = amount;
510 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
512 mPredictionAmountAdjustment = amount;
515 void PanGesture::SetSmoothingMode(SmoothingMode mode)
517 mSmoothingMode = mode;
520 void PanGesture::SetSmoothingAmount(float amount)
522 mSmoothingAmount = amount;
525 void PanGesture::EnableProfiling()
529 mProfiling = new PanGestureProfiling();
533 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
535 mScreenPosition.Reset();
536 mScreenDisplacement.Reset();
537 mLocalPosition.Reset();
538 mLocalDisplacement.Reset();
542 PanGesture::PanGesture()
547 mPredictionMode(DEFAULT_PREDICTION_MODE),
548 mPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
549 mCurrentPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
550 mMaxPredictionAmount(DEFAULT_MAX_PREDICTION_INTERPOLATION),
551 mMinPredictionAmount(DEFAULT_MIN_PREDICTION_INTERPOLATION),
552 mPredictionAmountAdjustment(DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT),
553 mSmoothingMode(DEFAULT_SMOOTHING_MODE),
554 mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT),
559 } // namespace SceneGraph
561 } // namespace Internal