2 * Copyright (c) 2021 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/integration-api/debug.h>
26 #include <dali/internal/update/gestures/pan-gesture-profiling.h>
36 // TODO: Experimental - for changing in code only:
37 const bool TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION = false;
40 const int MAX_GESTURE_AGE = 200; ///< maximum age of a gesture before disallowing its use in algorithm TODO: Possibly make this configurable.
41 const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount.
42 const float OUTPUT_TIME_DIFFERENCE = (1000.0f / 60.0f); ///< This is used to optionally override actual times if they make results worse.
43 const float ACCELERATION_SMOOTHING = 0.44f; ///< Smoothes acceleration changes from one frame to another.
44 const float ACCELERATION_CAP = 0.0004f; ///< Limits acceleration changes from one frame to another.
46 // Defaults for Environment Variables:
49 const unsigned int DEFAULT_MAX_PREDICTION_AMOUNT = 32; ///< the upper bound of the range to clamp the prediction interpolation.
50 const unsigned int DEFAULT_MIN_PREDICTION_AMOUNT = 0; ///< the lower bound of the range to clamp the prediction interpolation.
51 const unsigned int DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT = 2; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes.
54 const bool DEFAULT_USE_ACTUAL_TIMES = false; ///< Disable to optionally override actual times if they make results worse.
55 const int DEFAULT_INTERPOLATION_TIME_RANGE = 255; ///< Time into past history (ms) to use points to interpolate the first point.
56 const bool DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED = false; ///< If enabled, prediction is done using velocity alone (no integration or acceleration).
57 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.
58 const int DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME = 42; ///< The target time in the past to generate the second interpolated point.
59 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.
60 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.
61 const int DEFAULT_MULTITAP_SMOOTHING_RANGE = 34; ///< The range in time (ms) of points in the history to smooth the final output against.
63 // Prediction Modes 1 & 2.
64 const unsigned int DEFAULT_PREDICTION_AMOUNT[2] = {5, 57}; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
65 const float DEFAULT_SMOOTHING_AMOUNT[2] = {0.25f, 0.23f}; ///< how much to smooth final result from last vsync time
67 } // unnamed namespace
69 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
70 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_2 + 1;
72 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
73 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_MULTI_TAP + 1;
75 PanGesture* PanGesture::New()
77 return new PanGesture();
80 PanGesture::~PanGesture()
85 void PanGesture::AddGesture(const Internal::PanGesture& gesture)
87 Dali::Mutex::ScopedLock lock(mMutex);
88 mGestures[mWritePosition] = gesture;
90 // Update our write position.
92 mWritePosition %= PAN_GESTURE_HISTORY;
95 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
97 PanInfoHistoryConstIter endIter = panHistory.end();
98 PanInfoHistoryIter iter = panHistory.begin();
99 while(iter != endIter && panHistory.size() > minEvents)
101 PanInfo currentGesture = *iter;
102 if(currentTime < currentGesture.time + maxAge)
106 iter = panHistory.erase(iter);
107 endIter = panHistory.end();
110 // dont want more than 5 previous predictions for smoothing
111 iter = mPredictionHistory.begin();
112 while(mPredictionHistory.size() > 1 && iter != mPredictionHistory.end())
114 iter = mPredictionHistory.erase(iter);
118 void PanGesture::PredictionMode1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
120 RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
121 size_t panHistorySize = panHistory.size();
122 if(panHistorySize == 0)
124 // cant do any prediction without a history
128 PanInfoHistoryConstIter endIter = panHistory.end();
129 PanInfoHistoryIter iter = panHistory.begin();
130 Vector2 screenVelocity = gestureOut.screen.velocity;
131 Vector2 localVelocity = gestureOut.local.velocity;
132 Vector2 screenDisplacement = gestureOut.screen.displacement;
133 Vector2 localDisplacement = gestureOut.local.displacement;
135 bool havePreviousAcceleration = false;
136 bool previousVelocity = false;
137 float previousAccel = 0.0f;
138 unsigned int lastTime(0);
140 unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
142 if(interpolationTime > gestureOut.time) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
144 interpolationTime -= gestureOut.time;
148 interpolationTime = 0u;
151 while(iter != endIter)
153 PanInfo currentGesture = *iter;
154 if(!previousVelocity)
156 // not yet set a previous velocity
157 screenVelocity = currentGesture.screen.velocity;
158 previousVelocity = true;
159 lastTime = currentGesture.time;
163 float previousValueWeight = (static_cast<float>(MAX_GESTURE_AGE) - static_cast<float>(lastVSyncTime - lastTime)) / static_cast<float>(MAX_GESTURE_AGE);
164 float velMag = currentGesture.screen.velocity.Length();
165 float velDiff = velMag - screenVelocity.Length();
166 float acceleration = 0.0f;
169 if(currentGesture.time > lastTime) // Guard against invalid timestamps
171 time = static_cast<float>(currentGesture.time - lastTime);
173 if(time > Math::MACHINE_EPSILON_1)
175 acceleration = velDiff / time;
178 float newVelMag = 0.0f;
179 int currentInterpolation = interpolationTime;
180 if(!havePreviousAcceleration)
183 havePreviousAcceleration = true;
187 newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * static_cast<float>(currentInterpolation));
190 if(velMag > Math::MACHINE_EPSILON_1)
192 velMod = newVelMag / velMag;
194 gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
195 gestureOut.local.velocity = currentGesture.local.velocity * velMod;
196 screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * static_cast<float>(interpolationTime));
197 localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * static_cast<float>(interpolationTime));
198 screenVelocity = currentGesture.screen.velocity;
199 localVelocity = currentGesture.local.velocity;
200 previousAccel = acceleration;
203 // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
204 // add interpolated distance and position to current
205 // work out interpolated velocity
206 gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
207 gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
208 gestureOut.screen.displacement = screenDisplacement;
209 gestureOut.local.displacement = localDisplacement;
210 gestureOut.time += interpolationTime;
213 void PanGesture::BlendPoints(PanInfo& gesture, PanInfo& lastGesture, float blendValue)
215 gesture.screen.position -= (gesture.screen.position - lastGesture.screen.position) * 0.5f * (1.0f - blendValue);
216 gesture.local.position -= (gesture.local.position - lastGesture.local.position) * 0.5f * (1.0f - blendValue);
217 // Make current displacement relative to previous update-frame now.
218 gesture.screen.displacement = gesture.screen.position - lastGesture.screen.position;
219 gesture.local.displacement = gesture.local.position - lastGesture.local.position;
220 // Calculate velocity relative to previous update-frame
221 float timeDifference = static_cast<float>(gesture.time - lastGesture.time);
222 gesture.screen.velocity = gesture.screen.displacement / timeDifference;
223 gesture.local.velocity = gesture.local.displacement / timeDifference;
226 bool PanGesture::ReadGestures(FrameGestureInfo& info, unsigned int currentTimestamp)
228 unsigned int previousReadPosition = 0;
229 bool eventFound = false;
230 info.frameGesture = mLastUnmodifiedGesture;
232 while(mReadPosition != mWritePosition)
234 // Copy the gesture first
235 PanInfo currentGesture(mGestures[mReadPosition]);
239 mProfiling->mRawData.push_back(PanGestureProfiling::Position(currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state));
241 info.frameGesture.local.position = currentGesture.local.position;
242 info.frameGesture.local.velocity = currentGesture.local.velocity;
243 info.frameGesture.screen.position = currentGesture.screen.position;
244 info.frameGesture.screen.velocity = currentGesture.screen.velocity;
246 if(info.eventsThisFrame > 0)
248 info.acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
253 info.frameGesture.local.displacement = currentGesture.local.displacement;
254 info.frameGesture.screen.displacement = currentGesture.screen.displacement;
259 info.frameGesture.local.displacement += currentGesture.local.displacement;
260 info.frameGesture.screen.displacement += currentGesture.screen.displacement;
262 info.frameGesture.time = currentGesture.time;
264 // add event to history
265 mPanHistory.push_back(currentGesture);
266 if(currentGesture.state == GestureState::STARTED)
268 info.justStarted = true;
269 // clear just finished as we have started new pan
270 info.justFinished = false;
272 info.justFinished |= (currentGesture.state == GestureState::FINISHED || currentGesture.state == GestureState::CANCELLED);
274 // Update our read position.
275 previousReadPosition = mReadPosition;
276 ++info.eventsThisFrame;
278 mReadPosition %= PAN_GESTURE_HISTORY;
280 // This code does not determine if the data will be used.
284 bool PanGesture::ReadAndResampleGestures(FrameGestureInfo& info, unsigned int currentTimestamp)
286 PanInfo lastReadGesture;
287 Dali::Mutex::ScopedLock lock(mMutex);
288 while(mReadPosition != mWritePosition)
290 // Copy the gesture first
291 lastReadGesture = mGestures[mReadPosition];
294 mProfiling->mRawData.push_back(PanGestureProfiling::Position(lastReadGesture.time, lastReadGesture.screen.position, lastReadGesture.screen.displacement, lastReadGesture.screen.velocity, lastReadGesture.state));
297 info.frameGesture.screen.position += lastReadGesture.screen.position;
298 info.frameGesture.local.position += lastReadGesture.local.position;
299 info.frameGesture.screen.velocity += lastReadGesture.screen.velocity;
300 info.frameGesture.local.velocity += lastReadGesture.local.velocity;
302 if(lastReadGesture.state == GestureState::STARTED)
304 // Clear just finished as we have started new pan.
305 info.justFinished = false;
306 info.justStarted = true;
310 info.justFinished |= (lastReadGesture.state == GestureState::FINISHED || lastReadGesture.state == GestureState::CANCELLED);
313 // Add event to history
314 mPanHistory.push_back(lastReadGesture);
316 // Update our read position.
317 ++info.eventsThisFrame;
319 mReadPosition %= PAN_GESTURE_HISTORY;
322 bool updateProperties = false;
323 if(info.eventsThisFrame > 0)
325 // Some events were read this frame.
326 mTargetGesture = lastReadGesture;
328 if(info.eventsThisFrame > 1)
330 const float eventsThisFrame = static_cast<float>(info.eventsThisFrame);
331 info.frameGesture.screen.position /= eventsThisFrame;
332 info.frameGesture.local.position /= eventsThisFrame;
333 info.frameGesture.screen.velocity /= eventsThisFrame;
334 info.frameGesture.local.velocity /= eventsThisFrame;
336 info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position;
337 info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position;
343 info.frameGesture.screen.displacement = lastReadGesture.screen.displacement;
344 info.frameGesture.local.displacement = lastReadGesture.local.displacement;
347 info.frameGesture.time = currentTimestamp;
349 updateProperties = true;
353 // 0 Events this frame.
356 mNotAtTarget = false;
357 info.frameGesture = mTargetGesture;
358 updateProperties = true;
362 info.frameGesture = mLastGesture;
366 return updateProperties;
369 bool PanGesture::UpdateProperties(unsigned int lastVSyncTime, unsigned int nextVSyncTime)
371 if(mPredictionMode == PREDICTION_2)
373 // TODO: Have the two prediction modes share more behavior so some parts of mode 2 can
374 // be used with mode 1 etc. Needs code moving and more importantly testing.
375 return NewAlgorithm(lastVSyncTime, nextVSyncTime);
380 // clear current pan history
382 mPredictionHistory.clear();
385 FrameGestureInfo frameInfo;
386 bool updateProperties = false;
389 // If we are using a form of prediction, read all the input as-is.
390 if(mPredictionMode != PREDICTION_NONE)
392 // Read input required for prediction algorithms.
393 updateProperties = ReadGestures(frameInfo, lastVSyncTime);
397 // Read and resample input.
398 updateProperties = ReadAndResampleGestures(frameInfo, lastVSyncTime);
401 PanInfo frameGesture = frameInfo.frameGesture;
402 PanInfo unmodifiedGesture = frameGesture;
404 // Process input data.
405 mInGesture |= frameInfo.justStarted;
411 mProfiling->mLatestData.push_back(PanGestureProfiling::Position(lastVSyncTime, frameGesture.screen.position, frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state));
414 // Perform prediction.
415 if(mPredictionMode == PREDICTION_1)
417 // Dynamically change the prediction amount according to the pan velocity acceleration.
418 if(!frameInfo.justStarted)
420 if(frameInfo.eventsThisFrame <= 1)
422 frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length();
425 // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
426 if(fabsf(frameInfo.acceleration) > ACCELERATION_THRESHOLD)
428 mCurrentPredictionAmount += static_cast<unsigned int>(static_cast<float>(mPredictionAmountAdjustment) * (frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f));
429 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
431 mCurrentPredictionAmount = 0;
437 if(!mPredictionAmountOverridden)
439 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
440 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[0];
442 mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
445 mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
447 // Calculate the delta of positions before the prediction
448 Vector2 deltaPosition = frameGesture.screen.position - mLastUnmodifiedGesture.screen.position;
450 // Make latest gesture equal to current gesture before interpolation
451 PredictionMode1(frameInfo.eventsThisFrame, frameGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
453 // Calculate the delta of positions after the prediction.
454 Vector2 deltaPredictedPosition = frameGesture.screen.position - mLastGesture.screen.position;
456 // If the change in the prediction has a different sign than the change in the actual position,
457 // there is overshot (i.e. the current prediction is too large). Return the previous prediction
458 // to give the user's finger a chance to catch up with where we have panned to.
459 bool overshotXAxis = false;
460 bool overshotYAxis = false;
461 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))
463 overshotXAxis = true;
464 frameGesture.screen.position.x = mLastGesture.screen.position.x;
467 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))
469 overshotYAxis = true;
470 frameGesture.screen.position.y = mLastGesture.screen.position.y;
473 // If there is overshot in one axis, reduce the possible overshot in the other axis,
474 // and reduce the prediction amount so that it doesn't overshoot as easily next time.
475 if(overshotXAxis || overshotYAxis)
477 mCurrentPredictionAmount -= mPredictionAmountAdjustment;
478 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
480 mCurrentPredictionAmount = 0;
482 mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
484 if(overshotXAxis && !overshotYAxis)
486 frameGesture.screen.position.y = (mLastGesture.screen.position.y + frameGesture.screen.position.y) * 0.5f;
489 if(overshotYAxis && !overshotXAxis)
491 frameGesture.screen.position.x = (mLastGesture.screen.position.x + frameGesture.screen.position.x) * 0.5f;
495 updateProperties = true;
498 // Perform smoothing.
499 switch(mSmoothingMode)
502 case SMOOTHING_MULTI_TAP:
505 // TODO: Old algorithm to be able to use multitap smoothing.
508 case SMOOTHING_LAST_VALUE:
510 if(!frameInfo.justStarted)
512 if(!mSmoothingAmountOverridden)
514 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
515 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[0];
517 BlendPoints(frameGesture, mLastGesture, mSmoothingAmount);
525 // only update properties if event received
526 // set latest gesture to raw pan info with unchanged time
527 mPanning.Set(mInGesture & !frameInfo.justFinished);
528 mScreenPosition.Set(frameGesture.screen.position);
529 mScreenDisplacement.Set(frameGesture.screen.displacement);
530 mScreenVelocity.Set(frameGesture.screen.velocity);
531 mLocalPosition.Set(frameGesture.local.position);
532 mLocalDisplacement.Set(frameGesture.local.displacement);
533 mLocalVelocity.Set(frameGesture.local.velocity);
538 mProfiling->mAveragedData.push_back(PanGestureProfiling::Position(frameGesture.time, frameGesture.screen.position, frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state));
542 mLastGesture = frameGesture;
543 mLastUnmodifiedGesture = unmodifiedGesture;
545 mInGesture = mInGesture && !frameInfo.justFinished;
546 if(mProfiling && frameInfo.justFinished)
548 mProfiling->PrintData();
549 mProfiling->ClearData();
552 return updateProperties;
555 const GesturePropertyBool& PanGesture::GetPanningProperty() const
560 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
562 return mScreenPosition;
565 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
567 return mScreenVelocity;
570 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
572 return mScreenDisplacement;
575 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
577 return mLocalPosition;
580 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
582 return mLocalDisplacement;
585 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
587 return mLocalVelocity;
590 void PanGesture::SetPredictionMode(PredictionMode mode)
592 mPredictionMode = mode;
595 void PanGesture::SetPredictionAmount(unsigned int amount)
597 mPredictionAmount = amount;
598 mPredictionAmountOverridden = true;
601 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
603 mMaxPredictionAmount = amount;
606 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
608 mMinPredictionAmount = amount;
611 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
613 mPredictionAmountAdjustment = amount;
616 void PanGesture::SetSmoothingMode(SmoothingMode mode)
618 mSmoothingMode = mode;
621 void PanGesture::SetSmoothingAmount(float amount)
623 mSmoothingAmount = amount;
624 mSmoothingAmountOverridden = true;
627 void PanGesture::SetUseActualTimes(bool value)
629 mUseActualTimes = value;
632 void PanGesture::SetInterpolationTimeRange(int value)
634 mInterpolationTimeRange = value;
637 void PanGesture::SetScalarOnlyPredictionEnabled(bool value)
639 mScalarOnlyPredictionEnabled = value;
642 void PanGesture::SetTwoPointPredictionEnabled(bool value)
644 mTwoPointPredictionEnabled = value;
647 void PanGesture::SetTwoPointInterpolatePastTime(int value)
649 mTwoPointPastInterpolateTime = value;
652 void PanGesture::SetTwoPointVelocityBias(float value)
654 mTwoPointVelocityBias = value;
657 void PanGesture::SetTwoPointAccelerationBias(float value)
659 mTwoPointAccelerationBias = value;
662 void PanGesture::SetMultitapSmoothingRange(int value)
664 mMultiTapSmoothingRange = value;
667 void PanGesture::EnableProfiling()
671 mProfiling = new PanGestureProfiling();
675 void PanGesture::ResetDefaultProperties(BufferIndex updateBufferIndex)
677 mScreenPosition.Reset();
678 mScreenDisplacement.Reset();
679 mLocalPosition.Reset();
680 mLocalDisplacement.Reset();
684 PanGesture::PanGesture()
691 mPredictionAmountOverridden(false),
692 mSmoothingAmountOverridden(false),
695 // Set environment variable defaults:
696 mPredictionMode(DEFAULT_PREDICTION_MODE),
697 mPredictionAmount(DEFAULT_PREDICTION_AMOUNT[0]),
698 mCurrentPredictionAmount(DEFAULT_PREDICTION_AMOUNT[0]),
699 mMaxPredictionAmount(DEFAULT_MAX_PREDICTION_AMOUNT),
700 mMinPredictionAmount(DEFAULT_MIN_PREDICTION_AMOUNT),
701 mPredictionAmountAdjustment(DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT),
702 mSmoothingMode(DEFAULT_SMOOTHING_MODE),
703 mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT[0]),
704 mUseActualTimes(DEFAULT_USE_ACTUAL_TIMES),
705 mInterpolationTimeRange(DEFAULT_INTERPOLATION_TIME_RANGE),
706 mScalarOnlyPredictionEnabled(DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED),
707 mTwoPointPredictionEnabled(DEFAULT_TWO_POINT_PREDICTION_ENABLED),
708 mTwoPointPastInterpolateTime(DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME),
709 mTwoPointVelocityBias(DEFAULT_TWO_POINT_VELOCITY_BIAS),
710 mTwoPointAccelerationBias(DEFAULT_TWO_POINT_ACCELERATION_BIAS),
711 mMultiTapSmoothingRange(DEFAULT_MULTITAP_SMOOTHING_RANGE)
715 // Prediction mode 2 related code and functions follow:
717 unsigned int PanGesture::ReadFrameEvents()
719 unsigned int eventsThisFrame;
720 // Copy the events into a linear buffer while holding the mutex.
721 // This is so the lock is not held while any processing is done.
722 Dali::Mutex::ScopedLock lock(mMutex);
723 for(eventsThisFrame = 0; mReadPosition != mWritePosition; ++eventsThisFrame)
725 mReadGestures[eventsThisFrame] = mGestures[mReadPosition];
727 mReadPosition %= PAN_GESTURE_HISTORY;
729 return eventsThisFrame;
732 // TODO: eventsThisFrame parameter can be removed if we use a smarter container.
733 bool PanGesture::InputRateConversion(PanInfo& rateConvertedGesture, unsigned int eventsThisFrame, unsigned int currentFrameTime, unsigned int lastFrameTime, bool& justStarted, bool& justFinished)
735 // TODO: Lots of variables on the stack. Needs optimizing.
737 PanInfo firstReadGesture;
738 unsigned int eventsKeptThisFrame = 0;
740 for(unsigned int readPosition = 0; readPosition < eventsThisFrame; ++readPosition)
742 // Copy the gesture first
743 readGesture = mReadGestures[readPosition];
747 mProfiling->mRawData.push_back(PanGestureProfiling::Position(readGesture.time, readGesture.screen.position, readGesture.screen.displacement, readGesture.screen.velocity, readGesture.state));
750 if(readGesture.state == GestureState::STARTED)
754 mPredictionHistory.clear();
755 mLastAcceleration.local = Vector2::ZERO;
756 mLastAcceleration.screen = Vector2::ZERO;
757 mLastInterpolatedAcceleration.local = Vector2::ZERO;
758 mLastInterpolatedAcceleration.screen = Vector2::ZERO;
759 mLastInitialAcceleration.local = Vector2::ZERO;
760 mLastInitialAcceleration.screen = Vector2::ZERO;
762 mLastGesture = startInfo;
763 mLastSecondInterpolatedPoint = startInfo;
764 mLastPredictedPoint = startInfo;
765 mLastFrameReadGesture = startInfo;
766 rateConvertedGesture = startInfo;
767 firstReadGesture = readGesture;
768 eventsKeptThisFrame = 0;
769 mNotAtTarget = false;
770 justFinished = false;
774 if(!mPredictionAmountOverridden)
776 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
777 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[1];
779 mCurrentPredictionAmount = mPredictionAmount;
783 justFinished |= (readGesture.state == GestureState::FINISHED || readGesture.state == GestureState::CANCELLED);
786 rateConvertedGesture.screen.position += readGesture.screen.position;
787 rateConvertedGesture.local.position += readGesture.local.position;
788 rateConvertedGesture.screen.velocity += readGesture.screen.velocity;
789 rateConvertedGesture.local.velocity += readGesture.local.velocity;
790 rateConvertedGesture.screen.displacement += readGesture.screen.displacement;
791 rateConvertedGesture.local.displacement += readGesture.local.displacement;
793 ++eventsKeptThisFrame;
796 bool storeGesture = false;
797 if(eventsKeptThisFrame > 0)
799 // Some events were read this frame.
800 if(eventsKeptThisFrame > 1)
802 const float eventDivisor = static_cast<float>(eventsKeptThisFrame);
803 rateConvertedGesture.screen.position /= eventDivisor;
804 rateConvertedGesture.local.position /= eventDivisor;
805 rateConvertedGesture.screen.velocity /= eventDivisor;
806 rateConvertedGesture.local.velocity /= eventDivisor;
807 rateConvertedGesture.screen.displacement /= eventDivisor;
808 rateConvertedGesture.local.displacement /= eventDivisor;
810 mTargetGesture = readGesture;
815 mNotAtTarget = false;
818 rateConvertedGesture.time = currentFrameTime;
823 // We did not get any event this frame.
824 // If we just started (or aren't in a gesture), exit.
825 if(!mInGesture || justStarted)
827 // We cannot guess what the event could be as we have no other events to base the guess from.
831 // As we are currently in a gesture, we can estimate an event.
832 readGesture = mLastFrameReadGesture;
833 readGesture.time = currentFrameTime;
835 // Take the last event, halve the acceleration, and use that.
836 const float accelerationDegrade = 2.0f;
837 Vector2 degradedAccelerationLocal(mLastAcceleration.local /= accelerationDegrade);
838 Vector2 degradedAccelerationScreen(mLastAcceleration.screen /= accelerationDegrade);
840 float outputTimeGranularity(GetDivisibleTimeDifference(currentFrameTime, lastFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
842 readGesture.local.velocity = degradedAccelerationLocal * outputTimeGranularity;
843 readGesture.local.displacement = readGesture.local.velocity * outputTimeGranularity;
844 readGesture.local.position = mLastFrameReadGesture.local.position + readGesture.local.displacement;
845 readGesture.screen.velocity = degradedAccelerationScreen * outputTimeGranularity;
846 readGesture.screen.displacement = readGesture.screen.velocity * outputTimeGranularity;
847 readGesture.screen.position = mLastFrameReadGesture.screen.position + readGesture.screen.displacement;
849 rateConvertedGesture = readGesture;
850 eventsKeptThisFrame = 1;
854 if(eventsKeptThisFrame > 0)
856 // Store last read gesture.
857 readGesture.time = currentFrameTime;
858 mLastFrameReadGesture = readGesture;
860 if(eventsKeptThisFrame > 2)
862 DALI_LOG_WARNING("Got events this frame:%d (more than 2 will compromise result)\n", eventsKeptThisFrame);
868 // Store final converted result.
869 mPanHistory.push_back(rateConvertedGesture);
874 bool PanGesture::InterpolatePoint(PanInfoHistory& history, unsigned int currentTime, unsigned int targetTime, unsigned int range, PanInfo& outPoint, RelativeVectors& acceleration, int outputTimeGranularity, bool eraseUnused)
876 unsigned int maxHistoryTime = targetTime - range;
877 unsigned int tapsUsed = 0;
878 outPoint.time = targetTime;
879 float divisor = 0.0f;
880 float accelerationDivisor = 0.0f;
881 PanInfoHistoryIter historyBegin = history.begin();
882 PanInfoHistoryIter lastIt = history.end();
883 bool pointGenerated = false;
884 bool havePreviousPoint = false;
885 RelativeVectors newAcceleration;
887 // Iterate through point history to perform interpolation.
888 for(PanInfoHistoryIter it = historyBegin; it != history.end();)
890 unsigned int gestureTime = it->time;
892 if(gestureTime < maxHistoryTime)
894 // Too far in the past, discard.
895 // Clean history as we go (if requested).
898 it = history.erase(it);
908 float timeDelta(static_cast<float>(abs(int(targetTime - gestureTime))));
909 // Handle low time deltas.
915 outPoint.local.position += it->local.position / timeDelta;
916 outPoint.screen.position += it->screen.position / timeDelta;
917 outPoint.local.velocity += it->local.velocity / timeDelta;
918 outPoint.screen.velocity += it->screen.velocity / timeDelta;
919 outPoint.local.displacement += it->local.displacement / timeDelta;
920 outPoint.screen.displacement += it->screen.displacement / timeDelta;
922 divisor += 1.0f / timeDelta;
924 // Acceleration requires a previous point.
925 if(havePreviousPoint)
927 // Time delta of input.
928 float timeDifference(GetDivisibleTimeDifference(it->time, lastIt->time, 1.0f, OUTPUT_TIME_DIFFERENCE));
930 newAcceleration.local += ((it->local.velocity - lastIt->local.velocity) / timeDifference) / timeDelta;
931 newAcceleration.screen += ((it->screen.velocity - lastIt->screen.velocity) / timeDifference) / timeDelta;
933 accelerationDivisor += 1.0f / timeDelta;
937 havePreviousPoint = true;
946 // Divide results by their respective divisors.
951 outPoint.local.position /= divisor;
952 outPoint.screen.position /= divisor;
953 outPoint.local.velocity /= divisor;
954 outPoint.screen.velocity /= divisor;
955 outPoint.local.displacement /= divisor;
956 outPoint.screen.displacement /= divisor;
961 if(accelerationDivisor > 0.0f)
963 newAcceleration.local /= accelerationDivisor;
964 newAcceleration.screen /= accelerationDivisor;
967 float accelerationSmoothing(ACCELERATION_SMOOTHING);
968 newAcceleration.local = (acceleration.local * accelerationSmoothing) + (newAcceleration.local * (1.0f - accelerationSmoothing));
969 newAcceleration.screen = (acceleration.screen * accelerationSmoothing) + (newAcceleration.screen * (1.0f - accelerationSmoothing));
973 // If we just started, last velocity was 0. So difference of zero to current velocity over time gives acceleration of the first point.
974 newAcceleration.local = outPoint.local.velocity / static_cast<float>(outputTimeGranularity);
975 newAcceleration.screen = outPoint.screen.velocity / static_cast<float>(outputTimeGranularity);
977 pointGenerated = true;
980 acceleration.local = newAcceleration.local;
981 acceleration.screen = newAcceleration.screen;
982 return pointGenerated;
985 float PanGesture::GetDivisibleTimeDifference(int timeA, int timeB, float minimumDelta, float overrideDifference)
987 float timeDifference(overrideDifference);
990 timeDifference = static_cast<float>(abs(timeA - timeB));
991 if(timeDifference < minimumDelta)
993 timeDifference = minimumDelta;
996 return timeDifference;
999 void PanGesture::LimitAccelerationChange(RelativeVectors& currentAcceleration, RelativeVectors& lastAcceleration, float changeLimit)
1001 // We don't use the float parameter version of clamp here, as that will create the capping vectors twice in total.
1002 Vector2 capMinimum(-changeLimit, -changeLimit);
1003 Vector2 capMaximum(changeLimit, changeLimit);
1004 Vector2 accelerationDeltaLocal(currentAcceleration.local - lastAcceleration.local);
1005 Vector2 accelerationDeltaScreen(currentAcceleration.screen - lastAcceleration.screen);
1006 accelerationDeltaLocal.Clamp(capMinimum, capMaximum);
1007 accelerationDeltaScreen.Clamp(capMinimum, capMaximum);
1008 currentAcceleration.local = lastAcceleration.local + accelerationDeltaLocal;
1009 currentAcceleration.screen = lastAcceleration.screen + accelerationDeltaScreen;
1012 void PanGesture::PredictionMode2(PanInfo& startPoint, RelativeVectors& accelerationToUse, PanInfo& predictedPoint, unsigned int currentFrameTime, unsigned int previousFrameTime, bool noPreviousData)
1014 // Do the prediction (based on mode).
1015 if(mScalarOnlyPredictionEnabled)
1017 // We are doing scalar based prediction.
1018 // This divisor is to help tuning by giving the scalar only result
1019 // a similar prediction amount to the integrated result.
1020 float scalarVelocityMultiplier = static_cast<float>(mCurrentPredictionAmount) / 1.364f;
1021 predictedPoint.local.position = startPoint.local.position + (startPoint.local.velocity * scalarVelocityMultiplier);
1022 predictedPoint.screen.position = startPoint.screen.position + (startPoint.screen.velocity * scalarVelocityMultiplier);
1026 // We are doing integration based prediction.
1027 float predictionDelta = static_cast<float>(mCurrentPredictionAmount);
1029 predictedPoint.local.position = startPoint.local.position + (startPoint.local.velocity * predictionDelta) +
1030 (accelerationToUse.local * (predictionDelta * predictionDelta * 0.5f));
1031 predictedPoint.screen.position = startPoint.screen.position + (startPoint.screen.velocity * predictionDelta) +
1032 (accelerationToUse.screen * (predictionDelta * predictionDelta * 0.5f));
1035 // Calculate remaining gesture data from the result.
1036 float timeDifference(GetDivisibleTimeDifference(currentFrameTime, previousFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1039 predictedPoint.local.displacement = predictedPoint.local.position - startPoint.local.position;
1040 predictedPoint.screen.displacement = predictedPoint.screen.position - startPoint.screen.position;
1044 predictedPoint.local.displacement = predictedPoint.local.position - mLastPredictedPoint.local.position;
1045 predictedPoint.screen.displacement = predictedPoint.screen.position - mLastPredictedPoint.screen.position;
1047 predictedPoint.local.velocity = predictedPoint.local.displacement / timeDifference;
1048 predictedPoint.screen.velocity = predictedPoint.screen.displacement / timeDifference;
1050 // TODO: Experimental - not used at run time. Left in code for reference only.
1051 if(TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION)
1053 // Overshoot protection
1056 if((mLastPredictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0) || (mLastPredictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0))
1058 predictedPoint.local.position.x = mLastPredictedPoint.local.position.x;
1059 predictedPoint.screen.position.x = mLastPredictedPoint.screen.position.x;
1060 mPredictionHistory.clear();
1062 if((mLastPredictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0) || (mLastPredictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0))
1064 predictedPoint.local.position.y = mLastPredictedPoint.local.position.y;
1065 predictedPoint.screen.position.y = mLastPredictedPoint.screen.position.y;
1066 mPredictionHistory.clear();
1071 predictedPoint.time = currentFrameTime;
1072 mLastPredictedPoint = predictedPoint;
1075 // TODO: This needs a better name! It is called this instead of prediction mode 2 because:
1076 // 1) It is the entire workflow, not just prediction.
1077 // 2) To make it less confusing as there is a function that does prediction alone called PerformPredictionMode2.
1078 // 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.
1079 // At the moment, the differences between the inputs & outputs of these different functions prevent that, but this can be resolved.
1080 bool PanGesture::NewAlgorithm(unsigned int lastVSyncTime, unsigned int nextVSyncTime)
1084 // clear current pan history
1085 mPanHistory.clear();
1086 mPredictionHistory.clear();
1089 /*#########################################################################################
1090 #### Read in all gestures received this frame first (holding a lock for a short time)
1091 #########################################################################################*/
1093 unsigned int eventsThisFrame = ReadFrameEvents();
1095 /*#########################################################################################
1096 #### Perform input rate-conversion on all gestures received this frame.
1097 #### This also populates the pan history.
1098 #########################################################################################*/
1100 bool justStarted = false;
1101 bool justFinished = false;
1102 PanInfo rateConvertedGesture;
1103 if(!InputRateConversion(rateConvertedGesture, eventsThisFrame, nextVSyncTime, lastVSyncTime, justStarted, justFinished))
1105 // There's nothing we can do with the input, exit.
1109 /*#########################################################################################
1110 #### If we are in gesture, Get first interpolated point with: target time = current time
1111 #########################################################################################*/
1113 bool performUpdate = false;
1114 RelativeVectors currentAcceleration;
1115 currentAcceleration.local = mLastInitialAcceleration.local;
1116 currentAcceleration.screen = mLastInitialAcceleration.screen;
1118 if(mInGesture || justStarted)
1120 // Get first interpolated point.
1121 // TODO: Erase time should be maximum of both interpolated point ranges in past.
1122 PanInfo targetPoint;
1123 float outputTimeGranularity(GetDivisibleTimeDifference(nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1124 bool pointGenerated = InterpolatePoint(mPanHistory, nextVSyncTime, nextVSyncTime, mInterpolationTimeRange, targetPoint, currentAcceleration, static_cast<int>(outputTimeGranularity), true); // truncated
1127 mLastInitialAcceleration.local = currentAcceleration.local;
1128 mLastInitialAcceleration.screen = currentAcceleration.screen;
1129 performUpdate = true;
1133 targetPoint = rateConvertedGesture;
1134 currentAcceleration.local = mLastInitialAcceleration.local;
1135 currentAcceleration.screen = mLastInitialAcceleration.screen;
1136 // TODO: Potentially do something to substitute lack of generated point (and perform update).
1139 /*#########################################################################################
1140 #### Limit the change of acceleration of the first interpolated point since last time
1141 #########################################################################################*/
1145 LimitAccelerationChange(currentAcceleration, mLastAcceleration, ACCELERATION_CAP);
1147 mLastAcceleration.local = currentAcceleration.local;
1148 mLastAcceleration.screen = currentAcceleration.screen;
1150 /*#########################################################################################
1151 #### Get second interpolated point, and blend the resultant velocity and acceleration (optional)
1152 #########################################################################################*/
1155 RelativeVectors interpolatedAcceleration;
1156 if(mTwoPointPredictionEnabled)
1158 // Get second interpolated point with target time = current time - past interpolate time.
1159 unsigned int pastInterpolateTime = nextVSyncTime - mTwoPointPastInterpolateTime;
1161 RelativeVectors interpolatedAcceleration;
1162 interpolatedAcceleration.local = mLastInterpolatedAcceleration.local;
1163 interpolatedAcceleration.screen = mLastInterpolatedAcceleration.screen;
1164 if(!InterpolatePoint(mPanHistory, nextVSyncTime, pastInterpolateTime, mTwoPointPastInterpolateTime, outPoint, interpolatedAcceleration, static_cast<int>(outputTimeGranularity), false)) // truncated
1168 outPoint = targetPoint;
1172 outPoint = mLastSecondInterpolatedPoint;
1175 mLastInterpolatedAcceleration.local = interpolatedAcceleration.local;
1176 mLastInterpolatedAcceleration.screen = interpolatedAcceleration.screen;
1177 mLastSecondInterpolatedPoint = outPoint;
1179 // Combine the first interpolated point and the second interpolated point.
1180 // by mixing them with the configured amount. This is done for acceleration and velocity.
1181 // It could be optionally done for position too, but this typically is worse as it means we have to predict further ahead.
1182 float currentVelocityMultiplier(1.0f - mTwoPointVelocityBias);
1183 float lastVelocityMultiplier(mTwoPointVelocityBias);
1184 targetPoint.local.velocity = (outPoint.local.velocity * lastVelocityMultiplier) + (targetPoint.local.velocity * currentVelocityMultiplier);
1185 targetPoint.screen.velocity = (outPoint.screen.velocity * lastVelocityMultiplier) + (targetPoint.screen.velocity * currentVelocityMultiplier);
1186 float currentAccelerationMultiplier(1.0f - mTwoPointAccelerationBias);
1187 float lastAccelerationMultiplier(mTwoPointAccelerationBias);
1188 currentAcceleration.local = (interpolatedAcceleration.local * lastAccelerationMultiplier) + (currentAcceleration.local * currentAccelerationMultiplier);
1189 currentAcceleration.screen = (interpolatedAcceleration.screen * lastAccelerationMultiplier) + (currentAcceleration.screen * currentAccelerationMultiplier);
1192 /*#########################################################################################
1193 #### Perform prediction
1194 #########################################################################################*/
1196 PanInfo predictedPoint;
1197 PredictionMode2(targetPoint, currentAcceleration, predictedPoint, nextVSyncTime, lastVSyncTime, justStarted);
1198 targetPoint = predictedPoint;
1200 /*#########################################################################################
1202 #########################################################################################*/
1204 // If we are using multi-tap smoothing, keep a history of predicted results.
1205 if(mSmoothingMode == SMOOTHING_MULTI_TAP)
1207 mPredictionHistory.push_back(targetPoint);
1212 float outputTimeGranularity(GetDivisibleTimeDifference(nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1213 if(mSmoothingMode == SMOOTHING_MULTI_TAP)
1215 // Perform Multi-tap Smoothing.
1216 RelativeVectors blank;
1217 InterpolatePoint(mPredictionHistory, nextVSyncTime, nextVSyncTime, mMultiTapSmoothingRange, targetPoint, blank, static_cast<int>(outputTimeGranularity), true); // truncated
1221 // Perform Single-tap Smoothing.
1222 if(!mSmoothingAmountOverridden)
1224 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
1225 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[1];
1227 BlendPoints(targetPoint, mLastGesture, mSmoothingAmount);
1230 /*#########################################################################################
1231 #### Finalize other point data (from position)
1232 #########################################################################################*/
1234 targetPoint.local.displacement = targetPoint.local.position - mLastGesture.local.position;
1235 targetPoint.local.velocity = targetPoint.local.displacement / outputTimeGranularity;
1236 targetPoint.screen.displacement = targetPoint.screen.position - mLastGesture.screen.position;
1237 targetPoint.screen.velocity = targetPoint.screen.displacement / outputTimeGranularity;
1240 /*#########################################################################################
1241 #### Send out the new point, by setting the properties
1242 #### (Constraints will automatically react to this)
1243 #########################################################################################*/
1247 mPanning.Set(mInGesture & !justFinished);
1248 mScreenPosition.Set(targetPoint.screen.position);
1249 mScreenDisplacement.Set(targetPoint.screen.displacement);
1250 mScreenVelocity.Set(targetPoint.screen.velocity);
1251 mLocalPosition.Set(targetPoint.local.position);
1252 mLocalDisplacement.Set(targetPoint.local.displacement);
1253 mLocalVelocity.Set(targetPoint.local.velocity);
1255 mLastGesture = targetPoint;
1259 mProfiling->mAveragedData.push_back(PanGestureProfiling::Position(targetPoint.time, targetPoint.screen.position, targetPoint.screen.displacement, targetPoint.screen.velocity, targetPoint.state));
1264 mInGesture = mInGesture && !justFinished;
1266 return performUpdate;
1269 } // namespace SceneGraph
1271 } // namespace Internal