Merge "use modern construct '= default' for special functions." into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
index e1e027e..aa01b10 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 // 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
 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
 
-inline void WrapInPlayRange( float& elapsed, const Dali::Vector2& playRangeSeconds)
+inline void WrapInPlayRange( float& elapsed, const float& playRangeStartSeconds, const float& playRangeEndSeconds)
 {
-  if( elapsed > playRangeSeconds.y )
+  if( elapsed > playRangeEndSeconds )
   {
-    elapsed = playRangeSeconds.x + fmodf((elapsed-playRangeSeconds.x), (playRangeSeconds.y-playRangeSeconds.x));
+    elapsed = playRangeStartSeconds + fmodf( ( elapsed - playRangeStartSeconds ), ( playRangeEndSeconds - playRangeStartSeconds ) );
   }
-  else if( elapsed < playRangeSeconds.x )
+  else if( elapsed < playRangeStartSeconds )
   {
-    elapsed = playRangeSeconds.y - fmodf( (playRangeSeconds.x - elapsed), (playRangeSeconds.y-playRangeSeconds.x) );
+    elapsed = playRangeEndSeconds - fmodf( ( playRangeStartSeconds - elapsed ), ( playRangeEndSeconds - playRangeStartSeconds ) );
   }
 }
 
 /// 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
@@ -59,28 +59,31 @@ namespace Internal
 namespace SceneGraph
 {
 
-Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, EndAction endAction, EndAction disconnectAction )
+Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, int32_t loopCount, EndAction endAction, EndAction disconnectAction )
 {
   return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, loopCount, endAction, disconnectAction );
 }
 
-Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
-: mDurationSeconds(durationSeconds),
+Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, int32_t loopCount, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
+: 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 )
+  mProgressReachedSignalRequired( false ),
+  mAutoReverseEnabled( false ),
+  mIsActive{ false }
 {
 }
 
-Animation::~Animation()
-{
-}
+Animation::~Animation() = default;
 
 void Animation::operator delete( void* ptr )
 {
@@ -92,7 +95,16 @@ void Animation::SetDuration(float durationSeconds)
   mDurationSeconds = durationSeconds;
 }
 
-void Animation::SetLoopCount(int loopCount)
+void Animation::SetProgressNotification( float progress )
+{
+  mProgressMarker = progress;
+  if ( mProgressMarker > 0.0f )
+  {
+    mProgressReachedSignalRequired = true;
+  }
+}
+
+void Animation::SetLoopCount(int32_t loopCount)
 {
   mLoopCount = loopCount;
   mCurrentLoop = 0;
@@ -109,9 +121,9 @@ void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
   {
     mDisconnectAction = action;
 
-    for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+    for ( auto&& item : mAnimators )
     {
-      (*iter)->SetDisconnectAction( action );
+      item->SetDisconnectAction( action );
     }
   }
 }
@@ -163,6 +175,26 @@ void Animation::PlayFrom( float progress )
     mState = Playing;
 
     SetAnimatorsActive( true );
+
+    mCurrentLoop = 0;
+  }
+}
+
+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;
   }
 }
 
