Added api function to specify speed factor of an animation 17/27417/7
authorFerran Sole <ferran.sole@samsung.com>
Fri, 12 Sep 2014 10:35:02 +0000 (11:35 +0100)
committerFerran Sole <ferran.sole@samsung.com>
Tue, 16 Sep 2014 07:58:05 +0000 (08:58 +0100)
The speed factor is a multiplier of the normal velocity of the animation.
Values between [0-1] will slow down the animation and values above 1 will speed
it up. It is also possible to specify a negative multiplier so the animation
will play in reverse.

Change-Id: I4e74cb3cd5372f4c141089a05be8325e0892ce7f

automated-tests/src/dali/utc-Dali-Animation.cpp
dali/internal/event/animation/animation-impl.cpp
dali/internal/event/animation/animation-impl.h
dali/internal/update/animation/scene-graph-animation.cpp
dali/internal/update/animation/scene-graph-animation.h
dali/public-api/animation/animation.cpp
dali/public-api/animation/animation.h

index 5287188..9bf8296 100644 (file)
@@ -643,6 +643,183 @@ int UtcDaliAnimationPlay(void)
   END_TEST;
 }
 
+int UtcDaliAnimationSetSpeedFactor(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+
+  const Vector3 initialPosition(0.0f, 0.0f, 0.0f);
+  const Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add( 0.0f, initialPosition);
+  keyframes.Add( 1.0f, targetPosition );
+  animation.AnimateBetween( Property(actor, Actor::POSITION), keyframes, AlphaFunctions::Linear);
+
+  //Set speed to be x2
+  animation.SetSpeedFactor(2.0f);
+
+  // Start the animation
+  animation.Play();
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 40% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.4f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 80% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*100.0f) + 1u/*just beyond half the duration*/);
+
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+
+  // Check that nothing has changed after a couple of buffer swaps
+  application.Render(0);
+  DALI_TEST_EQUALS( targetPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+  application.Render(0);
+  DALI_TEST_EQUALS( targetPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+
+  finishCheck.Reset();
+
+  //Test -1 speed factor. Animation will play in reverse at normal speed
+  animation.SetSpeedFactor( -1.0f );
+
+  // Start the animation
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 80% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 60% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.6f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 40% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.4f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 20% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.2f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f) + 1u/*just beyond the animation duration*/);
+
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), initialPosition, TEST_LOCATION );
+
+  // Check that nothing has changed after a couple of buffer swaps
+  application.Render(0);
+  DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+  application.Render(0);
+  DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+
+
+  //Test change speed factor on the fly
+  finishCheck.Reset();
+
+  //Set speed to be half of normal speed
+  animation.SetSpeedFactor( 0.5f );
+
+  // Start the animation
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 10% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.1f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 20% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.2f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 30% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.3f), TEST_LOCATION );
+
+  //Change speed factor while animation still playing.
+  animation.SetSpeedFactor(-1.0f);
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f)/* 10% progress */);
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.1f), TEST_LOCATION );
+
+  application.Render(static_cast<unsigned int>(durationSeconds*100.0f) + 1u/*just beyond the animation duration*/);
+
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), initialPosition, TEST_LOCATION );
+
+  // Check that nothing has changed after a couple of buffer swaps
+  application.Render(0);
+  DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+  application.Render(0);
+  DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliAnimationGetSpeedFactor(void)
+{
+  TestApplication application;
+
+  Animation animation = Animation::New(1.0f);
+  animation.SetSpeedFactor(0.5f);
+  DALI_TEST_EQUALS(animation.GetSpeedFactor(), 0.5f, TEST_LOCATION);
+
+  animation.SetSpeedFactor(-2.5f);
+  DALI_TEST_EQUALS(animation.GetSpeedFactor(), -2.5f, TEST_LOCATION);
+  END_TEST;
+}
+
 int UtcDaliAnimationPlayOffStage(void)
 {
   // Test that an animation can be played, when the actor is off-stage.
index d5bc8f9..3f680a5 100644 (file)
@@ -101,6 +101,7 @@ Animation::Animation( UpdateManager& updateManager, AnimationPlaylist& playlist,
   mFinishedCallback( NULL ),
   mFinishedCallbackObject( NULL ),
   mDurationSeconds( durationSeconds ),
+  mSpeedFactor(1.0f),
   mIsLooping( false ),
   mEndAction( endAction ),
   mDestroyAction( destroyAction ),
@@ -137,7 +138,7 @@ void Animation::CreateSceneObject()
   DALI_ASSERT_DEBUG( mAnimation == NULL );
 
   // Create a new animation, temporarily owned
-  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mIsLooping, mEndAction, mDestroyAction );
+  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mIsLooping, mEndAction, mDestroyAction );
 
   // Keep a const pointer to the animation.
   mAnimation = animation;
