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 a11ca7856cd90c73ee62bfdc4930186869d59353..d753f985012e7a89baaba23fc19b4a77e59d3bc6 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 0840ae7d914dbb46e7345cbe3702efccbfc404ad..54842568bbe6a35bc3fb11b8d4a61b4a472a45e2 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 b106f6fc10f3fe455b92b4b42f3a57cebc4a084b..4efa70a20548f3628ee20a46da8ffaecfa9a4d8e 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 a985f71b484904fe53fdae22057a23bad7577d87..b7cc8995b0dd1132253a826b0596fb94af045243 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 6135cc745956b7794f25fa3c1d96497d80a9a0ff..bf1e628a3d1edc04d83c7e730390ae4b093ae10e 100644 (file)
@@ -169,6 +169,11 @@ public:
    */
   void PlayFrom( float progress );
 
+  /**
+   * @copydoc Dali::Animation::PlayAfter()
+   */
+  void PlayAfter( float delaySeconds );
+
   /**
    * @copydoc Dali::Animation::Pause()
    */
@@ -534,6 +539,7 @@ private:
   AlphaFunction mDefaultAlpha;
   Dali::Animation::State mState;
   float mProgressReachedMarker;
+  float mDelaySeconds;
 };
 
 } // namespace Internal
index ccd089469442809d135214cb6791695a0498963e..9ce8ef0b4af1ec29a0f3cf2ff49575e01427ef80 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 4f2095eadbd40d5acfa5b18e30f1f9c34266de7c..3e27055f4ada3db8e6a9daeda3264536cc954150 100644 (file)
@@ -204,12 +204,18 @@ 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.
    */
@@ -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 3fe041bd818d7c6a7b2a843e6ba963771b2ba6af..d1cac15b4d071fb1855412eb854a9343a080ea1b 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;