(Animation) Update object's event-side properties if we stop/pause/clear an animation 39/211739/3
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 8 Aug 2019 12:12:17 +0000 (13:12 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 8 Aug 2019 15:07:51 +0000 (16:07 +0100)
Change-Id: Ia1a966db81d7f18eed9d0b7ea4e3a8ff1644b753

automated-tests/src/dali/utc-Dali-Animation.cpp
automated-tests/src/dali/utc-Dali-Renderer.cpp
dali/internal/event/animation/animation-impl.cpp
dali/internal/event/animation/animation-impl.h

index 254c862..41517d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -42,6 +42,7 @@ namespace
 
 static const float ROTATION_EPSILON = 0.0001f;
 static const float VECTOR4_EPSILON = 0.0001f;
+static const float VECTOR3_EPSILON = 0.0001f;
 
 // Functor to test whether a Finish signal is emitted
 struct AnimationFinishCheck
@@ -815,7 +816,7 @@ int UtcDaliAnimationIsLoopingP(void)
   END_TEST;
 }
 
-int UtcDaliAnimationSetEndActioN(void)
+int UtcDaliAnimationSetEndActionN(void)
 {
   TestApplication application;
 
@@ -13311,3 +13312,145 @@ int UtcDaliAnimationAnimateBetweenInvalidParameters(void)
 
   END_TEST;
 }
