Implement Animation PlayAfter() API 02/139402/11
authorSeoyeon Kim <seoyeon2.kim@samsung.com>
Wed, 19 Jul 2017 00:53:02 +0000 (09:53 +0900)
committerSeoyeon Kim <seoyeon2.kim@samsung.com>
Wed, 26 Jul 2017 02:58:53 +0000 (11:58 +0900)
- PlayAfter() API is to play Animation after the given time.

Change-Id: I177fed58bb2bdfd9df1f7acec7a9acbd83f8453e
Signed-off-by: Seoyeon Kim <seoyeon2.kim@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/devel-api/animation/animation-devel.cpp
dali/devel-api/animation/animation-devel.h
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/internal/update/animation/scene-graph-animator.h

index a11ca78..d753f98 100644 (file)
@@ -11353,6 +11353,190 @@ int UtcDaliAnimationProgressCallbackP(void)
   END_TEST;
 }
 
+int UtcDaliAnimationPlayAfterP(void)
+{
+  TestApplication application;
+
+  tet_printf("Testing that playing after 2 seconds\n");
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+
+  bool signalReceived( false );
+  AnimationFinishCheck finishCheck( signalReceived );
+  animation.FinishedSignal().Connect( &application, finishCheck );
+  application.SendNotification();
+
+  Vector3 targetPosition( 100.0f, 100.0f, 100.0f );
+  animation.AnimateTo( Property( actor, Actor::Property::POSITION ), targetPosition, AlphaFunction::LINEAR, TimePeriod( 0.5f, 0.5f ) );
+
+  // Play animation after the initial delay time
+  DevelAnimation::PlayAfter( animation, 0.2f );
+  application.SendNotification();
+  application.Render(0); // start animation
+
+  application.Render( durationSeconds * 200.f ); // The intial delay time of PlayAfter
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.0f ), TEST_LOCATION ); // Not move
+
+  application.Render( static_cast< unsigned int >( durationSeconds * 500.0f )/* 50% animation progress, 0% animator progress */ );
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.0f ), TEST_LOCATION ); // Not move - A delay time of TimePeriod in seconds
+
+  application.SendNotification();
+  application.Render( static_cast< unsigned int >( durationSeconds * 250.0f )/* 75% animation progress, 50% animator progress */ );
+
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.5f ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render( static_cast< unsigned int >( durationSeconds * 250.0f ) + 1u/*just beyond the animation 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( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayAfterP2(void)
+{
+  TestApplication application;
+
+  tet_printf("Testing that playing after 2 seconds before looping\n");
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+  animation.SetLooping( true );
+
+  bool signalReceived( false );
+  AnimationFinishCheck finishCheck( signalReceived );
+  animation.FinishedSignal().Connect( &application, finishCheck );
+  application.SendNotification();
+
+  Vector3 targetPosition( 100.0f, 100.0f, 100.0f );
+  animation.AnimateTo( Property( actor, Actor::Property::POSITION ), targetPosition, AlphaFunction::LINEAR, TimePeriod( 0.5f, 0.5f ) );
+
+  // Play animation after the initial delay time
+  DevelAnimation::PlayAfter( animation, 0.2f );
+  application.SendNotification();
+  application.Render(0); // start animation
+
+  for( int iterations = 0; iterations < 3; ++iterations )
+  {
+    // The initial delay time of PlayAfter() applies only once in looping mode.
+    if( iterations == 0 )
+    {
+      application.Render( durationSeconds * 200.f ); // The intial delay time of PlayAfter
+      application.SendNotification();
+      finishCheck.CheckSignalNotReceived();
+      DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.0f ), TEST_LOCATION ); // Not move
+    }
+
+    application.Render( static_cast< unsigned int >( durationSeconds * 500.0f )/* 50% animation progress, 0% animator progress */ );
+
+    // We didn't expect the animation to finish yet
+    application.SendNotification();
+    finishCheck.CheckSignalNotReceived();
+    DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.0f ), TEST_LOCATION ); // Not move - A delay time of TimePeriod in seconds
+
+    application.SendNotification();
+    application.Render( static_cast< unsigned int >( durationSeconds * 250.0f )/* 75% animation progress, 50% animator progress */ );
+
+    application.SendNotification();
+    finishCheck.CheckSignalNotReceived();
+    DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.5f ), TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render( static_cast< unsigned int >( durationSeconds * 250.0f ) /* 100% progress */ );
+
+    // We did expect the animation to finish
+    application.SendNotification();
+    finishCheck.CheckSignalNotReceived();
+    DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+  }
+
+  animation.SetLooping(false);
+  application.SendNotification();
+  application.Render( static_cast< unsigned int >( durationSeconds * 1000.0f ) + 1u /*just beyond the animation duration*/ );
+
+  application.SendNotification();
+  finishCheck.CheckSignalReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayAfterP3(void)
+{
+  TestApplication application;
+
+  tet_printf("Testing that PlayAfter with the negative delay seconds\n");
+
+  Actor actor = Actor::New();
+  Stage::GetCurrent().Add(actor);
+
+  // Build the animation
+  float durationSeconds(1.0f);
+  Animation animation = Animation::New(durationSeconds);
+
+  bool signalReceived( false );
+  AnimationFinishCheck finishCheck( signalReceived );
+  animation.FinishedSignal().Connect( &application, finishCheck );
+  application.SendNotification();
+
+  Vector3 targetPosition( 100.0f, 100.0f, 100.0f );
+  animation.AnimateTo( Property( actor, Actor::Property::POSITION ), targetPosition, AlphaFunction::LINEAR, TimePeriod( 0.5f, 0.5f ) );
+
+  // When the delay time is negative value, it would treat as play immediately.
+  DevelAnimation::PlayAfter( animation, -2.0f );
+  application.SendNotification();
+  application.Render(0); // start animation
+
+  application.Render( static_cast< unsigned int >( durationSeconds * 500.0f )/* 50% animation progress, 0% animator progress */ );
+
+  // We didn't expect the animation to finish yet
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.0f ), TEST_LOCATION ); // Not move - A delay time of TimePeriod in seconds
+
+  application.SendNotification();
+  application.Render( static_cast< unsigned int >( durationSeconds * 250.0f )/* 75% animation progress, 50% animator progress */ );
+
+  application.SendNotification();
+  finishCheck.CheckSignalNotReceived();
+  DALI_TEST_EQUALS( actor.GetCurrentPosition(), ( targetPosition * 0.5f ), TEST_LOCATION );
+
+  application.SendNotification();
+  application.Render( static_cast< unsigned int >( durationSeconds * 250.0f ) + 1u/*just beyond the animation 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( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION );
+  END_TEST;
+}
+
 int UtcDaliAnimationProgressSignalConnectionWithoutProgressMarkerP(void)
 {
   TestApplication application;
index 0840ae7..5484256 100644 (file)
@@ -40,6 +40,11 @@ Animation::AnimationSignalType& ProgressReachedSignal( Animation animation )
   return GetImplementation( animation ).ProgressReachedSignal();
 }
 
+void PlayAfter( Animation animation, float delaySeconds )
+{
+  GetImplementation( animation ).PlayAfter( delaySeconds );
+}
+
 } // namespace DevelAnimation
 
 } // namespace Dali
index b106f6f..4efa70a 100644 (file)
@@ -51,6 +51,16 @@ DALI_IMPORT_API float GetProgressNotification( Animation animation );
  */
 DALI_IMPORT_API Animation::AnimationSignalType& ProgressReachedSignal( Animation animation );
 
+/**
+ * @brief Play the animation after a given delay time.
+ *
+ * The delay time is not included in the looping time.
+ * When the delay time is negative value, it would treat as play immediately.
+ * @param[in] animation The animation object to perform this operation on
+ * @param[in] delaySeconds The delay time
+ */
+DALI_IMPORT_API void PlayAfter( Animation animation, float delaySeconds );
+
 } // namespace DevelAnimation
 
 } // namespace Dali
