(Animation) Allow the value to be baked upon property owner disconnection, DestroyEnd...
[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
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 }
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*/);
141 }
142
143 bool Animation::Stop(BufferIndex bufferIndex)
144 {
145   bool animationFinished(false);
146
147   if (mState == Playing || mState == Paused)
148   {
149     animationFinished = true; // The actor-thread should be notified of this
150
151     if( mEndAction != Dali::Animation::Discard )
152     {
153       Bake( bufferIndex, mEndAction );
154     }
155
156     // The animation has now been played to completion
157     ++mPlayCount;
158   }
159
160   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
161   mState = Stopped;
162
163   return animationFinished;
164 }
165
166 void Animation::OnDestroy(BufferIndex bufferIndex)
167 {
168   if (mState == Playing || mState == Paused)
169   {
170     if (mEndAction != Dali::Animation::Discard)
171     {
172       Bake( bufferIndex, mEndAction );
173     }
174   }
175
176   mState = Destroyed;
177 }
178
179 void Animation::AddAnimator( AnimatorBase* animator, PropertyOwner* propertyOwner )
180 {
181   animator->Attach( propertyOwner );
182   animator->SetDisconnectAction( mDisconnectAction );
183
184   mAnimators.PushBack( animator );
185 }
186
187 bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
188 {
189   if (mState == Stopped || mState == Destroyed)
190   {
191     // Short circuit when animation isn't running
192     return false;
193   }
194
195   // The animation must still be applied when Paused/Stopping
196   if (mState == Playing)
197   {
198     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
199   }
200
201   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
202   if (mLooping)
203   {
204     if (mElapsedSeconds > playRangeSeconds.y )
205     {
206       mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
207     }
208     else if( mElapsedSeconds < playRangeSeconds.x )
209     {
210       mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
211     }
212   }
213
214   const bool animationFinished(mState == Playing                                                &&
215                               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
216                                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
217                               );
218
219   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard));
220
221   if (animationFinished)
222   {
223     // The animation has now been played to completion
224     ++mPlayCount;
225
226     mElapsedSeconds = playRangeSeconds.x;
227     mState = Stopped;
228   }
229
230   return animationFinished;
231 }
232
233 void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
234 {
235   float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
236   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
237   {
238     // If an animator is not successfully applied, then it has been orphaned
239     bool applied(true);
240
241     AnimatorBase *animator = *iter;
242     const float initialDelay(animator->GetInitialDelay());
243
244     if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
245     {
246       // Calculate a progress specific to each individual animator
247       float progress(1.0f);
248       const float animatorDuration = animator->GetDuration();
249       if (animatorDuration > 0.0f) // animators can be "immediate"
250       {
251         progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
252       }
253
254       applied = animator->Update(bufferIndex, progress, bake);
255     }
256
257     // Animators are automatically removed, when orphaned from animatable scene objects.
258     if (!applied)
259     {
260       iter = mAnimators.Erase(iter);
261     }
262     else
263     {
264       ++iter;
265
266       INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
267     }
268   }
269 }
270
271 } // namespace SceneGraph
272
273 } // namespace Internal
274
275 } // namespace Dali