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