2 * Copyright (c) 2024 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;
272 mPredictionHistory.clear();
274 // clear just finished as we have started new pan
275 info.justFinished = false;
277 info.justFinished |= (currentGesture.state == GestureState::FINISHED || currentGesture.state == GestureState::CANCELLED);
279 // Update our read position.
280 previousReadPosition = mReadPosition;
281 ++info.eventsThisFrame;
283 mReadPosition %= PAN_GESTURE_HISTORY;
285 // This code does not determine if the data will be used.
289 bool PanGesture::ReadAndResampleGestures(FrameGestureInfo& info, unsigned int currentTimestamp)
291 PanInfo lastReadGesture;
292 Dali::Mutex::ScopedLock lock(mMutex);
293 while(mReadPosition != mWritePosition)
295 // Copy the gesture first
296 lastReadGesture = mGestures[mReadPosition];
299 mProfiling->mRawData.push_back(PanGestureProfiling::Position(lastReadGesture.time, lastReadGesture.screen.position, 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 == GestureState::STARTED)
309 info.justStarted = true;
313 mPredictionHistory.clear();
315 // Clear just finished as we have started new pan.
316 info.justFinished = false;
320 info.justFinished |= (lastReadGesture.state == GestureState::FINISHED || lastReadGesture.state == GestureState::CANCELLED);
323 // Add event to history
324 mPanHistory.push_back(lastReadGesture);
326 // Update our read position.
327 ++info.eventsThisFrame;
329 mReadPosition %= PAN_GESTURE_HISTORY;
332 bool updateProperties = false;
333 if(info.eventsThisFrame > 0)
335 // Some events were read this frame.
336 mTargetGesture = lastReadGesture;
338 if(info.eventsThisFrame > 1)
340 const float eventsThisFrame = static_cast<float>(info.eventsThisFrame);
341 info.frameGesture.screen.position /= eventsThisFrame;
342 info.frameGesture.local.position /= eventsThisFrame;
343 info.frameGesture.screen.velocity /= eventsThisFrame;
344 info.frameGesture.local.velocity /= eventsThisFrame;
346 info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position;
347 info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position;
353 info.frameGesture.screen.displacement = lastReadGesture.screen.displacement;
354 info.frameGesture.local.displacement = lastReadGesture.local.displacement;
357 info.frameGesture.time = currentTimestamp;
359 updateProperties = true;
363 // 0 Events this frame.
366 mNotAtTarget = false;
367 info.frameGesture = mTargetGesture;
368 updateProperties = true;
372 info.frameGesture = mLastGesture;
376 return updateProperties;
379 bool PanGesture::UpdateProperties(unsigned int lastVSyncTime, unsigned int nextVSyncTime)
381 if(mPredictionMode == PREDICTION_2)
383 // TODO: Have the two prediction modes share more behavior so some parts of mode 2 can
384 // be used with mode 1 etc. Needs code moving and more importantly testing.
385 return NewAlgorithm(lastVSyncTime, nextVSyncTime);
390 // clear current pan history
392 mPredictionHistory.clear();
395 FrameGestureInfo frameInfo;
396 bool updateProperties = false;
399 // If we are using a form of prediction, read all the input as-is.
400 if(mPredictionMode != PREDICTION_NONE)
402 // Read input required for prediction algorithms.
403 updateProperties = ReadGestures(frameInfo, lastVSyncTime);
407 // Read and resample input.
408 updateProperties = ReadAndResampleGestures(frameInfo, lastVSyncTime);
411 PanInfo frameGesture = frameInfo.frameGesture;
412 PanInfo unmodifiedGesture = frameGesture;
414 // Process input data.
415 mInGesture |= frameInfo.justStarted;
421 mProfiling->mLatestData.push_back(PanGestureProfiling::Position(lastVSyncTime, frameGesture.screen.position, frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state));
424 // Perform prediction.
425 if(mPredictionMode == PREDICTION_1)
427 // Dynamically change the prediction amount according to the pan velocity acceleration.
428 if(!frameInfo.justStarted)
430 if(frameInfo.eventsThisFrame <= 1)
432 frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length();
435 // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
436 if(fabsf(frameInfo.acceleration) > ACCELERATION_THRESHOLD)
438 mCurrentPredictionAmount += static_cast<unsigned int>(static_cast<float>(mPredictionAmountAdjustment) * (frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f));
439 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
441 mCurrentPredictionAmount = 0;
447 if(!mPredictionAmountOverridden)
449 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
450 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[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 PredictionMode1(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) || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0))
473 overshotXAxis = true;
474 frameGesture.screen.position.x = mLastGesture.screen.position.x;
477 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))
479 overshotYAxis = true;
480 frameGesture.screen.position.y = mLastGesture.screen.position.y;
483 // If there is overshot in one axis, reduce the possible overshot in the other axis,
484 // and reduce the prediction amount so that it doesn't overshoot as easily next time.
485 if(overshotXAxis || overshotYAxis)
487 mCurrentPredictionAmount -= mPredictionAmountAdjustment;
488 if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
490 mCurrentPredictionAmount = 0;
492 mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
494 if(overshotXAxis && !overshotYAxis)
496 frameGesture.screen.position.y = (mLastGesture.screen.position.y + frameGesture.screen.position.y) * 0.5f;
499 if(overshotYAxis && !overshotXAxis)
501 frameGesture.screen.position.x = (mLastGesture.screen.position.x + frameGesture.screen.position.x) * 0.5f;
505 updateProperties = true;
508 // Perform smoothing.
509 switch(mSmoothingMode)
512 case SMOOTHING_MULTI_TAP:
515 // TODO: Old algorithm to be able to use multitap smoothing.
518 case SMOOTHING_LAST_VALUE:
520 if(!frameInfo.justStarted)
522 if(!mSmoothingAmountOverridden)
524 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
525 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[0];
527 BlendPoints(frameGesture, mLastGesture, mSmoothingAmount);
535 // only update properties if event received
536 // set latest gesture to raw pan info with unchanged time
537 mPanning.Set(mInGesture & !frameInfo.justFinished);
538 mScreenPosition.Set(frameGesture.screen.position);
539 mScreenDisplacement.Set(frameGesture.screen.displacement);
540 mScreenVelocity.Set(frameGesture.screen.velocity);
541 mLocalPosition.Set(frameGesture.local.position);
542 mLocalDisplacement.Set(frameGesture.local.displacement);
543 mLocalVelocity.Set(frameGesture.local.velocity);
548 mProfiling->mAveragedData.push_back(PanGestureProfiling::Position(frameGesture.time, frameGesture.screen.position, frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state));
552 mLastGesture = frameGesture;
553 mLastUnmodifiedGesture = unmodifiedGesture;
555 mInGesture = mInGesture && !frameInfo.justFinished;
556 if(mProfiling && frameInfo.justFinished)
558 mProfiling->PrintData();
559 mProfiling->ClearData();
562 return updateProperties;
565 const GesturePropertyBool& PanGesture::GetPanningProperty() const
570 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
572 return mScreenPosition;
575 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
577 return mScreenVelocity;
580 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
582 return mScreenDisplacement;
585 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
587 return mLocalPosition;
590 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
592 return mLocalDisplacement;
595 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
597 return mLocalVelocity;
600 void PanGesture::SetPredictionMode(PredictionMode mode)
602 mPredictionMode = mode;
605 void PanGesture::SetPredictionAmount(unsigned int amount)
607 mPredictionAmount = amount;
608 mPredictionAmountOverridden = true;
611 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
613 mMaxPredictionAmount = amount;
616 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
618 mMinPredictionAmount = amount;
621 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
623 mPredictionAmountAdjustment = amount;
626 void PanGesture::SetSmoothingMode(SmoothingMode mode)
628 mSmoothingMode = mode;
631 void PanGesture::SetSmoothingAmount(float amount)
633 mSmoothingAmount = amount;
634 mSmoothingAmountOverridden = true;
637 void PanGesture::SetUseActualTimes(bool value)
639 mUseActualTimes = value;
642 void PanGesture::SetInterpolationTimeRange(int value)
644 mInterpolationTimeRange = value;
647 void PanGesture::SetScalarOnlyPredictionEnabled(bool value)
649 mScalarOnlyPredictionEnabled = value;
652 void PanGesture::SetTwoPointPredictionEnabled(bool value)
654 mTwoPointPredictionEnabled = value;
657 void PanGesture::SetTwoPointInterpolatePastTime(int value)
659 mTwoPointPastInterpolateTime = value;
662 void PanGesture::SetTwoPointVelocityBias(float value)
664 mTwoPointVelocityBias = value;
667 void PanGesture::SetTwoPointAccelerationBias(float value)
669 mTwoPointAccelerationBias = value;
672 void PanGesture::SetMultitapSmoothingRange(int value)
674 mMultiTapSmoothingRange = value;
677 void PanGesture::EnableProfiling()
681 mProfiling = new PanGestureProfiling();
685 void PanGesture::ResetDefaultProperties(BufferIndex updateBufferIndex)
687 mScreenPosition.Reset();
688 mScreenDisplacement.Reset();
689 mLocalPosition.Reset();
690 mLocalDisplacement.Reset();
694 PanGesture::PanGesture()
701 mPredictionAmountOverridden(false),
702 mSmoothingAmountOverridden(false),
705 // Set environment variable defaults:
706 mPredictionMode(DEFAULT_PREDICTION_MODE),
707 mPredictionAmount(DEFAULT_PREDICTION_AMOUNT[0]),
708 mCurrentPredictionAmount(DEFAULT_PREDICTION_AMOUNT[0]),
709 mMaxPredictionAmount(DEFAULT_MAX_PREDICTION_AMOUNT),
710 mMinPredictionAmount(DEFAULT_MIN_PREDICTION_AMOUNT),
711 mPredictionAmountAdjustment(DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT),
712 mSmoothingMode(DEFAULT_SMOOTHING_MODE),
713 mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT[0]),
714 mUseActualTimes(DEFAULT_USE_ACTUAL_TIMES),
715 mInterpolationTimeRange(DEFAULT_INTERPOLATION_TIME_RANGE),
716 mScalarOnlyPredictionEnabled(DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED),
717 mTwoPointPredictionEnabled(DEFAULT_TWO_POINT_PREDICTION_ENABLED),
718 mTwoPointPastInterpolateTime(DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME),
719 mTwoPointVelocityBias(DEFAULT_TWO_POINT_VELOCITY_BIAS),
720 mTwoPointAccelerationBias(DEFAULT_TWO_POINT_ACCELERATION_BIAS),
721 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, unsigned int currentFrameTime, unsigned int lastFrameTime, bool& justStarted, bool& justFinished)
745 // TODO: Lots of variables on the stack. Needs optimizing.
747 PanInfo firstReadGesture;
748 unsigned int eventsKeptThisFrame = 0;
750 for(unsigned int readPosition = 0; readPosition < eventsThisFrame; ++readPosition)
752 // Copy the gesture first
753 readGesture = mReadGestures[readPosition];
757 mProfiling->mRawData.push_back(PanGestureProfiling::Position(readGesture.time, readGesture.screen.position, readGesture.screen.displacement, readGesture.screen.velocity, readGesture.state));
760 if(readGesture.state == GestureState::STARTED)
764 mPredictionHistory.clear();
765 mLastAcceleration.local = Vector2::ZERO;
766 mLastAcceleration.screen = Vector2::ZERO;
767 mLastInterpolatedAcceleration.local = Vector2::ZERO;
768 mLastInterpolatedAcceleration.screen = Vector2::ZERO;
769 mLastInitialAcceleration.local = Vector2::ZERO;
770 mLastInitialAcceleration.screen = Vector2::ZERO;
772 mLastGesture = startInfo;
773 mLastSecondInterpolatedPoint = startInfo;
774 mLastPredictedPoint = startInfo;
775 mLastFrameReadGesture = startInfo;
776 rateConvertedGesture = startInfo;
777 firstReadGesture = readGesture;
778 eventsKeptThisFrame = 0;
779 mNotAtTarget = false;
780 justFinished = false;
784 if(!mPredictionAmountOverridden)
786 // If the prediction amount has not been modified, default to the correct amount for this algorithm.
787 mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[1];
789 mCurrentPredictionAmount = mPredictionAmount;
793 justFinished |= (readGesture.state == GestureState::FINISHED || readGesture.state == GestureState::CANCELLED);
796 rateConvertedGesture.screen.position += readGesture.screen.position;
797 rateConvertedGesture.local.position += readGesture.local.position;
798 rateConvertedGesture.screen.velocity += readGesture.screen.velocity;
799 rateConvertedGesture.local.velocity += readGesture.local.velocity;
800 rateConvertedGesture.screen.displacement += readGesture.screen.displacement;
801 rateConvertedGesture.local.displacement += readGesture.local.displacement;
803 ++eventsKeptThisFrame;
806 bool storeGesture = false;
807 if(eventsKeptThisFrame > 0)
809 // Some events were read this frame.
810 if(eventsKeptThisFrame > 1)
812 const float eventDivisor = static_cast<float>(eventsKeptThisFrame);
813 rateConvertedGesture.screen.position /= eventDivisor;
814 rateConvertedGesture.local.position /= eventDivisor;
815 rateConvertedGesture.screen.velocity /= eventDivisor;
816 rateConvertedGesture.local.velocity /= eventDivisor;
817 rateConvertedGesture.screen.displacement /= eventDivisor;
818 rateConvertedGesture.local.displacement /= eventDivisor;
820 mTargetGesture = readGesture;
825 mNotAtTarget = false;
828 rateConvertedGesture.time = currentFrameTime;
833 // We did not get any event this frame.
834 // If we just started (or aren't in a gesture), exit.
835 if(!mInGesture || justStarted)
837 // We cannot guess what the event could be as we have no other events to base the guess from.
841 // As we are currently in a gesture, we can estimate an event.
842 readGesture = mLastFrameReadGesture;
843 readGesture.time = currentFrameTime;
845 // Take the last event, halve the acceleration, and use that.
846 const float accelerationDegrade = 2.0f;
847 Vector2 degradedAccelerationLocal(mLastAcceleration.local /= accelerationDegrade);
848 Vector2 degradedAccelerationScreen(mLastAcceleration.screen /= accelerationDegrade);
850 float outputTimeGranularity(GetDivisibleTimeDifference(currentFrameTime, lastFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
852 readGesture.local.velocity = degradedAccelerationLocal * outputTimeGranularity;
853 readGesture.local.displacement = readGesture.local.velocity * outputTimeGranularity;
854 readGesture.local.position = mLastFrameReadGesture.local.position + readGesture.local.displacement;
855 readGesture.screen.velocity = degradedAccelerationScreen * outputTimeGranularity;
856 readGesture.screen.displacement = readGesture.screen.velocity * outputTimeGranularity;
857 readGesture.screen.position = mLastFrameReadGesture.screen.position + readGesture.screen.displacement;
859 rateConvertedGesture = readGesture;
860 eventsKeptThisFrame = 1;
864 if(eventsKeptThisFrame > 0)
866 // Store last read gesture.
867 readGesture.time = currentFrameTime;
868 mLastFrameReadGesture = readGesture;
870 if(eventsKeptThisFrame > 2)
872 DALI_LOG_WARNING("Got events this frame:%d (more than 2 will compromise result)\n", eventsKeptThisFrame);
878 // Store final converted result.
879 mPanHistory.push_back(rateConvertedGesture);
884 bool PanGesture::InterpolatePoint(PanInfoHistory& history, unsigned int currentTime, unsigned int targetTime, unsigned int range, PanInfo& outPoint, RelativeVectors& acceleration, int outputTimeGranularity, bool eraseUnused)
886 unsigned int maxHistoryTime = targetTime - range;
887 unsigned int tapsUsed = 0;
888 outPoint.time = targetTime;
889 float divisor = 0.0f;
890 float accelerationDivisor = 0.0f;
891 PanInfoHistoryIter historyBegin = history.begin();
892 PanInfoHistoryIter lastIt = history.end();
893 bool pointGenerated = false;
894 bool havePreviousPoint = false;
895 RelativeVectors newAcceleration;
897 // Iterate through point history to perform interpolation.
898 for(PanInfoHistoryIter it = historyBegin; it != history.end();)
900 unsigned int gestureTime = it->time;
902 if(gestureTime < maxHistoryTime)
904 // Too far in the past, discard.
905 // Clean history as we go (if requested).
908 it = history.erase(it);
918 float timeDelta(static_cast<float>(abs(int(targetTime - gestureTime))));
919 // Handle low time deltas.
925 outPoint.local.position += it->local.position / timeDelta;
926 outPoint.screen.position += it->screen.position / timeDelta;
927 outPoint.local.velocity += it->local.velocity / timeDelta;
928 outPoint.screen.velocity += it->screen.velocity / timeDelta;
929 outPoint.local.displacement += it->local.displacement / timeDelta;
930 outPoint.screen.displacement += it->screen.displacement / timeDelta;
932 divisor += 1.0f / timeDelta;
934 // Acceleration requires a previous point.
935 if(havePreviousPoint)
937 // Time delta of input.
938 float timeDifference(GetDivisibleTimeDifference(it->time, lastIt->time, 1.0f, OUTPUT_TIME_DIFFERENCE));
940 newAcceleration.local += ((it->local.velocity - lastIt->local.velocity) / timeDifference) / timeDelta;
941 newAcceleration.screen += ((it->screen.velocity - lastIt->screen.velocity) / timeDifference) / timeDelta;
943 accelerationDivisor += 1.0f / timeDelta;
947 havePreviousPoint = true;
956 // Divide results by their respective divisors.
961 outPoint.local.position /= divisor;
962 outPoint.screen.position /= divisor;
963 outPoint.local.velocity /= divisor;
964 outPoint.screen.velocity /= divisor;
965 outPoint.local.displacement /= divisor;
966 outPoint.screen.displacement /= divisor;
971 if(accelerationDivisor > 0.0f)
973 newAcceleration.local /= accelerationDivisor;
974 newAcceleration.screen /= accelerationDivisor;
977 float accelerationSmoothing(ACCELERATION_SMOOTHING);
978 newAcceleration.local = (acceleration.local * accelerationSmoothing) + (newAcceleration.local * (1.0f - accelerationSmoothing));
979 newAcceleration.screen = (acceleration.screen * accelerationSmoothing) + (newAcceleration.screen * (1.0f - accelerationSmoothing));
983 // If we just started, last velocity was 0. So difference of zero to current velocity over time gives acceleration of the first point.
984 newAcceleration.local = outPoint.local.velocity / static_cast<float>(outputTimeGranularity);
985 newAcceleration.screen = outPoint.screen.velocity / static_cast<float>(outputTimeGranularity);
987 pointGenerated = true;
990 acceleration.local = newAcceleration.local;
991 acceleration.screen = newAcceleration.screen;
992 return pointGenerated;
995 float PanGesture::GetDivisibleTimeDifference(int timeA, int timeB, float minimumDelta, float overrideDifference)
997 float timeDifference(overrideDifference);
1000 timeDifference = static_cast<float>(abs(timeA - timeB));
1001 if(timeDifference < minimumDelta)
1003 timeDifference = minimumDelta;
1006 return timeDifference;
1009 void PanGesture::LimitAccelerationChange(RelativeVectors& currentAcceleration, RelativeVectors& lastAcceleration, float changeLimit)
1011 // We don't use the float parameter version of clamp here, as that will create the capping vectors twice in total.
1012 Vector2 capMinimum(-changeLimit, -changeLimit);
1013 Vector2 capMaximum(changeLimit, changeLimit);
1014 Vector2 accelerationDeltaLocal(currentAcceleration.local - lastAcceleration.local);
1015 Vector2 accelerationDeltaScreen(currentAcceleration.screen - lastAcceleration.screen);
1016 accelerationDeltaLocal.Clamp(capMinimum, capMaximum);
1017 accelerationDeltaScreen.Clamp(capMinimum, capMaximum);
1018 currentAcceleration.local = lastAcceleration.local + accelerationDeltaLocal;
1019 currentAcceleration.screen = lastAcceleration.screen + accelerationDeltaScreen;
1022 void PanGesture::PredictionMode2(PanInfo& startPoint, RelativeVectors& accelerationToUse, PanInfo& predictedPoint, unsigned int currentFrameTime, unsigned int previousFrameTime, bool noPreviousData)
1024 // Do the prediction (based on mode).
1025 if(mScalarOnlyPredictionEnabled)
1027 // We are doing scalar based prediction.
1028 // This divisor is to help tuning by giving the scalar only result
1029 // a similar prediction amount to the integrated result.
1030 float scalarVelocityMultiplier = static_cast<float>(mCurrentPredictionAmount) / 1.364f;
1031 predictedPoint.local.position = startPoint.local.position + (startPoint.local.velocity * scalarVelocityMultiplier);
1032 predictedPoint.screen.position = startPoint.screen.position + (startPoint.screen.velocity * scalarVelocityMultiplier);
1036 // We are doing integration based prediction.
1037 float predictionDelta = static_cast<float>(mCurrentPredictionAmount);
1039 predictedPoint.local.position = startPoint.local.position + (startPoint.local.velocity * predictionDelta) +
1040 (accelerationToUse.local * (predictionDelta * predictionDelta * 0.5f));
1041 predictedPoint.screen.position = startPoint.screen.position + (startPoint.screen.velocity * predictionDelta) +
1042 (accelerationToUse.screen * (predictionDelta * predictionDelta * 0.5f));
1045 // Calculate remaining gesture data from the result.
1046 float timeDifference(GetDivisibleTimeDifference(currentFrameTime, previousFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1049 predictedPoint.local.displacement = predictedPoint.local.position - startPoint.local.position;
1050 predictedPoint.screen.displacement = predictedPoint.screen.position - startPoint.screen.position;
1054 predictedPoint.local.displacement = predictedPoint.local.position - mLastPredictedPoint.local.position;
1055 predictedPoint.screen.displacement = predictedPoint.screen.position - mLastPredictedPoint.screen.position;
1057 predictedPoint.local.velocity = predictedPoint.local.displacement / timeDifference;
1058 predictedPoint.screen.velocity = predictedPoint.screen.displacement / timeDifference;
1060 // TODO: Experimental - not used at run time. Left in code for reference only.
1061 if(TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION)
1063 // Overshoot protection
1066 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))
1068 predictedPoint.local.position.x = mLastPredictedPoint.local.position.x;
1069 predictedPoint.screen.position.x = mLastPredictedPoint.screen.position.x;
1070 mPredictionHistory.clear();
1072 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))
1074 predictedPoint.local.position.y = mLastPredictedPoint.local.position.y;
1075 predictedPoint.screen.position.y = mLastPredictedPoint.screen.position.y;
1076 mPredictionHistory.clear();
1081 predictedPoint.time = currentFrameTime;
1082 mLastPredictedPoint = predictedPoint;
1085 // TODO: This needs a better name! It is called this instead of prediction mode 2 because:
1086 // 1) It is the entire workflow, not just prediction.
1087 // 2) To make it less confusing as there is a function that does prediction alone called PerformPredictionMode2.
1088 // 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.
1089 // At the moment, the differences between the inputs & outputs of these different functions prevent that, but this can be resolved.
1090 bool PanGesture::NewAlgorithm(unsigned int lastVSyncTime, unsigned int nextVSyncTime)
1094 // clear current pan history
1095 mPanHistory.clear();
1096 mPredictionHistory.clear();
1099 /*#########################################################################################
1100 #### Read in all gestures received this frame first (holding a lock for a short time)
1101 #########################################################################################*/
1103 unsigned int eventsThisFrame = ReadFrameEvents();
1105 /*#########################################################################################
1106 #### Perform input rate-conversion on all gestures received this frame.
1107 #### This also populates the pan history.
1108 #########################################################################################*/
1110 bool justStarted = false;
1111 bool justFinished = false;
1112 PanInfo rateConvertedGesture;
1113 if(!InputRateConversion(rateConvertedGesture, eventsThisFrame, nextVSyncTime, lastVSyncTime, justStarted, justFinished))
1115 // There's nothing we can do with the input, exit.
1119 /*#########################################################################################
1120 #### If we are in gesture, Get first interpolated point with: target time = current time
1121 #########################################################################################*/
1123 bool performUpdate = false;
1124 RelativeVectors currentAcceleration;
1125 currentAcceleration.local = mLastInitialAcceleration.local;
1126 currentAcceleration.screen = mLastInitialAcceleration.screen;
1128 if(mInGesture || justStarted)
1130 // Get first interpolated point.
1131 // TODO: Erase time should be maximum of both interpolated point ranges in past.
1132 PanInfo targetPoint;
1133 float outputTimeGranularity(GetDivisibleTimeDifference(nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1134 bool pointGenerated = InterpolatePoint(mPanHistory, nextVSyncTime, nextVSyncTime, mInterpolationTimeRange, targetPoint, currentAcceleration, static_cast<int>(outputTimeGranularity), true); // truncated
1137 mLastInitialAcceleration.local = currentAcceleration.local;
1138 mLastInitialAcceleration.screen = currentAcceleration.screen;
1139 performUpdate = true;
1143 targetPoint = rateConvertedGesture;
1144 currentAcceleration.local = mLastInitialAcceleration.local;
1145 currentAcceleration.screen = mLastInitialAcceleration.screen;
1146 // TODO: Potentially do something to substitute lack of generated point (and perform update).
1149 /*#########################################################################################
1150 #### Limit the change of acceleration of the first interpolated point since last time
1151 #########################################################################################*/
1155 LimitAccelerationChange(currentAcceleration, mLastAcceleration, ACCELERATION_CAP);
1157 mLastAcceleration.local = currentAcceleration.local;
1158 mLastAcceleration.screen = currentAcceleration.screen;
1160 /*#########################################################################################
1161 #### Get second interpolated point, and blend the resultant velocity and acceleration (optional)
1162 #########################################################################################*/
1165 RelativeVectors interpolatedAcceleration;
1166 if(mTwoPointPredictionEnabled)
1168 // Get second interpolated point with target time = current time - past interpolate time.
1169 unsigned int pastInterpolateTime = nextVSyncTime - mTwoPointPastInterpolateTime;
1171 RelativeVectors interpolatedAcceleration;
1172 interpolatedAcceleration.local = mLastInterpolatedAcceleration.local;
1173 interpolatedAcceleration.screen = mLastInterpolatedAcceleration.screen;
1174 if(!InterpolatePoint(mPanHistory, nextVSyncTime, pastInterpolateTime, mTwoPointPastInterpolateTime, outPoint, interpolatedAcceleration, static_cast<int>(outputTimeGranularity), false)) // truncated
1178 outPoint = targetPoint;
1182 outPoint = mLastSecondInterpolatedPoint;
1185 mLastInterpolatedAcceleration.local = interpolatedAcceleration.local;
1186 mLastInterpolatedAcceleration.screen = interpolatedAcceleration.screen;
1187 mLastSecondInterpolatedPoint = outPoint;
1189 // Combine the first interpolated point and the second interpolated point.
1190 // by mixing them with the configured amount. This is done for acceleration and velocity.
1191 // It could be optionally done for position too, but this typically is worse as it means we have to predict further ahead.
1192 float currentVelocityMultiplier(1.0f - mTwoPointVelocityBias);
1193 float lastVelocityMultiplier(mTwoPointVelocityBias);
1194 targetPoint.local.velocity = (outPoint.local.velocity * lastVelocityMultiplier) + (targetPoint.local.velocity * currentVelocityMultiplier);
1195 targetPoint.screen.velocity = (outPoint.screen.velocity * lastVelocityMultiplier) + (targetPoint.screen.velocity * currentVelocityMultiplier);
1196 float currentAccelerationMultiplier(1.0f - mTwoPointAccelerationBias);
1197 float lastAccelerationMultiplier(mTwoPointAccelerationBias);
1198 currentAcceleration.local = (interpolatedAcceleration.local * lastAccelerationMultiplier) + (currentAcceleration.local * currentAccelerationMultiplier);
1199 currentAcceleration.screen = (interpolatedAcceleration.screen * lastAccelerationMultiplier) + (currentAcceleration.screen * currentAccelerationMultiplier);
1202 /*#########################################################################################
1203 #### Perform prediction
1204 #########################################################################################*/
1206 PanInfo predictedPoint;
1207 PredictionMode2(targetPoint, currentAcceleration, predictedPoint, nextVSyncTime, lastVSyncTime, justStarted);
1208 targetPoint = predictedPoint;
1210 /*#########################################################################################
1212 #########################################################################################*/
1214 // If we are using multi-tap smoothing, keep a history of predicted results.
1215 if(mSmoothingMode == SMOOTHING_MULTI_TAP)
1217 mPredictionHistory.push_back(targetPoint);
1222 float outputTimeGranularity(GetDivisibleTimeDifference(nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE));
1223 if(mSmoothingMode == SMOOTHING_MULTI_TAP)
1225 // Perform Multi-tap Smoothing.
1226 RelativeVectors blank;
1227 InterpolatePoint(mPredictionHistory, nextVSyncTime, nextVSyncTime, mMultiTapSmoothingRange, targetPoint, blank, static_cast<int>(outputTimeGranularity), true); // truncated
1231 // Perform Single-tap Smoothing.
1232 if(!mSmoothingAmountOverridden)
1234 // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
1235 mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[1];
1237 BlendPoints(targetPoint, mLastGesture, mSmoothingAmount);
1240 /*#########################################################################################
1241 #### Finalize other point data (from position)
1242 #########################################################################################*/
1244 targetPoint.local.displacement = targetPoint.local.position - mLastGesture.local.position;
1245 targetPoint.local.velocity = targetPoint.local.displacement / outputTimeGranularity;
1246 targetPoint.screen.displacement = targetPoint.screen.position - mLastGesture.screen.position;
1247 targetPoint.screen.velocity = targetPoint.screen.displacement / outputTimeGranularity;
1250 /*#########################################################################################
1251 #### Send out the new point, by setting the properties
1252 #### (Constraints will automatically react to this)
1253 #########################################################################################*/
1257 mPanning.Set(mInGesture & !justFinished);
1258 mScreenPosition.Set(targetPoint.screen.position);
1259 mScreenDisplacement.Set(targetPoint.screen.displacement);
1260 mScreenVelocity.Set(targetPoint.screen.velocity);
1261 mLocalPosition.Set(targetPoint.local.position);
1262 mLocalDisplacement.Set(targetPoint.local.displacement);
1263 mLocalVelocity.Set(targetPoint.local.velocity);
1265 mLastGesture = targetPoint;
1269 mProfiling->mAveragedData.push_back(PanGestureProfiling::Position(targetPoint.time, targetPoint.screen.position, targetPoint.screen.displacement, targetPoint.screen.velocity, targetPoint.state));
1274 mInGesture = mInGesture && !justFinished;
1276 return performUpdate;
1279 } // namespace SceneGraph
1281 } // namespace Internal