+
+namespace // Purposefully left this in the middle as the values in this namespace are only used for the subsequent two test cases
+{
+enum TestFunction
+{
+  STOP,
+  CLEAR
+};
+
+void CheckPropertyValuesWhenCallingAnimationMethod( TestFunction functionToTest, const char * testName )
+{
+  tet_printf( "Testing %s\n", testName );
+
+  // When an Animation::Stop() or Animation::Clear() is called, the event-side property needs to be updated appropriately
+  // This test checks that that is being done
+
+  const float durationSeconds( 1.0f );
+  unsigned int halfAnimationDuration( static_cast< unsigned int >( durationSeconds * 1000.0f * 0.5f ) );
+  const Vector3 originalPosition( Vector3::ZERO );
+  const Vector3 targetPosition( 10.0f, 10.0f, 10.0f );
+  const Vector3 halfWayToTarget( targetPosition * 0.5f );
+
+  struct ExpectedValue
+  {
+    Animation::EndAction endAction;
+    Vector3 expectedGetPropertyValue;
+  };
+
+  ExpectedValue expectedValueTable[] =
+  {
+   { Animation::Bake,      halfWayToTarget  }, // When baking, the current value is the final value.
+   { Animation::BakeFinal, targetPosition   }, // When BakeFinal, we should jump to the final value when clearing or stopping.
+   { Animation::Discard,   originalPosition }, // When discarding, we should jump back to the original value when clearing or stopping.
+  };
+  const auto expectedValueTableCount = sizeof( expectedValueTable ) / sizeof( ExpectedValue );
+
+  for( auto i = 0u; i < expectedValueTableCount; ++i  )
+  {
+    TestApplication application;
+
+    Actor actor = Actor::New();
+    Stage::GetCurrent().Add(actor);
+
+    // Build the animation
+    Animation animation = Animation::New( durationSeconds );
+    animation.SetEndAction( expectedValueTable[ i ].endAction );
+    animation.AnimateTo( Property( actor, Actor::Property::POSITION ), targetPosition, AlphaFunction::LINEAR );
+
+    // Start the animation
+    animation.Play();
+
+    application.SendNotification();
+    application.Render( halfAnimationDuration );
+
+    // Stop or Clear the animation early, both have the same effect
+    if( functionToTest == TestFunction::STOP )
+    {
+      animation.Stop();
+    }
+    else
+    {
+      animation.Clear();
+    }
+
+    // The event side property should be set the expected value immediately, the update side property will still only be halfway as we haven't run an update yet
+    DALI_TEST_EQUALS( actor.GetProperty( Actor::Property::POSITION ).Get< Vector3 >(), expectedValueTable[ i ].expectedGetPropertyValue, VECTOR3_EPSILON, TEST_LOCATION );
+    DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), halfWayToTarget, VECTOR3_EPSILON, TEST_LOCATION );
+
+    // After one frame, both values should match regardless of the End Action
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( actor.GetProperty( Actor::Property::POSITION ).Get< Vector3 >(), expectedValueTable[ i ].expectedGetPropertyValue, VECTOR3_EPSILON, TEST_LOCATION );
+    DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), expectedValueTable[ i ].expectedGetPropertyValue, VECTOR3_EPSILON, TEST_LOCATION );
+  }
+}
+} // unnamed namespace
+
+int UtcDaliAnimationStopPropertyValue(void)
+{
+  CheckPropertyValuesWhenCallingAnimationMethod( TestFunction::STOP, "UtcDaliAnimationStopPropertyValue" );
+  END_TEST;
+}
+
+int UtcDaliAnimationClearPropertyValue(void)
+{
+  CheckPropertyValuesWhenCallingAnimationMethod( TestFunction::CLEAR, "UtcDaliAnimationStopPropertyValue" );
+  END_TEST;
+}
+
+int UtcDaliAnimationPausePropertyValue(void)
+{
+  const float durationSeconds( 1.0f );
+  unsigned int halfAnimationDuration( static_cast< unsigned int >( durationSeconds * 1000.0f * 0.5f ) );
+  const Vector3 originalPosition( Vector3::ZERO );
+  const Vector3 targetPosition( 10.0f, 10.0f, 10.0f );
+  const Vector3 halfWayToTarget( targetPosition * 0.5f );
+
+  Animation::EndAction endActions[] =
+  {
+   Animation::Bake,
+   Animation::BakeFinal,
+   Animation::Discard,
+  };
+  const auto endActionCount = sizeof( endActions ) / sizeof( endActions[0] );
+
+  // For all end actions, when pausing, we stay at the current value
+  for( auto i = 0u; i < endActionCount; ++i  )
+  {
+    TestApplication application;
+
+    Actor actor = Actor::New();
+    Stage::GetCurrent().Add(actor);
+
+    // Build the animation
+    Animation animation = Animation::New( durationSeconds );
+    animation.SetEndAction( endActions[ i ] );
+    animation.AnimateTo( Property( actor, Actor::Property::POSITION ), targetPosition, AlphaFunction::LINEAR );
+
+    // Start the animation
+    animation.Play();
+
+    application.SendNotification();
+    application.Render( halfAnimationDuration );
+
+    // Puase the animation early
+    animation.Pause();
+
+    // The event side property should be set the current value immediately, the update side property will still only be halfway
+    DALI_TEST_EQUALS( actor.GetProperty( Actor::Property::POSITION ).Get< Vector3 >(), halfWayToTarget, VECTOR3_EPSILON, TEST_LOCATION );
+    DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), halfWayToTarget, VECTOR3_EPSILON, TEST_LOCATION );
+
+    // After one frame, both values should match regardless of the End Action
+    application.SendNotification();
+    application.Render();
+
+    DALI_TEST_EQUALS( actor.GetProperty( Actor::Property::POSITION ).Get< Vector3 >(), halfWayToTarget, VECTOR3_EPSILON, TEST_LOCATION );
+    DALI_TEST_EQUALS( actor.GetCurrentProperty( Actor::Property::POSITION ).Get< Vector3 >(), halfWayToTarget, VECTOR3_EPSILON, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
index d1c81f6..0410a52 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -2897,9 +2897,10 @@ int UtcDaliRendererOpacityAnimation(void)
   DALI_TEST_CHECK( value.Get( opacity ) );
   DALI_TEST_EQUALS( opacity, 0.0f, Dali::Math::MACHINE_EPSILON_1, TEST_LOCATION );
 
+  // Need to clear the animation before setting the property as the animation value is baked and will override any previous setters
+  animation.Clear();
   renderer.SetProperty( DevelRenderer::Property::OPACITY, 0.1f );
 
-  animation.Clear();
   animation.AnimateBy( Property( renderer, DevelRenderer::Property::OPACITY ), 0.5f );
   animation.Play();
 
@@ -2909,6 +2910,7 @@ int UtcDaliRendererOpacityAnimation(void)
   value = renderer.GetProperty( DevelRenderer::Property::OPACITY );
   DALI_TEST_CHECK( value.Get( opacity ) );
   DALI_TEST_EQUALS( opacity, 0.6f, Dali::Math::MACHINE_EPSILON_1, TEST_LOCATION );
+  DALI_TEST_EQUALS( opacity, renderer.GetCurrentProperty( DevelRenderer::Property::OPACITY ).Get< float >(), Dali::Math::MACHINE_EPSILON_1, TEST_LOCATION );
 
   END_TEST;
 }
index 876ec29..ceab690 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -319,7 +319,7 @@ void Animation::Play()
 
   mState = Dali::Animation::PLAYING;
 
