Added api to specify a playing range inside an animation 28/27728/8
authorFerran Sole <ferran.sole@samsung.com>
Thu, 18 Sep 2014 09:19:02 +0000 (10:19 +0100)
committerFerran Sole <ferran.sole@samsung.com>
Wed, 24 Sep 2014 08:30:46 +0000 (09:30 +0100)
The user can now specify a range inside the animation so
when the animation play it will only play inside that range.

E.g

//Set play range between 0.2 and 0.8 progress
animation.SetPlayRange( Vector2( 0.2f, 0.8f ) );

//Animation will begin playing at progress 0.2 and will end at 0.8
animation.Play();

[Problem]  N/A
[Cause]    N/A
[Solution] N/A

Change-Id: I2677b8974fef1c4703c18b2b49f9d6a9f5f06047

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 9bf8296..3d1a11f 100644 (file)
@@ -1194,6 +1194,144 @@ int UtcDaliAnimationSetCurrentProgress(void)
   END_TEST;
 }
 
+int UtcDaliAnimationPlayRange(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add( 0.0f , Vector3(0.0f,0.0f,0.0f ) );
+  keyframes.Add( 1.0f , Vector3(100.0f,100.0f,100.0f ) );
+
+  animation.AnimateBetween( Property( actor, Actor::POSITION), keyframes );
+
+  // Set range between 0.4 and 0.8
+  animation.SetPlayRange( Vector2(0.4f,0.8f) );
+  animation.Play();
+
+  bool signalReceived(false);
+  AnimationFinishCheck finishCheck(signalReceived);
+  animation.FinishedSignal().Connect(&application, finishCheck);
+
+  //Test that setting progress outside the range doesn't work
+  animation.SetCurrentProgress( 0.9f );
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS( animation.GetCurrentProgress(), 0.4f, TEST_LOCATION );
+  animation.SetCurrentProgress( 0.2f );
+  application.SendNotification();
+  application.Render(0);
+  DALI_TEST_EQUALS( animation.GetCurrentProgress(), 0.4f, TEST_LOCATION );
+
+  application.SendNotification();
+  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 );
+
+  animation.Play(); // Test that calling play has no effect, when animation is already playing
+  application.SendNotification();
+  application.Render(static_cast<unsigned int>(durationSeconds*200.0f) + 1u/* 80% progress */);
+
+  // We did expect the animation to finish
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION );
+
+  // Check that nothing has changed after a couple of buffer swaps
+  application.Render(0);
+  DALI_TEST_EQUALS( targetPosition * 0.8f, actor.GetCurrentPosition(), TEST_LOCATION );
+  application.Render(0);
+  DALI_TEST_EQUALS( targetPosition * 0.8f, actor.GetCurrentPosition(), TEST_LOCATION );
+
+
+  //Loop inside the range
+  finishCheck.Reset();
+  animation.SetLooping( true );
+  animation.Play();
+  application.SendNotification();
+  float intervalSeconds = 0.1f;
+  float progress = 0.4f;
+  for (int iterations = 0; iterations < 10; ++iterations )
+  {
+    application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+    progress += intervalSeconds;
+    if (progress > 0.8f)
+    {
+      progress = progress - 0.4f;
+    }
+
+    DALI_TEST_EQUALS( targetPosition*progress, actor.GetCurrentPosition(), 0.001f, TEST_LOCATION );
+  }
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+
+
+  //Test change range on the fly
+  animation.SetPlayRange( Vector2( 0.2f, 0.9f ) );
+  application.SendNotification();
+
+  for (int iterations = 0; iterations < 10; ++iterations )
+  {
+    application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+    progress += intervalSeconds;
+    if (progress > 0.9f)
+    {
+      progress = progress - 0.7f;
+    }
+
+    DALI_TEST_EQUALS( targetPosition*progress, actor.GetCurrentPosition(), 0.001f, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
+
+int UtcDaliAnimationSetPlayRange(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  Animation animation = Animation::New(0);
+  application.SendNotification();
+
+  //If PlayRange not specified it should be 0.0-1.0 by default
+  DALI_TEST_EQUALS( Vector2(0.0,1.0), animation.GetPlayRange(), TEST_LOCATION );
+
+  //PlayRange out of bounds
+  animation.SetPlayRange( Vector2(-1.0f,1.0f) );
+  application.SendNotification();
+  DALI_TEST_EQUALS( Vector2(0.0f,1.0f), animation.GetPlayRange(), TEST_LOCATION );
+  animation.SetPlayRange( Vector2(0.0f,2.0f) );
+  application.SendNotification();
+  DALI_TEST_EQUALS( Vector2(0.0f,1.0f), animation.GetPlayRange(), TEST_LOCATION );
+
+  //If playRange is not in the correct order it has to be ordered
+  animation.SetPlayRange( Vector2(0.8f,0.2f) );
+  application.SendNotification();
+  DALI_TEST_EQUALS( Vector2(0.2f,0.8f), animation.GetPlayRange(), TEST_LOCATION );
+
+  // Set range between 0.4 and 0.8
+  animation.SetPlayRange( Vector2(0.4f,0.8f) );
+  application.SendNotification();
+  DALI_TEST_EQUALS( Vector2(0.4f,0.8f), animation.GetPlayRange(), TEST_LOCATION );
+
+  END_TEST;
+}
 
 int UtcDaliAnimationPause(void)
 {
index 3f680a5..9312fe5 100644 (file)
@@ -103,6 +103,7 @@ Animation::Animation( UpdateManager& updateManager, AnimationPlaylist& playlist,
   mDurationSeconds( durationSeconds ),
   mSpeedFactor(1.0f),
   mIsLooping( false ),
+  mPlayRange( Vector2(0.0f,1.0f)),
   mEndAction( endAction ),
   mDestroyAction( destroyAction ),
   mDefaultAlpha( defaultAlpha )
@@ -138,7 +139,7 @@ void Animation::CreateSceneObject()
   DALI_ASSERT_DEBUG( mAnimation == NULL );
 
   // Create a new animation, temporarily owned
-  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mIsLooping, mEndAction, mDestroyAction );
+  SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mIsLooping, mEndAction, mDestroyAction );
 
   // Keep a const pointer to the animation.
   mAnimation = animation;
@@ -228,7 +229,7 @@ void Animation::Play()
 
 void Animation::PlayFrom( float progress )
 {
-  if( progress >= 0.0f && progress <= 1.0f )
+  if( progress >= mPlayRange.x && progress <= mPlayRange.y )
   {
     // Update the current playlist
     mPlaylist.OnPlay( *this );
@@ -1206,23 +1207,23 @@ bool Animation::DoAction(BaseObject* object, const std::string& actionName, cons
   return done;
 }
 
-float Animation::GetCurrentProgress()
+void Animation::SetCurrentProgress(float progress)
 {
-  if( mAnimation )
+  if( mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y )
   {
-    return mAnimation->GetCurrentProgress();
+    // mAnimation is being used in a separate thread; queue a message to set the current progress
+    SetCurrentProgressMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, progress );
   }
-
-  return 0.0f;
 }
 
-void Animation::SetCurrentProgress(float progress)
+float Animation::GetCurrentProgress()
 {
-  if( mAnimation && progress >= 0.0f && progress <= 1.0f )
+  if( mAnimation )
   {
-    // mAnimation is being used in a separate thread; queue a message to set the current progress
-    SetCurrentProgressMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, progress );
+    return mAnimation->GetCurrentProgress();
   }
+
+  return 0.0f;
 }
 
 void Animation::ExtendDuration( const TimePeriod& timePeriod )
@@ -1249,6 +1250,30 @@ float Animation::GetSpeedFactor() const
   return mSpeedFactor;
 }
 
+void Animation::SetPlayRange( const Vector2& range)
+{
+  //Make sure the range specified is between 0.0 and 1.0
+  if( range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f )
+  {
+    Vector2 orderedRange( range );
+    //If the range is not in order swap values
+    if( range.x > range.y )
+    {
+      orderedRange = Vector2(range.y, range.x);
+    }
+
+    // Cache for public getters
+    mPlayRange = orderedRange;
+
+    // mAnimation is being used in a separate thread; queue a message to set play range
+    SetPlayRangeMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, orderedRange );
+  }
+}
+
+Vector2 Animation::GetPlayRange() const
+{
+  return mPlayRange;
+}
 
 
 } // namespace Internal
