Merge remote-tracking branch 'origin/tizen' into new_text
[platform/core/uifw/dali-core.git] / dali / internal / update / gestures / scene-graph-pan-gesture.cpp
1 /*
2  * Copyright (c) 2014 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
27 namespace Dali
28 {
29
30 namespace Internal
31 {
32
33 namespace SceneGraph
34 {
35 namespace
36 {
37 const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm
38 const float ACCELERATION_THRESHOLD = 0.1f; ///< minimum pan velocity change to trigger dynamic change of prediction amount
39 const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 0; ///< how much to interpolate pan position and displacement from last vsync time (in milliseconds)
40 const unsigned int DEFAULT_MAX_PREDICTION_INTERPOLATION = 32; ///< the upper bound of the range to clamp the prediction interpolation
41 const unsigned int DEFAULT_MIN_PREDICTION_INTERPOLATION = 0; ///< the lower bound of the range to clamp the prediction interpolation
42 const unsigned int DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT = 4; ///< the amount of prediction interpolation to adjust (in milliseconds) each time when pan velocity changes
43 const float DEFAULT_SMOOTHING_AMOUNT = 1.0f; ///< how much to interpolate pan position and displacement from last vsync time
44 } // unnamed namespace
45
46 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
47 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_1 + 1;
48
49 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
50 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_LAST_VALUE + 1;
51
52 PanGesture* PanGesture::New()
53 {
54   return new PanGesture();
55 }
56
57 PanGesture::~PanGesture()
58 {
59   delete mProfiling;
60 }
61
62 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
63 {
64   mGestures[ mWritePosition ] = gesture;
65
66   // Update our write position.
67   ++mWritePosition;
68   mWritePosition %= PAN_GESTURE_HISTORY;
69 }
70
71 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
72 {
73   PanInfoHistoryConstIter endIter = panHistory.end();
74   PanInfoHistoryIter iter = panHistory.begin();
75   while( iter != endIter && panHistory.size() > minEvents)
76   {
77     PanInfo currentGesture = *iter;
78     if( currentTime < currentGesture.time + maxAge )
79     {
80       break;
81     }
82     iter = panHistory.erase(iter);
83     endIter = panHistory.end();
84   }
85
86   // dont want more than 5 previous predictions for smoothing
87   iter = mPredictionHistory.begin();
88   while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
89   {
90     iter = mPredictionHistory.erase(iter);
91   }
92 }
93
94 void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
95 {
96   RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
97   size_t panHistorySize = panHistory.size();
98   if( panHistorySize == 0 )
99   {
100     // cant do any prediction without a history
101     return;
102   }
103
104   PanInfoHistoryConstIter endIter = panHistory.end();
105   PanInfoHistoryIter iter = panHistory.begin();
106   Vector2 screenVelocity = gestureOut.screen.velocity;
107   Vector2 localVelocity = gestureOut.local.velocity;
108   Vector2 screenDisplacement = gestureOut.screen.displacement;
109   Vector2 localDisplacement = gestureOut.local.displacement;
110
111   bool havePreviousAcceleration = false;
112   bool previousVelocity = false;
113   float previousAccel = 0.0f;
114   unsigned int lastTime(0);
115
116   unsigned int interpolationTime = lastVSyncTime + mCurrentPredictionAmount;
117   if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mCurrentPredictionAmount)
118   {
119     interpolationTime -= gestureOut.time;
120   }
121   else
122   {
123     interpolationTime = 0u;
124   }
125
126   while( iter != endIter )
127   {
128     PanInfo currentGesture = *iter;
129     if( !previousVelocity )
130     {
131       // not yet set a previous velocity
132       screenVelocity = currentGesture.screen.velocity;
133       previousVelocity = true;
134       lastTime = currentGesture.time;
135       ++iter;
136       continue;
137     }
138     float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
139     float velMag = currentGesture.screen.velocity.Length();
140     float velDiff = velMag - screenVelocity.Length();
141     float acceleration = 0.0f;
142
143     float time(0.f);
144     if (currentGesture.time > lastTime) // Guard against invalid timestamps
145     {
146       time = static_cast<float>( currentGesture.time - lastTime );
147     }
148     if( time > Math::MACHINE_EPSILON_1 )
149     {
150       acceleration = velDiff / time;
151     }
152
153     float newVelMag = 0.0f;
154     int currentInterpolation = interpolationTime;
155     if( !havePreviousAcceleration )
156     {
157       newVelMag =  velMag;
158       havePreviousAcceleration = true;
159     }
160     else
161     {
162       newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
163     }
164     float velMod = 1.0f;
165     if( velMag > Math::MACHINE_EPSILON_1 )
166     {
167       velMod = newVelMag / velMag;
168     }
169     gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
170     gestureOut.local.velocity = currentGesture.local.velocity * velMod;
171     screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
172     localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
173     screenVelocity = currentGesture.screen.velocity;
174     localVelocity = currentGesture.local.velocity;
175     previousAccel = acceleration;
176     ++iter;
177   }
178   // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
179   // add interpolated distance and position to current
180   // work out interpolated velocity
181   gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
182   gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
183   gestureOut.screen.displacement = screenDisplacement;
184   gestureOut.local.displacement = localDisplacement;
185   gestureOut.time += interpolationTime;
186 }
187
188 void PanGesture::SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
189 {
190   if( !justStarted )
191   {
192     gestureOut.screen.position -= (gestureOut.screen.position - mLastGesture.screen.position) * 0.5f * (1.0f - mSmoothingAmount);
193     gestureOut.local.position -= (gestureOut.local.position - mLastGesture.local.position) * 0.5f * (1.0f - mSmoothingAmount);
194     // make current displacement relative to previous update-frame now.
195     gestureOut.screen.displacement = gestureOut.screen.position - mLastGesture.screen.position;
196     gestureOut.local.displacement = gestureOut.local.position - mLastGesture.local.position;
197     // calculate velocity relative to previous update-frame
198     unsigned int timeDiff( gestureOut.time - mLastGesture.time );
199     gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
200     gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
201   }
202 }
203
204 void PanGesture::SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
205 {
206   // push back result
207   mPredictionHistory.push_back(gestureOut);
208
209   // now smooth current pan event
210   PanInfoHistoryConstIter endIter = mPredictionHistory.end() - 1;
211   PanInfoHistoryIter iter = mPredictionHistory.begin();
212
213   float distanceMod = 1.0f;
214   float weight = 0.8f;
215   while( iter != endIter )
216   {
217     PanInfo currentGesture = *iter;
218     float newDistanceMod = currentGesture.screen.displacement.Length() / gestureOut.screen.displacement.Length();
219     distanceMod = ((distanceMod * weight) + (newDistanceMod * (1.0f - weight)));
220     weight -= 0.15f;
221     ++iter;
222   }
223   gestureOut.screen.position -= gestureOut.screen.displacement;
224   gestureOut.local.position -= gestureOut.local.displacement;
225   gestureOut.screen.displacement *= distanceMod;
226   gestureOut.local.displacement *= distanceMod;
227   gestureOut.screen.position += gestureOut.screen.displacement;
228   gestureOut.local.position += gestureOut.local.displacement;
229 }
230
231 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
232 {
233   if( !mInGesture )
234   {
235     // clear current pan history
236     mPanHistory.clear();
237     mPredictionHistory.clear();
238   }
239
240   // create an event for this frame
241   bool justStarted ( false );
242   bool justFinished ( false );
243   bool eventFound( false );
244
245   float acceleration = 0.0f;
246
247   // Not going through array from the beginning, using it as a circular buffer and only using unread
248   // values.
249   int eventsThisFrame = 0;
250
251   // create PanInfo to pass into prediction method
252   mLastEventGesture = mEventGesture;
253   mLastGesture = mLatestGesture;
254   // add new gestures and work out one full gesture for the frame
255   unsigned int previousReadPosition = 0;
256   while(mReadPosition != mWritePosition)
257   {
258     // Copy the gesture first
259     PanInfo currentGesture(mGestures[mReadPosition]);
260
261     if( mProfiling )
262     {
263       mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position, currentGesture.screen.displacement, currentGesture.screen.velocity, currentGesture.state ) );
264     }
265     mEventGesture.local.position = currentGesture.local.position;
266     mEventGesture.local.velocity = currentGesture.local.velocity;
267     mEventGesture.screen.position = currentGesture.screen.position;
268     mEventGesture.screen.velocity = currentGesture.screen.velocity;
269
270     if(eventsThisFrame > 0)
271     {
272       acceleration = currentGesture.screen.velocity.Length() - mGestures[previousReadPosition].screen.velocity.Length();
273     }
274
275     if( !eventFound )
276     {
277       mEventGesture.local.displacement = currentGesture.local.displacement;
278       mEventGesture.screen.displacement = currentGesture.screen.displacement;
279     }
280     else
281     {
282       mEventGesture.local.displacement += currentGesture.local.displacement;
283       mEventGesture.screen.displacement += currentGesture.screen.displacement;
284     }
285     eventFound = true;
286     mEventGesture.time = currentGesture.time;
287
288     // add event to history
289     mPanHistory.push_back(currentGesture);
290     if( currentGesture.state == Gesture::Started )
291     {
292       justStarted = true;
293       // clear just finished as we have started new pan
294       justFinished = false;
295     }
296     justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled);
297
298     // Update our read position.
299     previousReadPosition = mReadPosition;
300     ++eventsThisFrame;
301     ++mReadPosition;
302     mReadPosition %= PAN_GESTURE_HISTORY;
303   }
304   mLatestGesture = mEventGesture;
305
306   mInGesture |= justStarted;
307
308   bool updateProperties = false;
309
310   if ( mInGesture )
311   {
312     if( mProfiling )
313     {
314       mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, mEventGesture.screen.position, mEventGesture.screen.displacement, mEventGesture.screen.velocity, mEventGesture.state ) );
315     }
316
317     switch( mPredictionMode )
318     {
319       case PREDICTION_NONE:
320       {
321         updateProperties = eventFound;
322         // dont want event time
323         unsigned int time = mLastGesture.time;
324         mLastGesture = mLastEventGesture;
325         mLastGesture.time = time;
326         mLatestGesture.time = lastVSyncTime;
327         break;
328       }
329       case PREDICTION_1:
330       {
331         // Dynamically change the prediction amount according to the pan velocity acceleration.
332         if(!justStarted)
333         {
334           if(eventsThisFrame <= 1)
335           {
336             acceleration = mEventGesture.screen.velocity.Length() - mLastEventGesture.screen.velocity.Length();
337           }
338
339           // Ignore tiny velocity fluctuation to avoid unnecessary prediction amount change
340           if(fabsf(acceleration) > ACCELERATION_THRESHOLD)
341           {
342             mCurrentPredictionAmount += mPredictionAmountAdjustment * (acceleration > Math::MACHINE_EPSILON_0 ? 1.0f : -1.0f);
343             if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
344             {
345               mCurrentPredictionAmount = 0;
346             }
347           }
348         }
349         else
350         {
351           mCurrentPredictionAmount = mPredictionAmount; // Reset the prediction amount for each new gesture
352         }
353
354         mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
355
356         // Calculate the delta of positions before the prediction
357         Vector2 deltaPosition = mLatestGesture.screen.position - mLastEventGesture.screen.position;
358
359         // Make latest gesture equal to current gesture before interpolation
360         PredictiveAlgorithm1(eventsThisFrame, mLatestGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
361
362         // Calculate the delta of positions after the prediction.
363         Vector2 deltaPredictedPosition = mLatestGesture.screen.position - mLastGesture.screen.position;
364
365         // If the change in the prediction has a different sign than the change in the actual position,
366         // there is overshot (i.e. the current prediction is too large). Return the previous prediction
367         // to give the user's finger a chance to catch up with where we have panned to.
368         bool overshotXAxis = false;
369         bool overshotYAxis = false;
370         if( (deltaPosition.x > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x < Math::MACHINE_EPSILON_0 )
371          || (deltaPosition.x < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.x > Math::MACHINE_EPSILON_0 ) )
372         {
373           overshotXAxis = true;
374           mLatestGesture.screen.position.x = mLastGesture.screen.position.x;
375         }
376
377         if( (deltaPosition.y > Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y < Math::MACHINE_EPSILON_0 )
378          || (deltaPosition.y < Math::MACHINE_EPSILON_0 && deltaPredictedPosition.y > Math::MACHINE_EPSILON_0 ) )
379         {
380           overshotYAxis = true;
381           mLatestGesture.screen.position.y = mLastGesture.screen.position.y;
382         }
383
384         // If there is overshot in one axis, reduce the possible overshot in the other axis,
385         // and reduce the prediction amount so that it doesn't overshoot as easily next time.
386         if(overshotXAxis || overshotYAxis)
387         {
388           mCurrentPredictionAmount -= mPredictionAmountAdjustment;
389           if(mCurrentPredictionAmount > mMaxPredictionAmount + mPredictionAmountAdjustment) // Guard against unsigned int overflow
390           {
391             mCurrentPredictionAmount = 0;
392           }
393           mCurrentPredictionAmount = std::max(mMinPredictionAmount, std::min(mCurrentPredictionAmount, mMaxPredictionAmount));
394
395           if(overshotXAxis && !overshotYAxis)
396           {
397             mLatestGesture.screen.position.y = (mLastGesture.screen.position.y + mLatestGesture.screen.position.y) * 0.5f;
398           }
399
400           if(overshotYAxis && !overshotXAxis)
401           {
402             mLatestGesture.screen.position.x = (mLastGesture.screen.position.x + mLatestGesture.screen.position.x) * 0.5f;
403           }
404         }
405
406         updateProperties = true;
407         break;
408       }
409     }
410
411     switch( mSmoothingMode )
412     {
413       case SMOOTHING_NONE:
414       {
415         // no smoothing
416         break;
417       }
418       case SMOOTHING_LAST_VALUE:
419       {
420         SmoothingAlgorithm1(justStarted, mLatestGesture, lastVSyncTime);
421         break;
422       }
423     }
424
425     if( updateProperties )
426     {
427       // only update properties if event received
428       // set latest gesture to raw pan info with unchanged time
429       mPanning.Set( mInGesture & !justFinished );
430       mScreenPosition.Set( mLatestGesture.screen.position );
431       mScreenDisplacement.Set( mLatestGesture.screen.displacement );
432       mScreenVelocity.Set( mLatestGesture.screen.velocity );
433       mLocalPosition.Set( mLatestGesture.local.position );
434       mLocalDisplacement.Set( mLatestGesture.local.displacement );
435       mLocalVelocity.Set( mLatestGesture.local.velocity );
436     }
437
438     if( mProfiling )
439     {
440       mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( mLatestGesture.time, mLatestGesture.screen.position, mLatestGesture.screen.displacement, mLatestGesture.screen.velocity, mLatestGesture.state ) );
441     }
442   }
443
444   mInGesture &= ~justFinished;
445
446   if( mProfiling && justFinished )
447   {
448     mProfiling->PrintData();
449     mProfiling->ClearData();
450   }
451
452   return updateProperties;
453 }
454
455 const GesturePropertyBool& PanGesture::GetPanningProperty() const
456 {
457   return mPanning;
458 }
459
460 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
461 {
462   return mScreenPosition;
463 }
464
465 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
466 {
467   return mScreenVelocity;
468 }
469
470 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
471 {
472   return mScreenDisplacement;
473 }
474
475 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
476 {
477   return mLocalPosition;
478 }
479
480 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
481 {
482   return mLocalDisplacement;
483 }
484
485 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
486 {
487   return mLocalVelocity;
488 }
489
490 void PanGesture::SetPredictionMode(PredictionMode mode)
491 {
492   mPredictionMode = mode;
493 }
494
495 void PanGesture::SetPredictionAmount(unsigned int amount)
496 {
497   mPredictionAmount = amount;
498 }
499
500 void PanGesture::SetMaximumPredictionAmount(unsigned int amount)
501 {
502   mMaxPredictionAmount = amount;
503 }
504
505 void PanGesture::SetMinimumPredictionAmount(unsigned int amount)
506 {
507   mMinPredictionAmount = amount;
508 }
509
510 void PanGesture::SetPredictionAmountAdjustment(unsigned int amount)
511 {
512   mPredictionAmountAdjustment = amount;
513 }
514
515 void PanGesture::SetSmoothingMode(SmoothingMode mode)
516 {
517   mSmoothingMode = mode;
518 }
519
520 void PanGesture::SetSmoothingAmount(float amount)
521 {
522   mSmoothingAmount = amount;
523 }
524
525 void PanGesture::EnableProfiling()
526 {
527   if( !mProfiling )
528   {
529     mProfiling = new PanGestureProfiling();
530   }
531 }
532
533 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
534 {
535   mScreenPosition.Reset();
536   mScreenDisplacement.Reset();
537   mLocalPosition.Reset();
538   mLocalDisplacement.Reset();
539   mPanning.Reset();
540 }
541
542 PanGesture::PanGesture()
543 : mGestures(),
544   mWritePosition( 0 ),
545   mReadPosition( 0 ),
546   mInGesture( false ),
547   mPredictionMode(DEFAULT_PREDICTION_MODE),
548   mPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
549   mCurrentPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
550   mMaxPredictionAmount(DEFAULT_MAX_PREDICTION_INTERPOLATION),
551   mMinPredictionAmount(DEFAULT_MIN_PREDICTION_INTERPOLATION),
552   mPredictionAmountAdjustment(DEFAULT_PREDICTION_INTERPOLATION_ADJUSTMENT),
553   mSmoothingMode(DEFAULT_SMOOTHING_MODE),
554   mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT),
555   mProfiling( NULL )
556 {
557 }
558
559 } // namespace SceneGraph
560
561 } // namespace Internal
562
563 } // namespace Dali