@@ -1234,6 +1235,22 @@ void Animation::ExtendDuration( const TimePeriod& timePeriod )
   }
 }
 
+void Animation::SetSpeedFactor( float factor )
+{
+  if( mAnimation )
+  {
+    mSpeedFactor = factor;
+    SetSpeedFactorMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, factor );
+  }
+}
+
+float Animation::GetSpeedFactor() const
+{
+  return mSpeedFactor;
+}
+
+
+
 } // namespace Internal
 
 } // namespace Dali
index 775637c..7405824 100644 (file)
@@ -797,6 +797,16 @@ public:
    */
   void SetCurrentProgress(float progress);
 
+  /*
+   * @copydoc Dali::Animation::SetSpeedFactor()
+   */
+  void SetSpeedFactor( float factor );
+
+  /*
+   * @copydoc Dali::Animation::GetSpeedFactor()
+   */
+  float GetSpeedFactor() const;
+
 public: // For connecting animators to animations
 
   /**
@@ -893,6 +903,7 @@ private:
 
   // Cached for public getters
   float mDurationSeconds;
+  float mSpeedFactor;
   bool mIsLooping;
   EndAction mEndAction;
   EndAction mDestroyAction;
index 8b1b925..f6a290a 100644 (file)
@@ -40,8 +40,9 @@ 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, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction)
 : mDurationSeconds(durationSeconds),
+  mSpeedFactor( speedFactor ),
   mLooping(isLooping),
   mEndAction(endAction),
   mDestroyAction(destroyAction),
@@ -80,6 +81,11 @@ void Animation::SetDestroyAction(Dali::Animation::EndAction action)
 void Animation::Play()
 {
   mState = Playing;
+
+  if ( mSpeedFactor < 0.0f && mElapsedSeconds <= 0.0f )
+  {
+    mElapsedSeconds = mDurationSeconds;
+  }
 }
 
 void Animation::PlayFrom( float progress )
@@ -112,7 +118,15 @@ bool Animation::Stop(BufferIndex bufferIndex)
     {
       if( mEndAction == Dali::Animation::BakeFinal )
       {
-        mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
+        if( mSpeedFactor > 0.0f )
+        {
+          mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
+        }
+        else
+        {
+          mElapsedSeconds = 0.0f - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
+        }
+
       }
       UpdateAnimators(bufferIndex, true/*bake the final result*/);
     }
@@ -158,18 +172,26 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
   // The animation must still be applied when Paused/Stopping
   if (mState == Playing)
   {
-    mElapsedSeconds += elapsedSeconds;
+    mElapsedSeconds += elapsedSeconds * mSpeedFactor;
   }
 
   if (mLooping)
   {
-    if (mElapsedSeconds > mDurationSeconds)
+    if (mElapsedSeconds > mDurationSeconds )
     {
       mElapsedSeconds = fmod(mElapsedSeconds, mDurationSeconds);
     }
+    else if( mElapsedSeconds < 0.0f )
+    {
+      mElapsedSeconds = mDurationSeconds - fmod(mElapsedSeconds, mDurationSeconds);
+    }
   }
 
-  const bool animationFinished(mState == Playing && mElapsedSeconds > mDurationSeconds);
+
+  const bool animationFinished(mState == Playing                                              &&
+                              (( mSpeedFactor > 0.0f && mElapsedSeconds > mDurationSeconds )  ||
+                               ( mSpeedFactor < 0.0f && mElapsedSeconds < 0.0f ))
+                              );
 
   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard));
 