index 7405824..a207091 100644 (file)
@@ -788,14 +788,14 @@ public:
   void Resize(Actor& actor, const Vector3& size, AlphaFunction alpha, float delaySeconds, float durationSeconds);
 
   /*
-   * @copydoc Dali::Animation::GetCurrentProgress()
+   * @copydoc Dali::Animation::SetCurrentProgress()
    */
-  float GetCurrentProgress();
+  void SetCurrentProgress(float progress);
 
   /*
-   * @copydoc Dali::Animation::SetCurrentProgress()
+   * @copydoc Dali::Animation::GetCurrentProgress()
    */
-  void SetCurrentProgress(float progress);
+  float GetCurrentProgress();
 
   /*
    * @copydoc Dali::Animation::SetSpeedFactor()
@@ -807,6 +807,16 @@ public:
    */
   float GetSpeedFactor() const;
 
+  /*
+   * @copydoc Dali::Animation::SetPlayRange()
+   */
+  void SetPlayRange( const Vector2& range );
+
+  /*
+   * @copydoc Dali::Animation::GetPlayRange
+   */
+  Vector2 GetPlayRange() const;
+
 public: // For connecting animators to animations
 
   /**
@@ -905,6 +915,7 @@ private:
   float mDurationSeconds;
   float mSpeedFactor;
   bool mIsLooping;
+  Vector2 mPlayRange;
   EndAction mEndAction;
   EndAction mDestroyAction;
   AlphaFunction mDefaultAlpha;
index 95dd664..77175f3 100644 (file)
@@ -40,15 +40,16 @@ float DefaultAlphaFunc(float progress)
   return progress; // linear
 }
 
-Animation::Animation(float durationSeconds, float speedFactor, 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 destroyAction)
 : mDurationSeconds(durationSeconds),
   mSpeedFactor( speedFactor ),
   mLooping(isLooping),
   mEndAction(endAction),
   mDestroyAction(destroyAction),
   mState(Stopped),
-  mElapsedSeconds(0.0f),
-  mPlayCount(0)
+  mElapsedSeconds(playRange.x*mDurationSeconds),
+  mPlayCount(0),
+  mPlayRange( playRange )
 {
 }
 
@@ -78,13 +79,21 @@ void Animation::SetDestroyAction(Dali::Animation::EndAction action)
   mDestroyAction = 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 <= 0.0f )
+  if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
   {
-    mElapsedSeconds = mDurationSeconds;
+    mElapsedSeconds = mPlayRange.y * mDurationSeconds;
   }
 }
 
@@ -112,11 +121,11 @@ void Animation::Bake(BufferIndex bufferIndex, EndAction action)
   {
     if( mSpeedFactor > 0.0f )
     {
-      mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
+      mElapsedSeconds = mPlayRange.y*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
+      mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
     }
   }
 
@@ -140,7 +149,7 @@ bool Animation::Stop(BufferIndex bufferIndex)
     ++mPlayCount;
   }
 
-  mElapsedSeconds = 0.0f;
+  mElapsedSeconds = mPlayRange.x*mDurationSeconds;
   mState = Stopped;
 
   return animationFinished;
@@ -180,22 +189,22 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
   }
 
+  Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
   if (mLooping)
   {
-    if (mElapsedSeconds > mDurationSeconds )
+    if (mElapsedSeconds > playRangeSeconds.y )
     {
-      mElapsedSeconds = fmod(mElapsedSeconds, mDurationSeconds);
+      mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
     }
-    else if( mElapsedSeconds < 0.0f )
+    else if( mElapsedSeconds < playRangeSeconds.x )
     {
-      mElapsedSeconds = mDurationSeconds - fmod(mElapsedSeconds, mDurationSeconds);
+      mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
     }
   }
 
-
-  const bool animationFinished(mState == Playing                                              &&
-                              (( mSpeedFactor > 0.0f && mElapsedSeconds > mDurationSeconds )  ||
-                               ( mSpeedFactor < 0.0f && mElapsedSeconds < 0.0f ))
+  const bool animationFinished(mState == Playing                                                &&
+                              (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
+                               ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
                               );
 
   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard));
@@ -205,7 +214,7 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
     // The animation has now been played to completion
     ++mPlayCount;
 
-    mElapsedSeconds = 0.0f;
+    mElapsedSeconds = playRangeSeconds.x;
     mState = Stopped;
   }
 
@@ -214,6 +223,7 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
 
 void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
 {
+  float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
   {
     // If an animator is not successfully applied, then it has been orphaned
@@ -222,14 +232,14 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
     AnimatorBase *animator = *iter;
     const float initialDelay(animator->GetInitialDelay());
 
-    if (mElapsedSeconds >= initialDelay || mSpeedFactor < 0.0f )
+    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((mElapsedSeconds - initialDelay) / animatorDuration, 0.0f , 1.0f );
+        progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
       }
 
       applied = animator->Update(bufferIndex, progress, bake);
index c99c182..9635037 100644 (file)
@@ -64,14 +64,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] playRange Minimum and maximum progress between which the animation will play.
    * @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, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction )
+  static Animation* New( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction destroyAction )
   {
-    return new Animation( durationSeconds, speedFactor, isLooping, endAction, destroyAction );
+    return new Animation( durationSeconds, speedFactor, playRange, isLooping, endAction, destroyAction );
   }
 
   /**
@@ -171,6 +172,14 @@ public:
   }
 
   /**
+   * Set the playing range. The animation will only play between the minimum and maximum progress
+   * speficied.
+   *
+   * @param[in] range Two values between [0,1] to specify minimum and maximum progress.
+   */
+  void SetPlayRange( const Vector2& range );
+
+  /**
    * Play the animation.
    */
   void Play();