index a985f71..b7cc899 100644 (file)
@@ -129,7 +129,8 @@ Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylis
   mDisconnectAction( disconnectAction ),
   mDefaultAlpha( defaultAlpha ),
   mState(Dali::Animation::STOPPED),
-  mProgressReachedMarker( 0.0f )
+  mProgressReachedMarker( 0.0f ),
+  mDelaySeconds( 0.0f )
 {
 }
 
@@ -301,6 +302,26 @@ void Animation::PlayFrom( float progress )
   }
 }
 
+void Animation::PlayAfter( float delaySeconds )
+{
+  // The negative delay means play immediately.
+  delaySeconds = std::max( 0.f, delaySeconds );
+
+  mDelaySeconds = delaySeconds;
+
+  // Update the current playlist
+  mPlaylist.OnPlay( *this );
+
+  mState = Dali::Animation::PLAYING;
+
+  NotifyObjects();
+
+  SendFinalProgressNotificationMessage();
+
+  // mAnimation is being used in a separate thread; queue a message to set the value
+  PlayAfterMessage( mEventThreadServices, *mAnimation, delaySeconds );
+}
+
 void Animation::Pause()
 {
   mState = Dali::Animation::PAUSED;
index 6135cc7..bf1e628 100644 (file)
@@ -170,6 +170,11 @@ public:
   void PlayFrom( float progress );
 
   /**
+   * @copydoc Dali::Animation::PlayAfter()
+   */
+  void PlayAfter( float delaySeconds );
+
+  /**
    * @copydoc Dali::Animation::Pause()
    */
   void Pause();
@@ -534,6 +539,7 @@ private:
   AlphaFunction mDefaultAlpha;
   Dali::Animation::State mState;
   float mProgressReachedMarker;
+  float mDelaySeconds;
 };
 
 } // namespace Internal