-  NotifyObjects();
+  NotifyObjects( Notify::USE_TARGET_VALUE );
 
   SendFinalProgressNotificationMessage();
 
@@ -336,7 +336,7 @@ void Animation::PlayFrom( float progress )
 
     mState = Dali::Animation::PLAYING;
 
-    NotifyObjects();
+    NotifyObjects( Notify::USE_TARGET_VALUE );
 
     SendFinalProgressNotificationMessage();
 
@@ -357,7 +357,7 @@ void Animation::PlayAfter( float delaySeconds )
 
   mState = Dali::Animation::PLAYING;
 
-  NotifyObjects();
+  NotifyObjects( Notify::USE_TARGET_VALUE );
 
   SendFinalProgressNotificationMessage();
 
@@ -371,6 +371,9 @@ void Animation::Pause()
 
   // mAnimation is being used in a separate thread; queue a Pause message
   PauseAnimationMessage( mEventThreadServices, *mAnimation );
+
+  // Notify the objects with the _paused_, i.e. current values
+  NotifyObjects( Notify::FORCE_CURRENT_VALUE );
 }
 
 Dali::Animation::State Animation::GetState() const
@@ -384,12 +387,24 @@ void Animation::Stop()
 
   // mAnimation is being used in a separate thread; queue a Stop message
   StopAnimationMessage( mEventThreadServices.GetUpdateManager(), *mAnimation );
+
+  // Only notify the objects with the _stopped_, i.e. current values if the end action is set to BAKE
+  if( mEndAction == EndAction::Bake )
+  {
+    NotifyObjects( Notify::USE_CURRENT_VALUE );
+  }
 }
 
 void Animation::Clear()
 {
   DALI_ASSERT_DEBUG(mAnimation);
 
+  // Only notify the objects with the current values if the end action is set to BAKE
+  if( mEndAction == EndAction::Bake )
+  {
+    NotifyObjects( Notify::USE_CURRENT_VALUE );
+  }
+
   // Remove all the connectors
   mConnectors.Clear();
 
@@ -1073,12 +1088,17 @@ bool Animation::CompareConnectorEndTimes( const Animation::ConnectorTargetValues
   return ( ( lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds ) < ( rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds ) );
 }
 
-void Animation::NotifyObjects()
+void Animation::NotifyObjects( Animation::Notify notifyValueType )
 {
-  if( mEndAction != EndAction::Discard ) // If the animation is discarded, then we do not want to change the target values
+  // If the animation is discarded, then we do not want to change the target values unless we want to force the current values
+  if( mEndAction != EndAction::Discard || notifyValueType == Notify::FORCE_CURRENT_VALUE )
   {
     // Sort according to end time with earlier end times coming first, if the end time is the same, then the connectors are not moved
-    std::stable_sort( mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes );
+    // Only do this if we're using the target value
+    if( notifyValueType == Notify::USE_TARGET_VALUE )
+    {
+      std::stable_sort( mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes );
+    }
 
     // Loop through all connector target values sorted by increasing end time
     ConnectorTargetValuesContainer::const_iterator iter = mConnectorTargetValues.begin();
@@ -1090,7 +1110,12 @@ void Animation::NotifyObjects()
       Object* object = connector->GetObject();
       if( object )
       {
-        object->NotifyPropertyAnimation( *this, connector->GetPropertyIndex(), iter->targetValue, iter->animatorType );
+        const auto propertyIndex = connector->GetPropertyIndex();
+        object->NotifyPropertyAnimation(
+          *this,
+          propertyIndex,
+          ( notifyValueType == Notify::USE_TARGET_VALUE ) ? iter->targetValue : object->GetCurrentProperty( propertyIndex ),
+          iter->animatorType );
       }
     }
   }
index 124140c..454b066 100644 (file)
@@ -490,6 +490,13 @@ private:
     Animation::Type animatorType;
   };
 
+  enum class Notify
+  {
+    USE_CURRENT_VALUE,   ///< Set the current value for the property
+    USE_TARGET_VALUE,    ///< Set the animator's target value for the property
+    FORCE_CURRENT_VALUE, ///< Set the current value for the property even if the end action is to discard
+  };
+
 private:
 
   /**
@@ -502,8 +509,9 @@ private:
 
   /**
    * Notifies all the objects whose properties are being animated.
+   * @param[in] notifyValueType Whether we should set the current or target value
    */
-  void NotifyObjects();
+  void NotifyObjects( Notify notifyValueType );
 
   /**
    * Sends message to SceneGraph with final progress value