@@ -195,14 +217,14 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
     AnimatorBase *animator = *iter;
     const float initialDelay(animator->GetInitialDelay());
 
-    if (mElapsedSeconds >= initialDelay)
+    if (mElapsedSeconds >= 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 = min(1.0f, (mElapsedSeconds - initialDelay) / animatorDuration);
+        progress = Clamp((mElapsedSeconds - initialDelay) / animatorDuration, 0.0f , 1.0f );
       }
 
       applied = animator->Update(bufferIndex, progress, bake);
index 302763a..afce4f9 100644 (file)
@@ -63,14 +63,15 @@ public:
   /**
    * Construct a new Animation.
    * @param[in] durationSeconds The duration of the animation in seconds.
+   * @param[in] speedFactor Multiplier to the animation velocity.
    * @param[in] isLooping Whether the animation will loop.
    * @param[in] endAction The action to perform when the animation ends.
    * @param[in] destroyAction The action to perform when the animation is destroyed.
    * @return A new Animation
    */
-  static Animation* New( float durationSeconds, bool isLooping, EndAction endAction, EndAction destroyAction )
+  static Animation* New( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction )
   {
-    return new Animation( durationSeconds, isLooping, endAction, destroyAction );
+    return new Animation( durationSeconds, speedFactor, isLooping, endAction, destroyAction );
   }
 
   /**
@@ -117,6 +118,11 @@ public:
     mElapsedSeconds = mDurationSeconds * progress;
   }
 
+  void SetSpeedFactor( float factor )
+  {
+    mSpeedFactor = factor;
+  }
+
   /**
    * Set whether the animation will loop.
    * @param[in] looping True if the animation will loop.
@@ -245,7 +251,7 @@ protected:
   /**
    * Protected constructor. See New()
    */
-  Animation( float durationSeconds, bool isLooping, EndAction endAction, EndAction destroyAction );
+  Animation( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction );
 
 
 private:
@@ -266,6 +272,7 @@ private:
 protected:
 
   float mDurationSeconds;
+  float mSpeedFactor;
   bool mLooping;
   EndAction mEndAction;
   EndAction mDestroyAction;
@@ -342,6 +349,17 @@ inline void SetCurrentProgressMessage( EventToUpdate& eventToUpdate, const Anima
   new (slot) LocalType( &animation, &Animation::SetCurrentProgress, progress );
 }
 
+inline void SetSpeedFactorMessage( EventToUpdate& eventToUpdate, const Animation& animation, float factor )
+{
+  typedef MessageValue1< Animation, float > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventToUpdate.ReserveMessageSlot( sizeof( LocalType ) );
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new (slot) LocalType( &animation, &Animation::SetSpeedFactor, factor );
+}
+
 inline void PlayAnimationMessage( EventToUpdate& eventToUpdate, const Animation& animation )
 {
   typedef Message< Animation > LocalType;
index 44ee44f..5d908d6 100644 (file)
@@ -498,4 +498,14 @@ void Animation::SetCurrentProgress( float progress )
   return GetImplementation(*this).SetCurrentProgress( progress );
 }
 
+void Animation::SetSpeedFactor( float factor )
+{
+  GetImplementation(*this).SetSpeedFactor( factor );
+}
+
+float Animation::GetSpeedFactor() const
+{
+  return GetImplementation(*this).GetSpeedFactor();
+}
+
 } // namespace Dali
index db2b23e..828c405 100644 (file)
@@ -281,6 +281,24 @@ public:
   */
   float GetCurrentProgress();
 
+  /**
+   * @brief Specifies an speed factor for the animation.
+   *
+   * The speed factor is a multiplier of the normal velocity of the animation. Values between [0,1] will
+   * slow down the animation and values above one will speed up the animation. It is also possible to specify a negative multiplier
+   * to play the animation in reverse.
+   *
+   * @param[in] factor A value which will multiply the velocity.
+   */
+  void SetSpeedFactor( float factor );
+
+  /**
+   * @brief Retrieve the speed factor of the animation
+   *
+   * @return speed factor
+   */
+  float GetSpeedFactor() const;
+
   /*
    * @brief Sets the progress of the animation.
    * The animation will play (or continue playing) from this point