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