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