index ccd0894..9ce8ef0 100644 (file)
@@ -24,7 +24,7 @@
 // INTERNAL INCLUDES
 #include <dali/internal/common/memory-pool-object-allocator.h>
 #include <dali/internal/render/common/performance-monitor.h>
-
+#include <dali/public-api/math/math-utils.h>
 namespace //Unnamed namespace
 {
 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
@@ -45,7 +45,7 @@ inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSecon
 /// Compares the end times of the animators and if the end time is less, then it is moved earlier in the list. If end times are the same, then no change.
 bool CompareAnimatorEndTimes( const Dali::Internal::SceneGraph::AnimatorBase* lhs, const Dali::Internal::SceneGraph::AnimatorBase* rhs )
 {
-  return ( ( lhs->GetInitialDelay() + lhs->GetDuration() ) < ( rhs->GetInitialDelay() + rhs->GetDuration() ) );
+  return ( ( lhs->GetIntervalDelay() + lhs->GetDuration() ) < ( rhs->GetIntervalDelay() + rhs->GetDuration() ) );
 }
 
 } // unnamed namespace
@@ -65,17 +65,18 @@ Animation* Animation::New( float durationSeconds, float speedFactor, const Vecto
 }
 
 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
-: mDurationSeconds(durationSeconds),
+: mPlayRange( playRange ),
+  mDurationSeconds( durationSeconds ),
+  mDelaySeconds( 0.0f ),
+  mElapsedSeconds( playRange.x*mDurationSeconds ),
   mSpeedFactor( speedFactor ),
+  mProgressMarker( 0.0f ),
+  mPlayedCount( 0 ),
+  mLoopCount(loopCount),
+  mCurrentLoop(0),
   mEndAction(endAction),
   mDisconnectAction(disconnectAction),
   mState(Stopped),
-  mElapsedSeconds(playRange.x*mDurationSeconds),
-  mPlayedCount(0),
-  mLoopCount(loopCount),
-  mCurrentLoop(0),
-  mPlayRange( playRange ),
-  mProgressMarker(0.0f),
   mProgressReachedSignalRequired( false )
 {
 }
@@ -177,6 +178,24 @@ void Animation::PlayFrom( float progress )
   }
 }
 