@@ -176,7 +208,7 @@ void Animation::Pause()
 
 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
 {
-  if( action == Dali::Animation::BakeFinal )
+  if( action == Dali::Animation::BAKE_FINAL )
   {
     if( mSpeedFactor > 0.0f )
     {
@@ -193,9 +225,9 @@ void Animation::Bake(BufferIndex bufferIndex, EndAction action)
 
 void Animation::SetAnimatorsActive( bool active )
 {
-  for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
+  for ( auto&& item : mAnimators )
   {
-    (*iter)->SetActive( active );
+    item->SetActive( active );
   }
 }
 
@@ -207,7 +239,7 @@ bool Animation::Stop(BufferIndex bufferIndex)
   {
     animationFinished = true; // The actor-thread should be notified of this
 
-    if( mEndAction != Dali::Animation::Discard )
+    if( mEndAction != Dali::Animation::DISCARD )
     {
       Bake( bufferIndex, mEndAction );
 
@@ -233,7 +265,7 @@ void Animation::OnDestroy(BufferIndex bufferIndex)
 {
   if (mState == Playing || mState == Paused)
   {
-    if (mEndAction != Dali::Animation::Discard)
+    if (mEndAction != Dali::Animation::DISCARD)
     {
       Bake( bufferIndex, mEndAction );
 
@@ -248,6 +280,19 @@ void Animation::OnDestroy(BufferIndex bufferIndex)
   mState = Destroyed;
 }
 
+void Animation::SetLoopingMode( bool loopingMode )
+{
+  mAutoReverseEnabled = loopingMode;
+
+  for ( auto&& item : mAnimators )
+  {
+    // Send some variables together to figure out the Animation status
+    item->SetSpeedFactor( mSpeedFactor );
+    item->SetLoopCount( mLoopCount );
+    item->SetLoopingMode( loopingMode );
+  }
+}
+
 void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
 {
   animator->ConnectToSceneGraph();
@@ -256,7 +301,7 @@ void Animation::AddAnimator( OwnerPointer<AnimatorBase>& animator )
   mAnimators.PushBack( animator.Release() );
 }
 
-void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished )
+void Animation::Update( BufferIndex bufferIndex, float elapsedSeconds, bool& looped, bool& finished, bool& progressReached )
 {
   looped = false;
   finished = false;
@@ -270,73 +315,124 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
   // The animation must still be applied when Paused/Stopping
   if (mState == Playing)
   {
-    mElapsedSeconds += elapsedSeconds * mSpeedFactor;
-  }
-
-  Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
-
-  if( 0 == mLoopCount )
-  {
-    // loop forever
-    WrapInPlayRange(mElapsedSeconds, playRangeSeconds);
+    // Sign value of speed factor. It can optimize many arithmetic comparision
+    float signSpeedFactor = ( mSpeedFactor < 0.0f ) ? -1.f : 1.f;
 
-    UpdateAnimators(bufferIndex, false, false);
+    // If there is delay time before Animation starts, wait the Animation until mDelaySeconds.
+    if( mDelaySeconds > 0.0f )
+    {
+      float reduceSeconds = fabsf( elapsedSeconds * mSpeedFactor );
+      if( reduceSeconds > mDelaySeconds )
+      {
+        // add overflowed time to mElapsedSecond.
+        // If speed factor > 0, add it. if speed factor < 0, subtract it.
+        float overflowSeconds = reduceSeconds - mDelaySeconds;
+        mElapsedSeconds += signSpeedFactor * overflowSeconds;
+        mDelaySeconds = 0.0f;
+      }
+      else
+      {
+        mDelaySeconds -= reduceSeconds;
+      }
+    }
+    else
+    {
+      mElapsedSeconds += ( elapsedSeconds * mSpeedFactor );
+    }
 
-    // don't increment mPlayedCount as event loop tracks this to indicate animation finished (end of all loops)
-  }
-  else if( mCurrentLoop < mLoopCount - 1) // '-1' here so last loop iteration uses play once below
-  {
-    // looping
-    looped =  (mState == Playing                                                 &&
-               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
-                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
+    const float playRangeStartSeconds = mPlayRange.x * mDurationSeconds;
+    const float playRangeEndSeconds = mPlayRange.y * mDurationSeconds;
+    // Final reached seconds. It can optimize many arithmetic comparision
+    float edgeRangeSeconds = ( mSpeedFactor < 0.0f ) ? playRangeStartSeconds : playRangeEndSeconds;
 
-    WrapInPlayRange( mElapsedSeconds, playRangeSeconds );
+    // Optimized Factors.
+    // elapsed >  edge   --> check if looped
+    // elapsed >= marker --> check if elapsed reached to marker in normal case
+    // edge    >= marker --> check if elapsed reached to marker in looped case
+    float elapsedFactor = signSpeedFactor * mElapsedSeconds;
+    float edgeFactor = signSpeedFactor * edgeRangeSeconds;
+    float markerFactor = signSpeedFactor * mProgressMarker;
 
-    UpdateAnimators(bufferIndex, false, false);
+    // check it is looped
+    looped = ( elapsedFactor > edgeFactor );
 
-    if(looped)
+    if( looped )
     {
-      ++mCurrentLoop;
-      // don't increment mPlayedCount until the finished final loop
-    }
-  }
-  else
-  {
-    // playing once (and last mCurrentLoop)
-    finished = (mState == Playing                                                 &&
-                (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
-                 ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x )) );
+      WrapInPlayRange( mElapsedSeconds, playRangeStartSeconds, playRangeEndSeconds );
 
-    // update with bake if finished
-    UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::Discard), finished);
-
-    if(finished)
-    {
-      // The animation has now been played to completion
-      ++mPlayedCount;
+      // Recalculate elapsedFactor here
+      elapsedFactor = signSpeedFactor * mElapsedSeconds;
 
-      // loop iterations come to this else branch for their final iterations
-      if( mCurrentLoop < mLoopCount)
+      if( mLoopCount != 0 )
       {
+        // Check If this animation is finished
         ++mCurrentLoop;
-        DALI_ASSERT_DEBUG(mCurrentLoop == mLoopCount);
+        if( mCurrentLoop >= mLoopCount )
+        {
+          DALI_ASSERT_DEBUG( mCurrentLoop == mLoopCount );
+          finished = true;
+
+          // The animation has now been played to completion
+          ++mPlayedCount;
+
+          // Make elapsed second as edge of range forcely.
+          mElapsedSeconds = edgeRangeSeconds + signSpeedFactor * Math::MACHINE_EPSILON_10;
+          UpdateAnimators(bufferIndex, finished && (mEndAction != Dali::Animation::DISCARD), finished );
+
+          // After update animation, mElapsedSeconds must be begin of value
+          mElapsedSeconds = playRangeStartSeconds + playRangeEndSeconds - edgeRangeSeconds;
+          mState = Stopped;
+        }
       }
 
-      mElapsedSeconds = playRangeSeconds.x;
-      mState = Stopped;
+      // when it is on looped state, 2 case to send progress signal.
+      // (require && range_value >= marker) ||         << Signal at previous loop
+      // (marker > 0 && !finished && elaped >= marker) << Signal at current loop
+      if( ( mProgressMarker > 0.0f ) && !finished && ( elapsedFactor >= markerFactor ) )
+      {
+        // The application should be notified by NotificationManager, in another thread
+        progressReached = true;
+        mProgressReachedSignalRequired = false;
+      }
+      else
+      {
+        if( mProgressReachedSignalRequired && ( edgeFactor >= markerFactor ) )
+        {
+          progressReached = true;
+        }
+        mProgressReachedSignalRequired = mProgressMarker > 0.0f;
+      }
+    }
+    else
+    {
+      // when it is not on looped state, only 1 case to send progress signal.
+      // (require && elaped >= marker)
+      if( mProgressReachedSignalRequired && ( elapsedFactor >= markerFactor ) )
+      {
+        // The application should be notified by NotificationManager, in another thread
+        progressReached = true;
+        mProgressReachedSignalRequired = false;
+      }
     }
   }
+
+  // Already updated when finished. So skip.
+  if( !finished )
+  {
+    UpdateAnimators(bufferIndex, false, false );
+  }
 }
 
 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
 {
+  mIsActive[bufferIndex] = false;
+
   const Vector2 playRange( mPlayRange * mDurationSeconds );
   float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
 
   //Loop through all animators
   bool applied(true);
-  for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
+  for ( auto&& iter = mAnimators.Begin(); iter != mAnimators.End(); )
   {
     AnimatorBase *animator = *iter;
 
@@ -349,17 +445,23 @@ 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);
+
+          if (animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
+          {
+            mIsActive[bufferIndex] = true;
+          }
         }
         applied = true;
       }