[4.0] pan-gesture code refactor and environment variables
[platform/core/uifw/dali-core.git] / dali / internal / update / gestures / scene-graph-pan-gesture.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/update/gestures/scene-graph-pan-gesture.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 // INTERNAL INCLUDES
25 #include <dali/internal/update/gestures/pan-gesture-profiling.h>
26 #include <dali/integration-api/debug.h>
27
28 namespace Dali
29 {
30
31 namespace Internal
32 {
33
34 namespace SceneGraph
35 {
36
37 namespace
38 {
39
40 // TODO: Experimental - for changing in code only:
41 const bool         TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION   = false;
42
43 // Internal defaults:
44 const int          MAX_GESTURE_AGE                         = 200;    ///< maximum age of a gesture before disallowing its use in algorithm TODO: Possibly make this configurable.
45 const float        ACCELERATION_THRESHOLD                  = 0.1f;   ///< minimum pan velocity change to trigger dynamic change of prediction amount.
46 const float        OUTPUT_TIME_DIFFERENCE                  = ( 1000.0f / 60.0f ); ///< This is used to optionally override actual times if they make results worse.
47 const float        ACCELERATION_SMOOTHING                  = 0.44f;  ///< Smoothes acceleration changes from one frame to another.
48 const float        ACCELERATION_CAP                        = 0.0004f;///< Limits acceleration changes from one frame to another.
49
50 // Defaults for Environment Variables:
51
52 // Prediction Mode 1:
53 const unsigned int DEFAULT_MAX_PREDICTION_AMOUNT           = 32;     ///< the upper bound of the range to clamp the prediction interpolation.
54 const unsigned int DEFAULT_MIN_PREDICTION_AMOUNT           = 0;      ///< the lower bound of the range to clamp the prediction interpolation.
55 const unsigned int DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT    = 2;      ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes.
56
57 // Prediction Mode 2:
58 const bool         DEFAULT_USE_ACTUAL_TIMES                = false;  ///< Disable to optionally override actual times if they make results worse.
59 const int          DEFAULT_INTERPOLATION_TIME_RANGE        = 255;    ///< Time into past history (ms) to use points to interpolate the first point.
60 const bool         DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED  = false;  ///< If enabled, prediction is done using velocity alone (no integration or acceleration).
61 const bool         DEFAULT_TWO_POINT_PREDICTION_ENABLED    = true;   ///< If enabled, a second interpolated point is predicted and combined with the first to get more stable values.
62 const int          DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME = 42;     ///< The target time in the past to generate the second interpolated point.
63 const float        DEFAULT_TWO_POINT_VELOCITY_BIAS         = 0.35f;  ///< The ratio of first and second interpolated points to use for velocity. 0.0f = 100% of first point. 1.0f = 100% of second point.
64 const float        DEFAULT_TWO_POINT_ACCELERATION_BIAS     = 0.10f;  ///< The ratio of first and second interpolated points to use for acceleration. 0.0f = 100% of first point. 1.0f = 100% of second point.
65 const int          DEFAULT_MULTITAP_SMOOTHING_RANGE        = 34;     ///< The range in time (ms) of points in the history to smooth the final output against.
66
67 // Prediction Modes 1 & 2.
68 const unsigned int DEFAULT_PREDICTION_AMOUNT[2]            = {     5,    57 }; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
69 const float        DEFAULT_SMOOTHING_AMOUNT[2]             = { 0.25f, 0.23f }; ///< how much to smooth final result from last vsync time
70
71 } // unnamed namespace
72
73 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
74 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_2 + 1;
75
76 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
77 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_MULTI_TAP + 1;
78
79 PanGesture* PanGesture::New()
80 {
81   return new PanGesture();
82 }
83
84 PanGesture::~PanGesture()
85 {
86   delete mProfiling;
87 }
88
89 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
90 {
91   Dali::Mutex::ScopedLock lock( mMutex );
92   mGestures[ mWritePosition ] = gesture;
93
94   // Update our write position.
95   ++mWritePosition;
96   mWritePosition %= PAN_GESTURE_HISTORY;
97 }
98
99 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
100 {
101   PanInfoHistoryConstIter endIter = panHistory.end();
102   PanInfoHistoryIter iter = panHistory.begin();
103   while( iter != endIter && panHistory.size() > minEvents)
104   {
105     PanInfo currentGesture = *iter;
106     if( currentTime < currentGesture.time + maxAge )
107     {
108       break;
109     }
110     iter = panHistory.erase(iter);
111     endIter = panHistory.end();
112   }
113
114   // dont want more than 5 previous predictions for smoothing
115   iter = mPredictionHistory.begin();
116   while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
117   {
118     iter = mPredictionHistory.erase(iter);
119   }
120 }
121
122 void PanGesture::PredictionMode1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
123 {
124   RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
125   size_t panHistorySize = panHistory.size();
126   if( panHistorySize == 0 )
127   {
128     // cant do any prediction without a history
129     return;
130   }
131
132   PanInfoHistoryConstIter endIter = panHistory.end();
133   PanInfoHistoryIter iter = panHistory.begin();
134   Vector2 screenVelocity = gestureOut.screen.velocity;
135   Vector2 localVelocity = gestureOut.local.velocity;
136   Vector2 screenDisplacement = gestureOut.screen.displacement;
137   Vector2 localDisplacement = gestureOut.local.displacement;
138
139   bool havePreviousAcceleration = false;
140   bool previousVelocity = false;
141   float previousAccel = 0.0f;
142   unsigned int lastTime(0);
143
144   unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
145
146   if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
147   {
148     interpolationTime -= gestureOut.time;
149   }
150   else
151   {
152     interpolationTime = 0u;
153   }
154
155   while( iter != endIter )
156   {
157     PanInfo currentGesture = *iter;
158     if( !previousVelocity )
159     {
160       // not yet set a previous velocity
161       screenVelocity = currentGesture.screen.velocity;
162       previousVelocity = true;
163       lastTime = currentGesture.time;
164       ++iter;
165       continue;
166     }
167     float previousValueWeight = ( static_cast< float >( MAX_GESTURE_AGE ) - (lastVSyncTime - lastTime) ) / static_cast< float >( MAX_GESTURE_AGE );
168     float velMag = currentGesture.screen.velocity.Length();
169     float velDiff = velMag - screenVelocity.Length();
170     float acceleration = 0.0f;
171
172     float time(0.f);
173     if (currentGesture.time > lastTime) // Guard against invalid timestamps
174     {
175       time = static_cast<float>( currentGesture.time - lastTime );
176     }
177     if( time > Math::MACHINE_EPSILON_1 )
178     {
179       acceleration = velDiff / time;
180     }
181
182     float newVelMag = 0.0f;
183     int currentInterpolation = interpolationTime;
184     if( !havePreviousAcceleration )
185     {
186       newVelMag =  velMag;
187       havePreviousAcceleration = true;
188     }
189     else
190     {
191       newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
192     }
193     float velMod = 1.0f;
194     if( velMag > Math::MACHINE_EPSILON_1 )
195     {
196       velMod = newVelMag / velMag;
197     }
198     gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
199     gestureOut.local.velocity = currentGesture.local.velocity * velMod;
200     screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
201     localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
202     screenVelocity = currentGesture.screen.velocity;
203     localVelocity = currentGesture.local.velocity;
204     previousAccel = acceleration;
205     ++iter;
206   }
207   // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
208   // add interpolated distance and position to current
209   // work out interpolated velocity
210   gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
211   gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
212   gestureOut.screen.displacement = screenDisplacement;
213   gestureOut.local.displacement = localDisplacement;
214   gestureOut.time += interpolationTime;
215 }
216
217 void PanGesture::BlendPoints( PanInfo& gesture, PanInfo& lastGesture, float blendValue )
218 {
219   gesture.screen.position -= ( gesture.screen.position - lastGesture.screen.position ) * 0.5f * ( 1.0f - blendValue );
220   gesture.local.position -= ( gesture.local.position - lastGesture.local.position ) * 0.5f * ( 1.0f - blendValue );
221   // Make current displacement relative to previous update-frame now.
222   gesture.screen.displacement = gesture.screen.position - lastGesture.screen.position;
223   gesture.local.displacement = gesture.local.position - lastGesture.local.position;
224   // Calculate velocity relative to previous update-frame
225   float timeDifference( gesture.time - lastGesture.time );
226   gesture.screen.velocity = gesture.screen.displacement / timeDifference;
227   gesture.local.velocity = gesture.local.displacement / timeDifference;
228 }
229
230 bool PanGesture::ReadGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
231 {
232   unsigned int previousReadPosition = 0;
233   bool eventFound = false;
234   info.frameGesture = mLastUnmodifiedGesture;
235
236   while( mReadPosition != mWritePosition )
237   {
238     // Copy the gesture first
239     PanInfo currentGesture( mGestures[mReadPosition] );
240
241     if( mProfiling )
242     {
243       mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) );
244     }
245     info.frameGesture.local.position = currentGesture.local.position;
246     info.frameGesture.local.velocity = currentGesture.local.velocity;
247     info.frameGesture.screen.position = currentGesture.screen.position;
248     info.frameGesture.screen.velocity = currentGesture.screen.velocity;
249
250     if( info.eventsThisFrame > 0 )
251     {
252       info.acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
253     }
254
255     if( !eventFound )
256     {
257       info.frameGesture.local.displacement = currentGesture.local.displacement;
258       info.frameGesture.screen.displacement = currentGesture.screen.displacement;
259       eventFound = true;
260     }
261     else
262     {
263       info.frameGesture.local.displacement += currentGesture.local.displacement;
264       info.frameGesture.screen.displacement += currentGesture.screen.displacement;
265     }
266     info.frameGesture.time = currentGesture.time;
267
268     // add event to history
269     mPanHistory.push_back( currentGesture );
270     if( currentGesture.state == Gesture::Started )
271     {
272       info.justStarted = true;
273       // clear just finished as we have started new pan
274       info.justFinished = false;
275     }
276     info.justFinished |= ( currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled );
277
278     // Update our read position.
279     previousReadPosition = mReadPosition;
280     ++info.eventsThisFrame;
281     ++mReadPosition;
282     mReadPosition %= PAN_GESTURE_HISTORY;
283   }
284   // This code does not determine if the data will be used.
285   return false;
286 }
287
288 bool PanGesture::ReadAndResampleGestures( FrameGestureInfo& info, unsigned int currentTimestamp )
289 {
290   PanInfo lastReadGesture;
291   Dali::Mutex::ScopedLock lock( mMutex );
292   while( mReadPosition != mWritePosition )
293   {
294     // Copy the gesture first
295     lastReadGesture = mGestures[mReadPosition];
296     if( mProfiling )
297     {
298       mProfiling->mRawData.push_back( PanGestureProfiling::Position( lastReadGesture.time, lastReadGesture.screen.position,
299           lastReadGesture.screen.displacement, lastReadGesture.screen.velocity, lastReadGesture.state ) );
300     }
301
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;
306
307     if( lastReadGesture.state == Gesture::Started )
308     {
309       // Clear just finished as we have started new pan.
310       info.justFinished = false;
311       info.justStarted = true;
312     }
313     else
314     {
315       info.justFinished |= ( lastReadGesture.state == Gesture::Finished || lastReadGesture.state == Gesture::Cancelled );
316     }
317
318     // Add event to history
319     mPanHistory.push_back( lastReadGesture );
320
321     // Update our read position.
322     ++info.eventsThisFrame;
323     ++mReadPosition;
324     mReadPosition %= PAN_GESTURE_HISTORY;
325   }
326
327   bool updateProperties = false;
328   if( info.eventsThisFrame > 0 )
329   {
330     // Some events were read this frame.
331     mTargetGesture = lastReadGesture;
332
333     if( info.eventsThisFrame > 1 )
334     {
335       info.frameGesture.screen.position /= info.eventsThisFrame;
336       info.frameGesture.local.position /= info.eventsThisFrame;
337       info.frameGesture.screen.velocity /= info.eventsThisFrame;
338       info.frameGesture.local.velocity /= info.eventsThisFrame;
339
340       info.frameGesture.screen.displacement = info.frameGesture.screen.position - mLastGesture.screen.position;
341       info.frameGesture.local.displacement = info.frameGesture.local.position - mLastGesture.local.position;
342
343       mNotAtTarget = true;
344     }
345     else
346     {
347       info.frameGesture.screen.displacement = lastReadGesture.screen.displacement;
348       info.frameGesture.local.displacement = lastReadGesture.local.displacement;
349     }
350
351     info.frameGesture.time = currentTimestamp;
352
353     updateProperties = true;
354   }
355   else
356   {
357     // 0 Events this frame.
358     if( mNotAtTarget )
359     {
360       mNotAtTarget = false;
361       info.frameGesture = mTargetGesture;
362       updateProperties = true;
363     }
364     else
365     {
366       info.frameGesture = mLastGesture;
367     }
368   }
369
370   return updateProperties;
371 }
372
373 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
374 {
375   if( mPredictionMode == PREDICTION_2 )
376   {
377     // TODO: Have the two prediction modes share more behavior so some parts of mode 2 can
378     // be used with mode 1 etc. Needs code moving and more importantly testing.
379     return NewAlgorithm( lastVSyncTime, nextVSyncTime );
380   }
381
382   if( !mInGesture )
383   {
384     // clear current pan history
385     mPanHistory.clear();
386     mPredictionHistory.clear();
387   }
388
389   FrameGestureInfo frameInfo;
390   bool updateProperties = false;
391
392   // Read input data.
393   // If we are using a form of prediction, read all the input as-is.
394   if( mPredictionMode != PREDICTION_NONE )
395   {
396     // Read input required for prediction algorithms.
397     updateProperties = ReadGestures( frameInfo, lastVSyncTime );
398   }
399   else
400   {
401     // Read and resample input.
402     updateProperties = ReadAndResampleGestures( frameInfo, lastVSyncTime );
403   }
404
405   PanInfo frameGesture = frameInfo.frameGesture;
406   PanInfo unmodifiedGesture = frameGesture;
407
408   // Process input data.
409   mInGesture |= frameInfo.justStarted;
410   if( mInGesture )
411   {
412     // Profiling.
413     if( mProfiling )
414     {
415       mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, frameGesture.screen.position,
416           frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
417     }
418
419     // Perform prediction.
420     if( mPredictionMode == PREDICTION_1 )
421     {
422       // Dynamically change the prediction amount according to the pan velocity acceleration.
423       if( !frameInfo.justStarted )
424       {
425         if( frameInfo.eventsThisFrame <= 1 )
426         {
427           frameInfo.acceleration = frameGesture.screen.velocity.Length() - mLastUnmodifiedGesture.screen.velocity.Length();
428         }
429
430         // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
431         if( fabsf( frameInfo.acceleration ) > ACCELERATION_THRESHOLD )
432         {
433           mCurrentPredictionAmount += mPredictionAmountAdjustment * ( frameInfo.acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f );
434           if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
435           {
436             mCurrentPredictionAmount = 0;
437           }
438         }
439       }
440       else
441       {
442         if( !mPredictionAmountOverridden )
443         {
444           // If the prediction amount has not been modified, default to the correct amount for this algorithm.
445           mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[0];
446         }
447         mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
448       }
449
450       mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
451
452       // Calculate the delta of positions before the prediction
453       Vector2 deltaPosition = frameGesture.screen.position - mLastUnmodifiedGesture.screen.position;
454
455       // Make latest gesture equal to current gesture before interpolation
456       PredictionMode1( frameInfo.eventsThisFrame, frameGesture, mPanHistory, lastVSyncTime, nextVSyncTime );
457
458       // Calculate the delta of positions after the prediction.
459       Vector2 deltaPredictedPosition = frameGesture.screen.position - mLastGesture.screen.position;
460
461       // If the change in the prediction has a different sign than the change in the actual position,
462       // there is overshot (i.e. the current prediction is too large). Return the previous prediction
463       // to give the user's finger a chance to catch up with where we have panned to.
464       bool overshotXAxis = false;
465       bool overshotYAxis = false;
466       if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 )
467        || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) )
468       {
469         overshotXAxis = true;
470         frameGesture.screen.position.x = mLastGesture.screen.position.x;
471       }
472
473       if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 )
474        || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) )
475       {
476         overshotYAxis = true;
477         frameGesture.screen.position.y = mLastGesture.screen.position.y;
478       }
479
480       // If there is overshot in one axis, reduce the possible overshot in the other axis,
481       // and reduce the prediction amount so that it doesn't overshoot as easily next time.
482       if(overshotXAxis || overshotYAxis)
483       {
484         mCurrentPredictionAmount -= mPredictionAmountAdjustment;
485         if( mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment ) // Guard against unsigned int overflow
486         {
487           mCurrentPredictionAmount = 0;
488         }
489         mCurrentPredictionAmount = std::max( mMinPredictionAmount, std::min( mCurrentPredictionAmount, mMaxPredictionAmount ) );
490
491         if( overshotXAxis && !overshotYAxis )
492         {
493           frameGesture.screen.position.y = ( mLastGesture.screen.position.y + frameGesture.screen.position.y ) * 0.5f;
494         }
495
496         if( overshotYAxis && !overshotXAxis )
497         {
498           frameGesture.screen.position.x = ( mLastGesture.screen.position.x + frameGesture.screen.position.x ) * 0.5f;
499         }
500       }
501
502       updateProperties = true;
503     }
504
505     // Perform smoothing.
506     switch( mSmoothingMode )
507     {
508       case SMOOTHING_NONE:
509       case SMOOTHING_MULTI_TAP:
510       {
511         // No smoothing
512         // TODO: Old algorithm to be able to use multitap smoothing.
513         break;
514       }
515       case SMOOTHING_LAST_VALUE:
516       {
517         if( !frameInfo.justStarted )
518         {
519           if( !mSmoothingAmountOverridden )
520           {
521             // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
522             mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[0];
523           }
524           BlendPoints( frameGesture, mLastGesture, mSmoothingAmount );
525         }
526         break;
527       }
528     }
529
530     if( updateProperties )
531     {
532       // only update properties if event received
533       // set latest gesture to raw pan info with unchanged time
534       mPanning.Set( mInGesture & !frameInfo.justFinished );
535       mScreenPosition.Set( frameGesture.screen.position );
536       mScreenDisplacement.Set( frameGesture.screen.displacement );
537       mScreenVelocity.Set( frameGesture.screen.velocity );
538       mLocalPosition.Set( frameGesture.local.position );
539       mLocalDisplacement.Set( frameGesture.local.displacement );
540       mLocalVelocity.Set( frameGesture.local.velocity );
541     }
542
543     if( mProfiling )
544     {
545       mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( frameGesture.time, frameGesture.screen.position,
546           frameGesture.screen.displacement, frameGesture.screen.velocity, frameGesture.state ) );
547     }
548   }
549
550   mLastGesture = frameGesture;
551   mLastUnmodifiedGesture = unmodifiedGesture;
552
553   mInGesture &= ~frameInfo.justFinished;
554   if( mProfiling && frameInfo.justFinished )
555   {
556     mProfiling->PrintData();
557     mProfiling->ClearData();
558   }
559
560   return updateProperties;
561 }
562
563 const GesturePropertyBool& PanGesture::GetPanningProperty() const
564 {
565   return mPanning;
566 }
567
568 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
569 {
570   return mScreenPosition;
571 }
572
573 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
574 {
575   return mScreenVelocity;
576 }
577
578 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
579 {
580   return mScreenDisplacement;
581 }
582
583 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
584 {
585   return mLocalPosition;
586 }
587
588 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
589 {
590   return mLocalDisplacement;
591 }
592
593 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
594 {
595   return mLocalVelocity;
596 }
597
598 void PanGesture::SetPredictionMode(PredictionMode mode)
599 {
600   mPredictionMode = mode;
601 }
602
603 void PanGesture::SetPredictionAmount(unsigned int amount)
604 {
605   mPredictionAmount = amount;
606   mPredictionAmountOverridden = true;
607 }
608
609 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
610 {
611   mMaxPredictionAmount = amount;
612 }
613
614 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
615 {
616   mMinPredictionAmount = amount;
617 }
618
619 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
620 {
621   mPredictionAmountAdjustment = amount;
622 }
623
624 void PanGesture::SetSmoothingMode(SmoothingMode mode)
625 {
626   mSmoothingMode = mode;
627 }
628
629 void PanGesture::SetSmoothingAmount(float amount)
630 {
631   mSmoothingAmount = amount;
632   mSmoothingAmountOverridden = true;
633 }
634
635 void PanGesture::SetUseActualTimes( bool value )
636 {
637   mUseActualTimes = value;
638 }
639
640 void PanGesture::SetInterpolationTimeRange( int value )
641 {
642   mInterpolationTimeRange = value;
643 }
644
645 void PanGesture::SetScalarOnlyPredictionEnabled( bool value )
646 {
647   mScalarOnlyPredictionEnabled = value;
648 }
649
650 void PanGesture::SetTwoPointPredictionEnabled( bool value )
651 {
652   mTwoPointPredictionEnabled = value;
653 }
654
655 void PanGesture::SetTwoPointInterpolatePastTime( int value )
656 {
657   mTwoPointPastInterpolateTime = value;
658 }
659
660 void PanGesture::SetTwoPointVelocityBias( float value )
661 {
662   mTwoPointVelocityBias = value;
663 }
664
665 void PanGesture::SetTwoPointAccelerationBias( float value )
666 {
667   mTwoPointAccelerationBias = value;
668 }
669
670 void PanGesture::SetMultitapSmoothingRange( int value )
671 {
672   mMultiTapSmoothingRange = value;
673 }
674
675 void PanGesture::EnableProfiling()
676 {
677   if( !mProfiling )
678   {
679     mProfiling = new PanGestureProfiling();
680   }
681 }
682
683 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
684 {
685   mScreenPosition.Reset();
686   mScreenDisplacement.Reset();
687   mLocalPosition.Reset();
688   mLocalDisplacement.Reset();
689   mPanning.Reset();
690 }
691
692 PanGesture::PanGesture()
693 : mPanning(),
694   mGestures(),
695   mWritePosition( 0 ),
696   mReadPosition( 0 ),
697   mNotAtTarget( false ),
698   mInGesture( false ),
699   mPredictionAmountOverridden( false ),
700   mSmoothingAmountOverridden( false ),
701   mProfiling( NULL ),
702
703   // Set environment variable defaults:
704   mPredictionMode( DEFAULT_PREDICTION_MODE ),
705   mPredictionAmount( DEFAULT_PREDICTION_AMOUNT[0] ),
706   mCurrentPredictionAmount( DEFAULT_PREDICTION_AMOUNT[0] ),
707   mMaxPredictionAmount( DEFAULT_MAX_PREDICTION_AMOUNT ),
708   mMinPredictionAmount( DEFAULT_MIN_PREDICTION_AMOUNT ),
709   mPredictionAmountAdjustment( DEFAULT_PREDICTION_AMOUNT_ADJUSTMENT ),
710   mSmoothingMode( DEFAULT_SMOOTHING_MODE ),
711   mSmoothingAmount( DEFAULT_SMOOTHING_AMOUNT[0] ),
712   mUseActualTimes( DEFAULT_USE_ACTUAL_TIMES ),
713   mInterpolationTimeRange( DEFAULT_INTERPOLATION_TIME_RANGE ),
714   mScalarOnlyPredictionEnabled( DEFAULT_SCALAR_ONLY_PREDICTION_ENABLED ),
715   mTwoPointPredictionEnabled( DEFAULT_TWO_POINT_PREDICTION_ENABLED ),
716   mTwoPointPastInterpolateTime( DEFAULT_TWO_POINT_PAST_INTERPOLATE_TIME ),
717   mTwoPointVelocityBias( DEFAULT_TWO_POINT_VELOCITY_BIAS ),
718   mTwoPointAccelerationBias( DEFAULT_TWO_POINT_ACCELERATION_BIAS ),
719   mMultiTapSmoothingRange( DEFAULT_MULTITAP_SMOOTHING_RANGE )
720 {
721
722 }
723
724 // Prediction mode 2 related code and functions follow:
725
726 unsigned int PanGesture::ReadFrameEvents()
727 {
728   unsigned int eventsThisFrame;
729   // Copy the events into a linear buffer while holding the mutex.
730   // This is so the lock is not held while any processing is done.
731   Dali::Mutex::ScopedLock lock( mMutex );
732   for( eventsThisFrame = 0; mReadPosition != mWritePosition; ++eventsThisFrame )
733   {
734     mReadGestures[ eventsThisFrame ] = mGestures[ mReadPosition ];
735     ++mReadPosition;
736     mReadPosition %= PAN_GESTURE_HISTORY;
737   }
738   return eventsThisFrame;
739 }
740
741 // TODO: eventsThisFrame parameter can be removed if we use a smarter container.
742 bool PanGesture::InputRateConversion( PanInfo& rateConvertedGesture, unsigned int eventsThisFrame,
743     unsigned int currentFrameTime, unsigned int lastFrameTime, bool& justStarted, bool& justFinished )
744 {
745   // TODO: Lots of variables on the stack. Needs optimizing.
746   PanInfo readGesture;
747   PanInfo firstReadGesture;
748   unsigned int eventsKeptThisFrame = 0;
749
750   for( unsigned int readPosition = 0; readPosition < eventsThisFrame; ++readPosition )
751   {
752     // Copy the gesture first
753     readGesture = mReadGestures[ readPosition ];
754
755     if( mProfiling )
756     {
757       mProfiling->mRawData.push_back( PanGestureProfiling::Position( readGesture.time, readGesture.screen.position,
758           readGesture.screen.displacement, readGesture.screen.velocity, readGesture.state ) );
759     }
760
761     if( readGesture.state == Gesture::Started )
762     {
763       // Clear pan data.
764       mPanHistory.clear();
765       mPredictionHistory.clear();
766       mLastAcceleration.local = Vector2::ZERO;
767       mLastAcceleration.screen = Vector2::ZERO;
768       mLastInterpolatedAcceleration.local = Vector2::ZERO;
769       mLastInterpolatedAcceleration.screen = Vector2::ZERO;
770       mLastInitialAcceleration.local = Vector2::ZERO;
771       mLastInitialAcceleration.screen = Vector2::ZERO;
772       PanInfo startInfo;
773       mLastGesture = startInfo;
774       mLastSecondInterpolatedPoint = startInfo;
775       mLastPredictedPoint = startInfo;
776       mLastFrameReadGesture = startInfo;
777       rateConvertedGesture = startInfo;
778       firstReadGesture = readGesture;
779       eventsKeptThisFrame = 0;
780       mNotAtTarget = false;
781       justFinished = false;
782       justStarted = true;
783       mInGesture = true;
784
785       if( !mPredictionAmountOverridden )
786       {
787         // If the prediction amount has not been modified, default to the correct amount for this algorithm.
788         mPredictionAmount = DEFAULT_PREDICTION_AMOUNT[1];
789       }
790       mCurrentPredictionAmount = mPredictionAmount;
791     }
792     else
793     {
794       justFinished |= ( readGesture.state == Gesture::Finished || readGesture.state == Gesture::Cancelled );
795     }
796
797     rateConvertedGesture.screen.position += readGesture.screen.position;
798     rateConvertedGesture.local.position += readGesture.local.position;
799     rateConvertedGesture.screen.velocity += readGesture.screen.velocity;
800     rateConvertedGesture.local.velocity += readGesture.local.velocity;
801     rateConvertedGesture.screen.displacement += readGesture.screen.displacement;
802     rateConvertedGesture.local.displacement += readGesture.local.displacement;
803
804     ++eventsKeptThisFrame;
805   }
806
807   bool storeGesture = false;
808   if( eventsKeptThisFrame > 0 )
809   {
810     // Some events were read this frame.
811     if( eventsKeptThisFrame > 1 )
812     {
813       float eventDivisor( eventsKeptThisFrame );
814       rateConvertedGesture.screen.position /= eventDivisor;
815       rateConvertedGesture.local.position /= eventDivisor;
816       rateConvertedGesture.screen.velocity /= eventDivisor;
817       rateConvertedGesture.local.velocity /= eventDivisor;
818       rateConvertedGesture.screen.displacement /= eventDivisor;
819       rateConvertedGesture.local.displacement /= eventDivisor;
820
821       mTargetGesture = readGesture;
822       mNotAtTarget = true;
823     }
824     else
825     {
826       mNotAtTarget = false;
827     }
828
829     rateConvertedGesture.time = currentFrameTime;
830     storeGesture = true;
831   }
832   else
833   {
834     // We did not get any event this frame.
835     // If we just started (or aren't in a gesture), exit.
836     if( !mInGesture || justStarted )
837     {
838       // We cannot guess what the event could be as we have no other events to base the guess from.
839       return false;
840     }
841
842     // As we are currently in a gesture, we can estimate an event.
843     readGesture = mLastFrameReadGesture;
844     readGesture.time = currentFrameTime;
845
846     // Take the last event, halve the acceleration, and use that.
847     const float accelerationDegrade = 2.0f;
848     Vector2 degradedAccelerationLocal( mLastAcceleration.local /= accelerationDegrade );
849     Vector2 degradedAccelerationScreen( mLastAcceleration.screen /= accelerationDegrade );
850
851     float outputTimeGranularity( GetDivisibleTimeDifference( currentFrameTime, lastFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
852
853     readGesture.local.velocity = degradedAccelerationLocal * outputTimeGranularity;
854     readGesture.local.displacement = readGesture.local.velocity * outputTimeGranularity;
855     readGesture.local.position = mLastFrameReadGesture.local.position + readGesture.local.displacement;
856     readGesture.screen.velocity = degradedAccelerationScreen * outputTimeGranularity;
857     readGesture.screen.displacement = readGesture.screen.velocity * outputTimeGranularity;
858     readGesture.screen.position = mLastFrameReadGesture.screen.position + readGesture.screen.displacement;
859
860     rateConvertedGesture = readGesture;
861     eventsKeptThisFrame = 1;
862     storeGesture = true;
863   }
864
865   if( eventsKeptThisFrame > 0 )
866   {
867     // Store last read gesture.
868     readGesture.time = currentFrameTime;
869     mLastFrameReadGesture = readGesture;
870
871     if( eventsKeptThisFrame > 2 )
872     {
873       DALI_LOG_WARNING( "Got events this frame:%d (more than 2 will compromise result)\n", eventsKeptThisFrame );
874     }
875   }
876
877   if( storeGesture )
878   {
879     // Store final converted result.
880     mPanHistory.push_back( rateConvertedGesture );
881   }
882   return true;
883 }
884
885 bool PanGesture::InterpolatePoint( PanInfoHistory& history, unsigned int currentTime, unsigned int targetTime, unsigned int range,
886       PanInfo& outPoint, RelativeVectors& acceleration, int outputTimeGranularity, bool eraseUnused )
887 {
888   unsigned int maxHistoryTime = targetTime - range;
889   unsigned int tapsUsed = 0;
890   outPoint.time = targetTime;
891   float divisor = 0.0f;
892   float accelerationDivisor = 0.0f;
893   PanInfoHistoryIter historyBegin = history.begin();
894   PanInfoHistoryIter lastIt = history.end();
895   bool pointGenerated = false;
896   bool havePreviousPoint = false;
897   RelativeVectors newAcceleration;
898
899   // Iterate through point history to perform interpolation.
900   for( PanInfoHistoryIter it = historyBegin; it != history.end(); )
901   {
902     unsigned int gestureTime = it->time;
903
904     if( gestureTime < maxHistoryTime )
905     {
906       // Too far in the past, discard.
907       // Clean history as we go (if requested).
908       if( eraseUnused )
909       {
910         it = history.erase( it );
911       }
912       else
913       {
914         ++it;
915         continue;
916       }
917     }
918     else
919     {
920       float timeDelta( static_cast<float>( abs( int( targetTime - gestureTime ) ) ) );
921       // Handle low time deltas.
922       if( timeDelta < 1.0f )
923       {
924         timeDelta = 1.0f;
925       }
926
927       outPoint.local.position += it->local.position / timeDelta;
928       outPoint.screen.position += it->screen.position / timeDelta;
929       outPoint.local.velocity += it->local.velocity / timeDelta;
930       outPoint.screen.velocity += it->screen.velocity / timeDelta;
931       outPoint.local.displacement += it->local.displacement / timeDelta;
932       outPoint.screen.displacement += it->screen.displacement / timeDelta;
933
934       divisor += 1.0f / timeDelta;
935
936       // Acceleration requires a previous point.
937       if( havePreviousPoint )
938       {
939         // Time delta of input.
940         float timeDifference( GetDivisibleTimeDifference( it->time, lastIt->time, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
941
942         newAcceleration.local += ( ( it->local.velocity - lastIt->local.velocity ) / timeDifference ) / timeDelta;
943         newAcceleration.screen += ( ( it->screen.velocity - lastIt->screen.velocity ) / timeDifference ) / timeDelta;
944
945         accelerationDivisor += 1.0f / timeDelta;
946       }
947       else
948       {
949         havePreviousPoint = true;
950       }
951
952       tapsUsed++;
953       lastIt = it;
954       ++it;
955     }
956   }
957
958   // Divide results by their respective divisors.
959   if( tapsUsed > 0 )
960   {
961     if( divisor > 0.0f )
962     {
963       outPoint.local.position /= divisor;
964       outPoint.screen.position /= divisor;
965       outPoint.local.velocity /= divisor;
966       outPoint.screen.velocity /= divisor;
967       outPoint.local.displacement /= divisor;
968       outPoint.screen.displacement /= divisor;
969     }
970
971     if( tapsUsed > 1 )
972     {
973       if( accelerationDivisor > 0.0f )
974       {
975         newAcceleration.local /= accelerationDivisor;
976         newAcceleration.screen /= accelerationDivisor;
977       }
978
979       float accelerationSmoothing( ACCELERATION_SMOOTHING );
980       newAcceleration.local = ( acceleration.local * accelerationSmoothing ) + ( newAcceleration.local * ( 1.0f - accelerationSmoothing ) );
981       newAcceleration.screen = ( acceleration.screen * accelerationSmoothing ) + ( newAcceleration.screen * ( 1.0f - accelerationSmoothing ) );
982     }
983     else
984     {
985       // If we just started, last velocity was 0. So difference of zero to current velocity over time gives acceleration of the first point.
986       newAcceleration.local = outPoint.local.velocity / outputTimeGranularity;
987       newAcceleration.screen = outPoint.screen.velocity / outputTimeGranularity;
988     }
989     pointGenerated = true;
990   }
991
992   acceleration.local = newAcceleration.local;
993   acceleration.screen = newAcceleration.screen;
994   return pointGenerated;
995 }
996
997 float PanGesture::GetDivisibleTimeDifference( int timeA, int timeB, float minimumDelta, float overrideDifference )
998 {
999   float timeDifference( overrideDifference );
1000   if( mUseActualTimes )
1001   {
1002     timeDifference = static_cast<float>( abs( timeA - timeB ) );
1003     if( timeDifference < minimumDelta )
1004     {
1005       timeDifference = minimumDelta;
1006     }
1007   }
1008   return timeDifference;
1009 }
1010
1011 void PanGesture::LimitAccelerationChange( RelativeVectors& currentAcceleration, RelativeVectors& lastAcceleration, float changeLimit )
1012 {
1013   // We don't use the float parameter version of clamp here, as that will create the capping vectors twice in total.
1014   Vector2 capMinimum( -changeLimit, -changeLimit );
1015   Vector2 capMaximum( changeLimit, changeLimit );
1016   Vector2 accelerationDeltaLocal( currentAcceleration.local - lastAcceleration.local );
1017   Vector2 accelerationDeltaScreen( currentAcceleration.screen - lastAcceleration.screen );
1018   accelerationDeltaLocal.Clamp( capMinimum, capMaximum );
1019   accelerationDeltaScreen.Clamp( capMinimum, capMaximum );
1020   currentAcceleration.local = lastAcceleration.local + accelerationDeltaLocal;
1021   currentAcceleration.screen = lastAcceleration.screen + accelerationDeltaScreen;
1022 }
1023
1024 void PanGesture::PredictionMode2( PanInfo& startPoint, RelativeVectors& accelerationToUse,
1025     PanInfo& predictedPoint, unsigned int currentFrameTime, unsigned int previousFrameTime, bool noPreviousData )
1026 {
1027   // Do the prediction (based on mode).
1028   if( mScalarOnlyPredictionEnabled )
1029   {
1030     // We are doing scalar based prediction.
1031     // This divisor is to help tuning by giving the scalar only result
1032     // a similar prediction amount to the integrated result.
1033     float scalarVelocityMultiplier = static_cast<float>( mCurrentPredictionAmount ) / 1.364f;
1034     predictedPoint.local.position = startPoint.local.position + ( startPoint.local.velocity * scalarVelocityMultiplier );
1035     predictedPoint.screen.position = startPoint.screen.position + ( startPoint.screen.velocity * scalarVelocityMultiplier );
1036   }
1037   else
1038   {
1039     // We are doing integration based prediction.
1040     float predictionDelta( mCurrentPredictionAmount );
1041
1042     predictedPoint.local.position = startPoint.local.position + ( startPoint.local.velocity * predictionDelta ) +
1043         ( accelerationToUse.local * ( predictionDelta * predictionDelta * 0.5f ) );
1044     predictedPoint.screen.position = startPoint.screen.position + ( startPoint.screen.velocity * predictionDelta ) +
1045         ( accelerationToUse.screen * ( predictionDelta * predictionDelta * 0.5f ) );
1046   }
1047
1048   // Calculate remaining gesture data from the result.
1049   float timeDifference( GetDivisibleTimeDifference( currentFrameTime, previousFrameTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1050   if( noPreviousData )
1051   {
1052     predictedPoint.local.displacement = predictedPoint.local.position - startPoint.local.position;
1053     predictedPoint.screen.displacement = predictedPoint.screen.position - startPoint.screen.position;
1054   }
1055   else
1056   {
1057     predictedPoint.local.displacement = predictedPoint.local.position - mLastPredictedPoint.local.position;
1058     predictedPoint.screen.displacement = predictedPoint.screen.position - mLastPredictedPoint.screen.position;
1059   }
1060   predictedPoint.local.velocity = predictedPoint.local.displacement / timeDifference;
1061   predictedPoint.screen.velocity = predictedPoint.screen.displacement / timeDifference;
1062
1063   // TODO: Experimental - not used at run time. Left in code for reference only.
1064   if( TEST_TUNE_ENABLE_OVERSHOOT_PROTECTION )
1065   {
1066     // Overshoot protection
1067     if( !noPreviousData )
1068     {
1069       if( ( mLastPredictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0 )
1070         || ( mLastPredictedPoint.local.velocity.x < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.x > Math::MACHINE_EPSILON_0 ) )
1071       {
1072         predictedPoint.local.position.x = mLastPredictedPoint.local.position.x;
1073         predictedPoint.screen.position.x = mLastPredictedPoint.screen.position.x;
1074         mPredictionHistory.clear();
1075       }
1076       if( ( mLastPredictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0 )
1077         || ( mLastPredictedPoint.local.velocity.y < Math::MACHINE_EPSILON_0 && predictedPoint.local.velocity.y > Math::MACHINE_EPSILON_0 ) )
1078       {
1079         predictedPoint.local.position.y = mLastPredictedPoint.local.position.y;
1080         predictedPoint.screen.position.y = mLastPredictedPoint.screen.position.y;
1081         mPredictionHistory.clear();
1082       }
1083     }
1084   }
1085
1086   predictedPoint.time = currentFrameTime;
1087   mLastPredictedPoint = predictedPoint;
1088 }
1089
1090 // TODO: This needs a better name! It is called this instead of prediction mode 2 because:
1091 // 1) It is the entire workflow, not just prediction.
1092 // 2) To make it less confusing as there is a function that does prediction alone called PerformPredictionMode2.
1093 // 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.
1094 // At the moment, the differences between the inputs & outputs of these different functions prevent that, but this can be resolved.
1095 bool PanGesture::NewAlgorithm( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
1096 {
1097   if( !mInGesture )
1098   {
1099     // clear current pan history
1100     mPanHistory.clear();
1101     mPredictionHistory.clear();
1102   }
1103
1104   /*#########################################################################################
1105     #### Read in all gestures received this frame first (holding a lock for a short time)
1106     #########################################################################################*/
1107
1108   unsigned int eventsThisFrame = ReadFrameEvents();
1109
1110   /*#########################################################################################
1111     #### Perform input rate-conversion on all gestures received this frame.
1112     #### This also populates the pan history.
1113     #########################################################################################*/
1114
1115   bool justStarted = false;
1116   bool justFinished = false;
1117   PanInfo rateConvertedGesture;
1118   if( !InputRateConversion( rateConvertedGesture, eventsThisFrame, nextVSyncTime, lastVSyncTime, justStarted, justFinished ) )
1119   {
1120     // There's nothing we can do with the input, exit.
1121     return false;
1122   }
1123
1124   /*#########################################################################################
1125     #### If we are in gesture, Get first interpolated point with: target time = current time
1126     #########################################################################################*/
1127
1128   bool performUpdate = false;
1129   RelativeVectors currentAcceleration;
1130   currentAcceleration.local = mLastInitialAcceleration.local;
1131   currentAcceleration.screen = mLastInitialAcceleration.screen;
1132
1133   if( mInGesture || justStarted )
1134   {
1135     // Get first interpolated point.
1136     // TODO: Erase time should be maximum of both interpolated point ranges in past.
1137     PanInfo targetPoint;
1138     float outputTimeGranularity( GetDivisibleTimeDifference( nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1139     bool pointGenerated = InterpolatePoint( mPanHistory, nextVSyncTime, nextVSyncTime, mInterpolationTimeRange,
1140         targetPoint, currentAcceleration, outputTimeGranularity, true );
1141     if( pointGenerated )
1142     {
1143       mLastInitialAcceleration.local = currentAcceleration.local;
1144       mLastInitialAcceleration.screen = currentAcceleration.screen;
1145       performUpdate = true;
1146     }
1147     else
1148     {
1149       targetPoint = rateConvertedGesture;
1150       currentAcceleration.local = mLastInitialAcceleration.local;
1151       currentAcceleration.screen = mLastInitialAcceleration.screen;
1152       // TODO: Potentially do something to substitute lack of generated point (and perform update).
1153     }
1154
1155     /*#########################################################################################
1156       #### Limit the change of acceleration of the first interpolated point since last time
1157       #########################################################################################*/
1158
1159     if( !justStarted )
1160     {
1161       LimitAccelerationChange( currentAcceleration, mLastAcceleration, ACCELERATION_CAP );
1162     }
1163     mLastAcceleration.local = currentAcceleration.local;
1164     mLastAcceleration.screen = currentAcceleration.screen;
1165
1166     /*#########################################################################################
1167       #### Get second interpolated point, and blend the resultant velocity and acceleration (optional)
1168       #########################################################################################*/
1169
1170     PanInfo outPoint;
1171     RelativeVectors interpolatedAcceleration;
1172     if( mTwoPointPredictionEnabled )
1173     {
1174       // Get second interpolated point with target time = current time - past interpolate time.
1175       unsigned int pastInterpolateTime = nextVSyncTime - mTwoPointPastInterpolateTime;
1176       PanInfo outPoint;
1177       RelativeVectors interpolatedAcceleration;
1178       interpolatedAcceleration.local = mLastInterpolatedAcceleration.local;
1179       interpolatedAcceleration.screen = mLastInterpolatedAcceleration.screen;
1180       if( !InterpolatePoint( mPanHistory, nextVSyncTime, pastInterpolateTime, mTwoPointPastInterpolateTime,
1181           outPoint, interpolatedAcceleration, outputTimeGranularity, false ) )
1182       {
1183         if( justStarted )
1184         {
1185           outPoint = targetPoint;
1186         }
1187         else
1188         {
1189           outPoint = mLastSecondInterpolatedPoint;
1190         }
1191       }
1192       mLastInterpolatedAcceleration.local = interpolatedAcceleration.local;
1193       mLastInterpolatedAcceleration.screen = interpolatedAcceleration.screen;
1194       mLastSecondInterpolatedPoint = outPoint;
1195
1196       // Combine the first interpolated point and the second interpolated point.
1197       // by mixing them with the configured amount. This is done for acceleration and velocity.
1198       // It could be optionally done for position too, but this typically is worse as it means we have to predict further ahead.
1199       float currentVelocityMultiplier( 1.0f - mTwoPointVelocityBias );
1200       float lastVelocityMultiplier( mTwoPointVelocityBias );
1201       targetPoint.local.velocity = ( outPoint.local.velocity * lastVelocityMultiplier ) + ( targetPoint.local.velocity * currentVelocityMultiplier );
1202       targetPoint.screen.velocity = ( outPoint.screen.velocity * lastVelocityMultiplier ) + ( targetPoint.screen.velocity * currentVelocityMultiplier );
1203       float currentAccelerationMultiplier( 1.0f - mTwoPointAccelerationBias );
1204       float lastAccelerationMultiplier( mTwoPointAccelerationBias );
1205       currentAcceleration.local = ( interpolatedAcceleration.local * lastAccelerationMultiplier ) + ( currentAcceleration.local * currentAccelerationMultiplier );
1206       currentAcceleration.screen = ( interpolatedAcceleration.screen * lastAccelerationMultiplier ) + ( currentAcceleration.screen * currentAccelerationMultiplier );
1207     }
1208
1209     /*#########################################################################################
1210       #### Perform prediction
1211       #########################################################################################*/
1212
1213     PanInfo predictedPoint;
1214     PredictionMode2( targetPoint, currentAcceleration, predictedPoint, nextVSyncTime, lastVSyncTime, justStarted );
1215     targetPoint = predictedPoint;
1216
1217     /*#########################################################################################
1218       #### Smoothing
1219       #########################################################################################*/
1220
1221     // If we are using multi-tap smoothing, keep a history of predicted results.
1222     if( mSmoothingMode == SMOOTHING_MULTI_TAP )
1223     {
1224       mPredictionHistory.push_back( targetPoint );
1225     }
1226
1227     if( !justStarted )
1228     {
1229       float outputTimeGranularity( GetDivisibleTimeDifference( nextVSyncTime, lastVSyncTime, 1.0f, OUTPUT_TIME_DIFFERENCE ) );
1230       if( mSmoothingMode == SMOOTHING_MULTI_TAP )
1231       {
1232         // Perform Multi-tap Smoothing.
1233         RelativeVectors blank;
1234         InterpolatePoint( mPredictionHistory, nextVSyncTime, nextVSyncTime, mMultiTapSmoothingRange,
1235             targetPoint, blank, outputTimeGranularity, true );
1236       }
1237       else
1238       {
1239         // Perform Single-tap Smoothing.
1240         if( !mSmoothingAmountOverridden )
1241         {
1242           // If the smoothing amount has not been modified, default to the correct amount for this algorithm.
1243           mSmoothingAmount = DEFAULT_SMOOTHING_AMOUNT[1];
1244         }
1245         BlendPoints( targetPoint, mLastGesture, mSmoothingAmount );
1246       }
1247
1248       /*#########################################################################################
1249         #### Finalize other point data (from position)
1250         #########################################################################################*/
1251
1252       targetPoint.local.displacement = targetPoint.local.position - mLastGesture.local.position;
1253       targetPoint.local.velocity = targetPoint.local.displacement / outputTimeGranularity;
1254       targetPoint.screen.displacement = targetPoint.screen.position - mLastGesture.screen.position;
1255       targetPoint.screen.velocity = targetPoint.screen.displacement / outputTimeGranularity;
1256     }
1257
1258     /*#########################################################################################
1259       #### Send out the new point, by setting the properties
1260       #### (Constraints will automatically react to this)
1261       #########################################################################################*/
1262
1263     if( performUpdate )
1264     {
1265       mPanning.Set( mInGesture & !justFinished );
1266       mScreenPosition.Set( targetPoint.screen.position );
1267       mScreenDisplacement.Set( targetPoint.screen.displacement );
1268       mScreenVelocity.Set( targetPoint.screen.velocity );
1269       mLocalPosition.Set( targetPoint.local.position );
1270       mLocalDisplacement.Set( targetPoint.local.displacement );
1271       mLocalVelocity.Set( targetPoint.local.velocity );
1272
1273       mLastGesture = targetPoint;
1274
1275       if( mProfiling )
1276       {
1277         mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( targetPoint.time, targetPoint.screen.position,
1278             targetPoint.screen.displacement, targetPoint.screen.velocity, targetPoint.state ) );
1279       }
1280     }
1281   }
1282
1283   mInGesture &= ~justFinished;
1284
1285   return performUpdate;
1286 }
1287
1288
1289 } // namespace SceneGraph
1290
1291 } // namespace Internal
1292
1293 } // namespace Dali