@@ -251,7 +260,7 @@ protected:
   /**
    * Protected constructor. See New()
    */
-  Animation( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction );
+  Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction destroyAction );
 
 
 private:
@@ -289,6 +298,7 @@ protected:
   float mElapsedSeconds;
   int mPlayCount;
 
+  Vector2 mPlayRange;
   AnimatorContainer mAnimators;
 };
 
@@ -368,6 +378,17 @@ inline void SetSpeedFactorMessage( EventToUpdate& eventToUpdate, const Animation
   new (slot) LocalType( &animation, &Animation::SetSpeedFactor, factor );
 }
 
+inline void SetPlayRangeMessage( EventToUpdate& eventToUpdate, const Animation& animation, const Vector2& range )
+{
+  typedef MessageValue1< Animation, Vector2 > 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::SetPlayRange, range );
+}
+
 inline void PlayAnimationMessage( EventToUpdate& eventToUpdate, const Animation& animation )
 {
   typedef Message< Animation > LocalType;
index 5d908d6..f09fab0 100644 (file)
@@ -488,14 +488,14 @@ void Animation::Resize(Actor actor, Vector3 size, AlphaFunction alpha, float del
   GetImplementation(*this).Resize(GetImplementation(actor), size, alpha, delaySeconds, durationSeconds);
 }
 
-float Animation::GetCurrentProgress()
+void Animation::SetCurrentProgress( float progress )
 {
-  return GetImplementation(*this).GetCurrentProgress();
+  return GetImplementation(*this).SetCurrentProgress( progress );
 }
 
-void Animation::SetCurrentProgress( float progress )
+float Animation::GetCurrentProgress()
 {
-  return GetImplementation(*this).SetCurrentProgress( progress );
+  return GetImplementation(*this).GetCurrentProgress();
 }
 
 void Animation::SetSpeedFactor( float factor )
@@ -508,4 +508,14 @@ float Animation::GetSpeedFactor() const
   return GetImplementation(*this).GetSpeedFactor();
 }
 