+void Animation::PlayAfter( float delaySeconds )
+{
+  if( mState != Playing )
+  {
+    mDelaySeconds = delaySeconds;
+    mState = Playing;
+
+    if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
+    {
+      mElapsedSeconds = mPlayRange.y * mDurationSeconds;
+    }
+
+    SetAnimatorsActive( true );
+
+    mCurrentLoop = 0;
+  }
+}
+
 void Animation::Pause()
 {
   if (mState == Playing)
@@ -281,13 +300,21 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
   // The animation must still be applied when Paused/Stopping
   if (mState == Playing)
   {
-    mElapsedSeconds += elapsedSeconds * mSpeedFactor;
-
-    if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
+    // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
+    if( mDelaySeconds > 0)
     {
-      // The application should be notified by NotificationManager, in another thread
-      progressReached = true;
-      mProgressReachedSignalRequired = false;
+      mDelaySeconds = mDelaySeconds - ( elapsedSeconds * mSpeedFactor );
+    }
+    else
+    {
+      mElapsedSeconds += elapsedSeconds * mSpeedFactor;
+
+      if ( mProgressReachedSignalRequired && ( mElapsedSeconds >= mProgressMarker ) )
+      {
+        // The application should be notified by NotificationManager, in another thread
+        progressReached = true;
+        mProgressReachedSignalRequired = false;
+      }
     }
   }
 
@@ -369,15 +396,16 @@ void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animat
     {
       if( animator->IsEnabled() )
       {
-        const float initialDelay( animator->GetInitialDelay() );
-        if( elapsedSecondsClamped >= initialDelay )
+        const float intervalDelay( animator->GetIntervalDelay() );
+
+        if( elapsedSecondsClamped >= intervalDelay )
         {
           // 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 );
+            progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
           }
           animator->Update(bufferIndex, progress, bake);
         }
index 4f2095e..3e27055 100644 (file)
@@ -204,13 +204,19 @@ public:
    */
   void Play();
 
-  /*
+  /**
    * Play the animation from a given point
    * @param[in] progress A value between [0,1] form where the animation should start playing
    */
   void PlayFrom( float progress );
 
   /**
+   * @brief Play the animation after a given delay time.
+   * @param[in] delaySeconds The delay time
+   */
+  void PlayAfter( float delaySeconds );
+
+  /**
    * Pause the animation.
    */
   void Pause();
@@ -325,24 +331,27 @@ private:
 
 protected:
 
+  AnimatorContainer mAnimators;
+
+  Vector2 mPlayRange;
+
   float mDurationSeconds;
+  float mDelaySeconds;
+  float mElapsedSeconds;
   float mSpeedFactor;
-  EndAction mEndAction;
-  EndAction mDisconnectAction;
+  float mProgressMarker;         // Progress marker to trigger a notification
 
-  State mState;
-  float mElapsedSeconds;
   int mPlayedCount;              // Incremented at end of animation or completion of all loops
                                  // Never incremented when looping forever. Event thread tracks to signal end.
   int mLoopCount;                // N loop setting
   int mCurrentLoop;              // Current loop number
 
-  Vector2 mPlayRange;
+  EndAction mEndAction;
+  EndAction mDisconnectAction;
 
-  float mProgressMarker;                // Progress marker to trigger a notification
-  bool mProgressReachedSignalRequired;  // Flag to indicate the progress marker was hit
+  State mState;
 
-  AnimatorContainer mAnimators;
+  bool mProgressReachedSignalRequired;  // Flag to indicate the progress marker was hit
 };
 
 }; //namespace SceneGraph
@@ -489,6 +498,16 @@ inline void AddAnimatorMessage( EventThreadServices& eventThreadServices, const
   new (slot) LocalType( &animation, &Animation::AddAnimator, parameter );
 }
 
+inline void PlayAfterMessage( EventThreadServices& eventThreadServices, const Animation& animation, float delaySeconds )
+{
+  typedef MessageValue1< Animation, float > LocalType;
+
+  // Reserve some memory inside the message queue
+  unsigned int* slot = eventThreadServices.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::PlayAfter, delaySeconds );
+}
 
 } // namespace SceneGraph
 
index 3fe041b..d1cac15 100644 (file)
@@ -69,7 +69,7 @@ public:
    */
   AnimatorBase()
   : mDurationSeconds(1.0f),
-    mInitialDelaySeconds(0.0f),
+    mIntervalDelaySeconds(0.0f),
     mAlphaFunction(AlphaFunction::DEFAULT),
     mDisconnectAction(Dali::Animation::BakeFinal),
     mActive(false),
@@ -116,18 +116,18 @@ public:
    * The default is zero i.e. no delay.
    * @param [in] seconds The delay in seconds.
    */
-  void SetInitialDelay(float seconds)
+  void SetIntervalDelay(float seconds)
   {
-    mInitialDelaySeconds = seconds;
+    mIntervalDelaySeconds = seconds;
   }
 
   /**
-   * Retrieve the initial delay of the animator.
+   * Retrieve the delay before the animator should take effect.
    * @return The delay in seconds.
    */
-  float GetInitialDelay() const
+  float GetIntervalDelay() const
   {
-    return mInitialDelaySeconds;
+    return mIntervalDelaySeconds;
   }
 
   /**
@@ -357,7 +357,7 @@ protected:
   }
 
   float mDurationSeconds;
-  float mInitialDelaySeconds;
+  float mIntervalDelaySeconds;
 
   AlphaFunction mAlphaFunction;
 
@@ -397,7 +397,7 @@ public:
                                                animatorFunction );
 
     animator->SetAlphaFunction( alphaFunction );
-    animator->SetInitialDelay( timePeriod.delaySeconds );
+    animator->SetIntervalDelay( timePeriod.delaySeconds );
     animator->SetDuration( timePeriod.durationSeconds );
 
     return animator;
@@ -550,7 +550,7 @@ public:
                                                animatorFunction );
 
     animator->SetAlphaFunction( alphaFunction );
-    animator->SetInitialDelay( timePeriod.delaySeconds );
+    animator->SetIntervalDelay( timePeriod.delaySeconds );
     animator->SetDuration( timePeriod.durationSeconds );
 
     return animator;