Merge remote-tracking branch 'origin/tizen' into new_text
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.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/animation/scene-graph-animation.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath> // fmod
23
24 // INTERNAL INCLUDES
25 #include <dali/internal/render/common/performance-monitor.h>
26
27 namespace Dali
28 {
29
30 namespace Internal
31 {
32
33 namespace SceneGraph
34 {
35
36 float DefaultAlphaFunc(float progress)
37 {
38   return progress; // linear
39 }
40
41 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
42 : mDurationSeconds(durationSeconds),
43   mSpeedFactor( speedFactor ),
44   mLooping(isLooping),
45   mEndAction(endAction),
46   mDisconnectAction(disconnectAction),
47   mState(Stopped),
48   mElapsedSeconds(playRange.x*mDurationSeconds),
49   mPlayCount(0),
50   mPlayRange( playRange )
51 {
52 }
53
54 Animation::~Animation()
55 {
56 }
57
58 void Animation::SetDuration(float durationSeconds)
59 {
60   DALI_ASSERT_DEBUG(durationSeconds > 0.0f);
61
62   mDurationSeconds = durationSeconds;
63 }
64
65 void Animation::SetLooping(bool looping)
66 {
67   mLooping = looping;
68 }
69
70 void Animation::SetEndAction(Dali::Animation::EndAction action)
71 {
72   mEndAction = action;
73 }
74
75 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
76 {
77   if ( mDisconnectAction != action )
78   {
79     mDisconnectAction = action;
80
81     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
82     {
83       (*iter)->SetDisconnectAction( action );
84     }
85   }
86 }
87
88 void Animation::SetPlayRange( const Vector2& range )
89 {
90   mPlayRange = range;
91
92   //Make sure mElapsedSeconds is within the new range
93   mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
94 }
95
96 void Animation::Play()
97 {
98   mState = Playing;
99
100   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
101   {
102     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
103   }
104
105   SetAnimatorsActive( true );
106 }
107
108 void Animation::PlayFrom( float progress )
109 {
110   //If the animation is already playing this has no effect
111   if( mState != Playing )
112   {
113     mElapsedSeconds = progress * mDurationSeconds;
114     mState = Playing;
115
116     SetAnimatorsActive( true );
117   }
118 }
119
120 void Animation::Pause()
121 {
122   if (mState == Playing)
123   {
124     mState = Paused;
125   }
126 }
127
128 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
129 {
130   if( action == Dali::Animation::BakeFinal )
131   {
132     if( mSpeedFactor > 0.0f )
133     {
134       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
135     }
136     else
137     {
138       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
139     }
140   }
141
142   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
143 }
144
145 void Animation::SetAnimatorsActive( bool active )
146 {
147   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
148   {
149     (*iter)->SetActive( active );
150   }
151 }
152
153 bool Animation::Stop(BufferIndex bufferIndex)
154 {
155   bool animationFinished(false);
156
157   if (mState == Playing || mState == Paused)
158   {
159     animationFinished = true; // The actor-thread should be notified of this
160
161     if( mEndAction != Dali::Animation::Discard )
162     {
163       Bake( bufferIndex, mEndAction );
164
165       // Animators are automatically set to inactive in Bake
166     }
167     else
168     {
169       SetAnimatorsActive( false );
170     }
171
172     // The animation has now been played to completion
173     ++mPlayCount;
174   }
175
176   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
177   mState = Stopped;
178
179   return animationFinished;
180 }
181
182 void Animation::OnDestroy(BufferIndex bufferIndex)
183 {
184   if (mState == Playing || mState == Paused)
185   {
186     if (mEndAction != Dali::Animation::Discard)
187     {
188       Bake( bufferIndex, mEndAction );
189
190       // Animators are automatically set to inactive in Bake
191     }
192     else
193     {
194       SetAnimatorsActive( false );
195     }
196   }
197
198   mState = Destroyed;
199 }
200
201 void Animation::AddAnimator( AnimatorBase* animator )
202 {
203   animator->SetDisconnectAction( mDisconnectAction );
204   mAnimators.PushBack( animator );
205 }
206
207 bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
208 {
209   if (mState == Stopped || mState == Destroyed)
210   {
211     // Short circuit when animation isn't running
212     return false;
213   }
214
215   // The animation must still be applied when Paused/Stopping
216   if (mState == Playing)
217   {
218     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
219   }
220
221   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
222   if (mLooping)
223   {
224     if (mElapsedSeconds > playRangeSeconds.y )
225     {
226       mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
227     }
228     else if( mElapsedSeconds < playRangeSeconds.x )
229     {
230       mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
231     }
232   }
233
234   const bool animationFinished(mState == Playing                                                &&
235                               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
236                                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
237                               );
238
239   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard), animationFinished);
240
241   if (animationFinished)
242   {
243     // The animation has now been played to completion
244     ++mPlayCount;
245
246     mElapsedSeconds = playRangeSeconds.x;
247     mState = Stopped;
248   }
249
250   return animationFinished;
251 }
252
253 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
254 {
255   float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
256
257   //Loop through all animators
258   bool applied(true);
259   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
260   {
261     AnimatorBase *animator = *iter;
262
263     if( animator->Orphan() )
264     {
265       //Remove animators whose PropertyOwner has been destroyed
266       iter = mAnimators.Erase(iter);
267     }
268     else
269     {
270       if( animator->IsEnabled() )
271       {
272         const float initialDelay(animator->GetInitialDelay());
273         if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
274         {
275           // Calculate a progress specific to each individual animator
276           float progress(1.0f);
277           const float animatorDuration = animator->GetDuration();
278           if (animatorDuration > 0.0f) // animators can be "immediate"
279           {
280             progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
281           }
282           animator->Update(bufferIndex, progress, bake);
283         }
284         applied = true;
285       }
286       else
287       {
288         applied = false;
289       }
290
291       if ( animationFinished )
292       {
293         animator->SetActive( false );
294       }
295
296       if (applied)
297       {
298         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
299       }
300
301       ++iter;
302     }
303   }
304
305 }
306
307 } // namespace SceneGraph
308
309 } // namespace Internal
310
311 } // namespace Dali