+void Animation::SetPlayRange( const Vector2& range )
+{
+  GetImplementation(*this).SetPlayRange(range);
+}
+
+Vector2 Animation::GetPlayRange() const
+{
+  return GetImplementation(*this).GetPlayRange();
+}
+
 } // namespace Dali
index 828c405..b2837d1 100644 (file)
@@ -274,6 +274,17 @@ public:
    */
   AlphaFunction GetDefaultAlphaFunction() const;
 
+  /*
+   * @brief Sets the progress of the animation.
+   * The animation will play (or continue playing) from this point. The progress
+   * must be in the 0-1 interval or in the play range interval if defined ( See SetPlayRange ),
+   * otherwise, it will be ignored.
+   *
+   * @param[in] progress The new progress as a normalized value between [0,1] or between the
+   * play range if specified.
+   */
+  void SetCurrentProgress( float progress );
+
   /**
   * @brief Retrieve the current progress of the animation.
   *
@@ -299,13 +310,22 @@ public:
    */
   float GetSpeedFactor() const;
 
-  /*
-   * @brief Sets the progress of the animation.
-   * The animation will play (or continue playing) from this point
+  /**
+   * @brief Set the playing range.
+   * Animation will play between the values specified. Both values ( range.x and range.y ) should be between 0-1,
+   * otherwise they will be ignored. If the range provided is not in proper order ( minimum,maximum ), it will be reordered.
    *
-   * @param[in] progress The new progress as a normalized value between [0,1].
+   * @param[in] range Two values between [0,1] to specify minimum and maximum progress. The
+   * animation will play between those values.
    */
-  void SetCurrentProgress( float progress );
+  void SetPlayRange( const Vector2& range );
+
+  /**
+   * @brief Get the playing range
+   *
+   * @return The play range defined for the animation.
+   */
+  Vector2 GetPlayRange() const;
 
   /**
    * @brief Play the animation.
@@ -314,7 +334,10 @@ public:
 
   /**
    * @brief Play the animation from a given point.
-   * @param[in] progress A value between [0,1] form where the animation should start playing
+   * The progress must be in the 0-1 interval or in the play range interval if defined ( See SetPlayRange ),
+   * otherwise, it will be ignored.
+   *
+   * @param[in] progress A value between [0,1], or between the play range if specified, form where the animation should start playing
    */
   void PlayFrom( float progress );