Merge "Ensured that messages are aligned to 8 byte words on 64bit ARM" into tizen
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
index 8b1b925..4449e7f 100644 (file)
@@ -24,8 +24,6 @@
 // INTERNAL INCLUDES
 #include <dali/internal/render/common/performance-monitor.h>
 
-using namespace std;
-
 namespace Dali
 {
 
@@ -40,14 +38,16 @@ float DefaultAlphaFunc(float progress)
   return progress; // linear
 }
 
-Animation::Animation(float durationSeconds, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction)
+Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
 : mDurationSeconds(durationSeconds),
+  mSpeedFactor( speedFactor ),
   mLooping(isLooping),
   mEndAction(endAction),
-  mDestroyAction(destroyAction),
+  mDisconnectAction(disconnectAction),
   mState(Stopped),
-  mElapsedSeconds(0.0f),
-  mPlayCount(0)
+  mElapsedSeconds(playRange.x*mDurationSeconds),
+  mPlayCount(0),
+  mPlayRange( playRange )
 {
 }
 
@@ -72,14 +72,37 @@ void Animation::SetEndAction(Dali::Animation::EndAction action)
   mEndAction = action;
 }
 
-void Animation::SetDestroyAction(Dali::Animation::EndAction action)
+void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
 {
-  mDestroyAction = action;
+  if ( mDisconnectAction != action )
+  {
+    mDisconnectAction = action;
+
+    for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+    {
+      (*iter)->SetDisconnectAction( action );
+    }
+  }
+}
+
+void Animation::SetPlayRange( const Vector2& range )
+{
+  mPlayRange = range;
+
+  //Make sure mElapsedSeconds is within the new range
+  mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
 }
 
 void Animation::Play()
 {
   mState = Playing;
+
+  if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
+  {
+    mElapsedSeconds = mPlayRange.y * mDurationSeconds;
+  }
+
+  SetAnimatorsActive( true );
 }
 
 void Animation::PlayFrom( float progress )
@@ -89,6 +112,8 @@ void Animation::PlayFrom( float progress )
   {
     mElapsedSeconds = progress * mDurationSeconds;
     mState = Playing;
+
+    SetAnimatorsActive( true );
   }
 }
 
@@ -100,6 +125,31 @@ void Animation::Pause()
   }
 }
 
+void Animation::Bake(BufferIndex bufferIndex, EndAction action)
+{
+  if( action == Dali::Animation::BakeFinal )
+  {
+    if( mSpeedFactor > 0.0f )
+    {
+      mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
+    }
+    else
+    {
+      mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
+    }
+  }
+
+  UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
+}
+
+void Animation::SetAnimatorsActive( bool active )
+{
+  for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+  {
+    (*iter)->SetActive( active );
+  }
+}
+
 bool Animation::Stop(BufferIndex bufferIndex)
 {
   bool animationFinished(false);
@@ -110,18 +160,20 @@ bool Animation::Stop(BufferIndex bufferIndex)
 
     if( mEndAction != Dali::Animation::Discard )
     {
-      if( mEndAction == Dali::Animation::BakeFinal )
-      {
-        mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
-      }
-      UpdateAnimators(bufferIndex, true/*bake the final result*/);
+      Bake( bufferIndex, mEndAction );
+
+      // Animators are automatically set to inactive in Bake
+    }
+    else
+    {
+      SetAnimatorsActive( false );
     }
 
     // The animation has now been played to completion
     ++mPlayCount;
   }
 
-  mElapsedSeconds = 0.0f;
+  mElapsedSeconds = mPlayRange.x*mDurationSeconds;
   mState = Stopped;
 
   return animationFinished;
@@ -131,19 +183,24 @@ void Animation::OnDestroy(BufferIndex bufferIndex)
 {
   if (mState == Playing || mState == Paused)
   {
-    if (mDestroyAction != Dali::Animation::Discard)
+    if (mEndAction != Dali::Animation::Discard)
+    {
+      Bake( bufferIndex, mEndAction );
+
+      // Animators are automatically set to inactive in Bake
+    }
+    else
     {
-      UpdateAnimators(bufferIndex, true/*bake the final result*/);
+      SetAnimatorsActive( false );
     }
   }
 
   mState = Destroyed;
 }
 
-void Animation::AddAnimator( AnimatorBase* animator, PropertyOwner* propertyOwner )
+void Animation::AddAnimator( AnimatorBase* animator )
 {
-  animator->Attach( propertyOwner );
-
+  animator->SetDisconnectAction( mDisconnectAction );
   mAnimators.PushBack( animator );
 }
 
@@ -158,68 +215,93 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
   // The animation must still be applied when Paused/Stopping
   if (mState == Playing)
   {
-    mElapsedSeconds += elapsedSeconds;
+    mElapsedSeconds += elapsedSeconds * mSpeedFactor;
   }
 
+  Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
   if (mLooping)
   {
-    if (mElapsedSeconds > mDurationSeconds)
+    if (mElapsedSeconds > playRangeSeconds.y )
+    {
+      mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
+    }
+    else if( mElapsedSeconds < playRangeSeconds.x )
     {
-      mElapsedSeconds = fmod(mElapsedSeconds, mDurationSeconds);
+      mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
     }
   }
 
-  const bool animationFinished(mState == Playing && mElapsedSeconds > mDurationSeconds);
+  const bool animationFinished(mState == Playing                                                &&
+                              (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
+                               ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
+                              );
 
-  UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard));
+  UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard), animationFinished);
 
   if (animationFinished)
   {
     // The animation has now been played to completion
     ++mPlayCount;
 
-    mElapsedSeconds = 0.0f;
+    mElapsedSeconds = playRangeSeconds.x;
     mState = Stopped;
   }
 
   return animationFinished;
 }
 
-void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
+void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
 {
+  float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
+
+  //Loop through all animators
+  bool applied(true);
   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
   {
-    // If an animator is not successfully applied, then it has been orphaned
-    bool applied(true);
-
     AnimatorBase *animator = *iter;
-    const float initialDelay(animator->GetInitialDelay());
-
-    if (mElapsedSeconds >= initialDelay)
-    {
-      // Calculate a progress specific to each individual animator
-      float progress(1.0f);
-      const float animatorDuration = animator->GetDuration();
-      if (animatorDuration > 0.0f) // animators can be "immediate"
-      {
-        progress = min(1.0f, (mElapsedSeconds - initialDelay) / animatorDuration);
-      }
-
-      applied = animator->Update(bufferIndex, progress, bake);
-    }
 
-    // Animators are automatically removed, when orphaned from animatable scene objects.
-    if (!applied)
+    if( animator->Orphan() )
     {
+      //Remove animators whose PropertyOwner has been destroyed
       iter = mAnimators.Erase(iter);
     }
     else
     {
-      ++iter;
+      if( animator->IsEnabled() )
+      {
+        const float initialDelay(animator->GetInitialDelay());
+        if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
+        {
+          // Calculate a progress specific to each individual animator
+          float progress(1.0f);
+          const float animatorDuration = animator->GetDuration();
+          if (animatorDuration > 0.0f) // animators can be "immediate"
+          {
+            progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
+          }
+          animator->Update(bufferIndex, progress, bake);
+        }
+        applied = true;
+      }
+      else
+      {
+        applied = false;
+      }
 
-      INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
+      if ( animationFinished )
+      {
+        animator->SetActive( false );
+      }
+
+      if (applied)
+      {
+        INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
+      }
+
+      ++iter;
     }
   }
+
 }
 
 } // namespace SceneGraph