Merge "Guard against invalid PanGesture timestamps" into tizen
[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
23 // INTERNAL INCLUDES
24 #include <dali/internal/update/gestures/pan-gesture-profiling.h>
25
26 namespace Dali
27 {
28
29 namespace Internal
30 {
31
32 namespace SceneGraph
33 {
34 namespace
35 {
36 const int MAX_GESTURE_AGE = 50; ///< maximum age of a gesture before disallowing its use in algorithm
37 const unsigned int DEFAULT_PREDICTION_INTERPOLATION = 0; ///< how much to interpolate pan position and displacement from last vsync time
38 const float DEFAULT_SMOOTHING_AMOUNT = 1.0f; ///< how much to interpolate pan position and displacement from last vsync time
39 } // unnamed namespace
40
41 const PanGesture::PredictionMode PanGesture::DEFAULT_PREDICTION_MODE = PanGesture::PREDICTION_NONE;
42 const int PanGesture::NUM_PREDICTION_MODES = PanGesture::PREDICTION_1 + 1;
43
44 const PanGesture::SmoothingMode PanGesture::DEFAULT_SMOOTHING_MODE = PanGesture::SMOOTHING_LAST_VALUE;
45 const int PanGesture::NUM_SMOOTHING_MODES = PanGesture::SMOOTHING_LAST_VALUE + 1;
46
47 PanGesture* PanGesture::New()
48 {
49   return new PanGesture();
50 }
51
52 PanGesture::~PanGesture()
53 {
54   delete mProfiling;
55 }
56
57 void PanGesture::AddGesture( const Dali::PanGesture& gesture )
58 {
59   mGestures[ mWritePosition ] = gesture;
60
61   // Update our write position.
62   ++mWritePosition;
63   mWritePosition %= PAN_GESTURE_HISTORY;
64 }
65
66 void PanGesture::RemoveOldHistory(PanInfoHistory& panHistory, unsigned int currentTime, unsigned int maxAge, unsigned int minEvents)
67 {
68   PanInfoHistoryConstIter endIter = panHistory.end();
69   PanInfoHistoryIter iter = panHistory.begin();
70   while( iter != endIter && panHistory.size() > minEvents)
71   {
72     PanInfo currentGesture = *iter;
73     if( currentTime < currentGesture.time + maxAge )
74     {
75       break;
76     }
77     iter = panHistory.erase(iter);
78     endIter = panHistory.end();
79   }
80
81   // dont want more than 5 previous predictions for smoothing
82   iter = mPredictionHistory.begin();
83   while( mPredictionHistory.size() > 1 && iter != mPredictionHistory.end() )
84   {
85     iter = mPredictionHistory.erase(iter);
86   }
87 }
88
89 void PanGesture::PredictiveAlgorithm1(int eventsThisFrame, PanInfo& gestureOut, PanInfoHistory& panHistory, unsigned int lastVSyncTime, unsigned int nextVSyncTime)
90 {
91   RemoveOldHistory(panHistory, lastVSyncTime, MAX_GESTURE_AGE, 0);
92   size_t panHistorySize = panHistory.size();
93   if( panHistorySize == 0 )
94   {
95     // cant do any prediction without a history
96     return;
97   }
98
99   PanInfoHistoryConstIter endIter = panHistory.end();
100   PanInfoHistoryIter iter = panHistory.begin();
101   Vector2 screenVelocity = gestureOut.screen.velocity;
102   Vector2 localVelocity = gestureOut.local.velocity;
103   Vector2 screenDisplacement = gestureOut.screen.displacement;
104   Vector2 localDisplacement = gestureOut.local.displacement;
105
106   bool havePreviousAcceleration = false;
107   bool previousVelocity = false;
108   float previousAccel = 0.0f;
109   unsigned int lastTime(0);
110
111   unsigned int interpolationTime = lastVSyncTime + mPredictionAmount;
112   if( interpolationTime > gestureOut.time ) // Guard against the rare case when gestureOut.time > (lastVSyncTime + mPredictionAmount)
113   {
114     interpolationTime -= gestureOut.time;
115   }
116   else
117   {
118     interpolationTime = 0u;
119   }
120
121   while( iter != endIter )
122   {
123     PanInfo currentGesture = *iter;
124     if( !previousVelocity )
125     {
126       // not yet set a previous velocity
127       screenVelocity = currentGesture.screen.velocity;
128       previousVelocity = true;
129       lastTime = currentGesture.time;
130       ++iter;
131       continue;
132     }
133     float previousValueWeight = (float)(MAX_GESTURE_AGE - (lastVSyncTime - lastTime)) / (float)MAX_GESTURE_AGE;
134     float velMag = currentGesture.screen.velocity.Length();
135     float velDiff = velMag - screenVelocity.Length();
136     float acceleration = 0.0f;
137
138     float time(0.f);
139     if (currentGesture.time > lastTime) // Guard against invalid timestamps
140     {
141       time = static_cast<float>( currentGesture.time - lastTime );
142     }
143     if( time > Math::MACHINE_EPSILON_1 )
144     {
145       acceleration = velDiff / time;
146     }
147
148     float newVelMag = 0.0f;
149     int currentInterpolation = interpolationTime;
150     if( !havePreviousAcceleration )
151     {
152       newVelMag =  velMag;
153       havePreviousAcceleration = true;
154     }
155     else
156     {
157       newVelMag = velMag + (((acceleration * (1.0f - previousValueWeight)) + (previousAccel * previousValueWeight)) * currentInterpolation);
158     }
159     float velMod = 1.0f;
160     if( velMag > Math::MACHINE_EPSILON_1 )
161     {
162       velMod = newVelMag / velMag;
163     }
164     gestureOut.screen.velocity = currentGesture.screen.velocity * velMod;
165     gestureOut.local.velocity = currentGesture.local.velocity * velMod;
166     screenDisplacement = gestureOut.screen.displacement + (gestureOut.screen.velocity * interpolationTime);
167     localDisplacement = gestureOut.local.displacement + (gestureOut.local.velocity * interpolationTime);
168     screenVelocity = currentGesture.screen.velocity;
169     localVelocity = currentGesture.local.velocity;
170     previousAccel = acceleration;
171     ++iter;
172   }
173   // gestureOut's position is currently equal to the last event's position and its displacement is equal to last frame's total displacement
174   // add interpolated distance and position to current
175   // work out interpolated velocity
176   gestureOut.screen.position = (gestureOut.screen.position - gestureOut.screen.displacement) + screenDisplacement;
177   gestureOut.local.position = (gestureOut.local.position - gestureOut.local.displacement) + localDisplacement;
178   gestureOut.screen.displacement = screenDisplacement;
179   gestureOut.local.displacement = localDisplacement;
180   gestureOut.time += interpolationTime;
181 }
182
183 void PanGesture::SmoothingAlgorithm1(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
184 {
185   if( !justStarted )
186   {
187     gestureOut.screen.position -= (gestureOut.screen.position - mLastGesture.screen.position) * 0.5f * (1.0f - mSmoothingAmount);
188     gestureOut.local.position -= (gestureOut.local.position - mLastGesture.local.position) * 0.5f * (1.0f - mSmoothingAmount);
189     // make current displacement relative to previous update-frame now.
190     gestureOut.screen.displacement = gestureOut.screen.position - mLastGesture.screen.position;
191     gestureOut.local.displacement = gestureOut.local.position - mLastGesture.local.position;
192     // calculate velocity relative to previous update-frame
193     unsigned int timeDiff( gestureOut.time - mLastGesture.time );
194     gestureOut.screen.velocity = gestureOut.screen.displacement / timeDiff;
195     gestureOut.local.velocity = gestureOut.local.displacement / timeDiff;
196   }
197 }
198
199 void PanGesture::SmoothingAlgorithm2(bool justStarted, PanInfo& gestureOut, unsigned int lastVSyncTime)
200 {
201   // push back result
202   mPredictionHistory.push_back(gestureOut);
203
204   // now smooth current pan event
205   PanInfoHistoryConstIter endIter = mPredictionHistory.end() - 1;
206   PanInfoHistoryIter iter = mPredictionHistory.begin();
207
208   float distanceMod = 1.0f;
209   float weight = 0.8f;
210   while( iter != endIter )
211   {
212     PanInfo currentGesture = *iter;
213     float newDistanceMod = currentGesture.screen.displacement.Length() / gestureOut.screen.displacement.Length();
214     distanceMod = ((distanceMod * weight) + (newDistanceMod * (1.0f - weight)));
215     weight -= 0.15f;
216     ++iter;
217   }
218   gestureOut.screen.position -= gestureOut.screen.displacement;
219   gestureOut.local.position -= gestureOut.local.displacement;
220   gestureOut.screen.displacement *= distanceMod;
221   gestureOut.local.displacement *= distanceMod;
222   gestureOut.screen.position += gestureOut.screen.displacement;
223   gestureOut.local.position += gestureOut.local.displacement;
224 }
225
226 bool PanGesture::UpdateProperties( unsigned int lastVSyncTime, unsigned int nextVSyncTime )
227 {
228   if( !mInGesture )
229   {
230     // clear current pan history
231     mPanHistory.clear();
232     mPredictionHistory.clear();
233   }
234
235   // create an event for this frame
236   bool justStarted ( false );
237   bool justFinished ( false );
238   bool eventFound( false );
239
240   // Not going through array from the beginning, using it as a circular buffer and only using unread
241   // values.
242   int eventsThisFrame = 0;
243
244   // create PanInfo to pass into prediction method
245   mLastEventGesture = mEventGesture;
246   mLastGesture = mLatestGesture;
247   // add new gestures and work out one full gesture for the frame
248   while(mReadPosition != mWritePosition)
249   {
250     // Copy the gesture first
251     PanInfo currentGesture(mGestures[mReadPosition]);
252
253     if( mProfiling )
254     {
255       mProfiling->mRawData.push_back( PanGestureProfiling::Position( currentGesture.time, currentGesture.screen.position ) );
256     }
257     mEventGesture.local.position = currentGesture.local.position;
258     mEventGesture.local.velocity = currentGesture.local.velocity;
259     mEventGesture.screen.position = currentGesture.screen.position;
260     mEventGesture.screen.velocity = currentGesture.screen.velocity;
261     if( !eventFound )
262     {
263       mEventGesture.local.displacement = currentGesture.local.displacement;
264       mEventGesture.screen.displacement = currentGesture.screen.displacement;
265     }
266     else
267     {
268       mEventGesture.local.displacement += currentGesture.local.displacement;
269       mEventGesture.screen.displacement += currentGesture.screen.displacement;
270     }
271     eventFound = true;
272     mEventGesture.time = currentGesture.time;
273
274     // add event to history
275     mPanHistory.push_back(currentGesture);
276     if( currentGesture.state == Gesture::Started )
277     {
278       justStarted = true;
279       // clear just finished as we have started new pan
280       justFinished = false;
281     }
282     justFinished |= (currentGesture.state == Gesture::Finished || currentGesture.state == Gesture::Cancelled);
283
284     // Update our read position.
285     ++eventsThisFrame;
286     ++mReadPosition;
287     mReadPosition %= PAN_GESTURE_HISTORY;
288   }
289   mLatestGesture = mEventGesture;
290
291   mInGesture |= justStarted;
292
293   bool updateProperties = false;
294
295   if ( mInGesture )
296   {
297     if( mProfiling )
298     {
299       mProfiling->mLatestData.push_back( PanGestureProfiling::Position( lastVSyncTime, mEventGesture.screen.position ) );
300     }
301
302     switch( mPredictionMode )
303     {
304       case PREDICTION_NONE:
305       {
306         updateProperties = eventFound;
307         // dont want event time
308         unsigned int time = mLastGesture.time;
309         mLastGesture = mLastEventGesture;
310         mLastGesture.time = time;
311         mLatestGesture.time = lastVSyncTime;
312         break;
313       }
314       case PREDICTION_1:
315       {
316         // make latest gesture equal to current gesture before interpolation
317         PredictiveAlgorithm1(eventsThisFrame, mLatestGesture, mPanHistory, lastVSyncTime, nextVSyncTime);
318         updateProperties = true;
319         break;
320       }
321     }
322
323     switch( mSmoothingMode )
324     {
325       case SMOOTHING_NONE:
326       {
327         // no smoothing
328         break;
329       }
330       case SMOOTHING_LAST_VALUE:
331       {
332         SmoothingAlgorithm1(justStarted, mLatestGesture, lastVSyncTime);
333         break;
334       }
335     }
336
337     if( updateProperties )
338     {
339       // only update properties if event received
340       // set latest gesture to raw pan info with unchanged time
341       mPanning.Set( mInGesture & !justFinished );
342       mScreenPosition.Set( mLatestGesture.screen.position );
343       mScreenDisplacement.Set( mLatestGesture.screen.displacement );
344       mScreenVelocity.Set( mLatestGesture.screen.velocity );
345       mLocalPosition.Set( mLatestGesture.local.position );
346       mLocalDisplacement.Set( mLatestGesture.local.displacement );
347       mLocalVelocity.Set( mLatestGesture.local.velocity );
348     }
349
350     if( mProfiling )
351     {
352       mProfiling->mAveragedData.push_back( PanGestureProfiling::Position( mLatestGesture.time, mLatestGesture.screen.position ) );
353     }
354   }
355
356   mInGesture &= ~justFinished;
357
358   if( mProfiling && justFinished )
359   {
360     mProfiling->PrintData();
361     mProfiling->ClearData();
362   }
363
364   return updateProperties;
365 }
366
367 const GesturePropertyBool& PanGesture::GetPanningProperty() const
368 {
369   return mPanning;
370 }
371
372 const GesturePropertyVector2& PanGesture::GetScreenPositionProperty() const
373 {
374   return mScreenPosition;
375 }
376
377 const GesturePropertyVector2& PanGesture::GetScreenVelocityProperty() const
378 {
379   return mScreenVelocity;
380 }
381
382 const GesturePropertyVector2& PanGesture::GetScreenDisplacementProperty() const
383 {
384   return mScreenDisplacement;
385 }
386
387 const GesturePropertyVector2& PanGesture::GetLocalPositionProperty() const
388 {
389   return mLocalPosition;
390 }
391
392 const GesturePropertyVector2& PanGesture::GetLocalDisplacementProperty() const
393 {
394   return mLocalDisplacement;
395 }
396
397 const GesturePropertyVector2& PanGesture::GetLocalVelocityProperty() const
398 {
399   return mLocalVelocity;
400 }
401
402 void PanGesture::SetPredictionMode(PredictionMode mode)
403 {
404   mPredictionMode = mode;
405 }
406
407 void PanGesture::SetPredictionAmount(unsigned int amount)
408 {
409   mPredictionAmount = amount;
410 }
411
412 void PanGesture::SetSmoothingMode(SmoothingMode mode)
413 {
414   mSmoothingMode = mode;
415 }
416
417 void PanGesture::SetSmoothingAmount(float amount)
418 {
419   mSmoothingAmount = amount;
420 }
421
422 void PanGesture::EnableProfiling()
423 {
424   if( !mProfiling )
425   {
426     mProfiling = new PanGestureProfiling();
427   }
428 }
429
430 void PanGesture::ResetDefaultProperties( BufferIndex updateBufferIndex )
431 {
432   mScreenPosition.Reset();
433   mScreenDisplacement.Reset();
434   mLocalPosition.Reset();
435   mLocalDisplacement.Reset();
436   mPanning.Reset();
437 }
438
439 PanGesture::PanGesture()
440 : mGestures(),
441   mWritePosition( 0 ),
442   mReadPosition( 0 ),
443   mInGesture( false ),
444   mPredictionMode(DEFAULT_PREDICTION_MODE),
445   mPredictionAmount(DEFAULT_PREDICTION_INTERPOLATION),
446   mSmoothingMode(DEFAULT_SMOOTHING_MODE),
447   mSmoothingAmount(DEFAULT_SMOOTHING_AMOUNT),
448   mProfiling( NULL )
449 {
450 }
451
452 } // namespace SceneGraph
453
454 } // namespace Internal
455
456 } // namespace Dali