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