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