[Tizen] Support Animation Blend for AnimateBetween 50/292450/2
authorseungho baek <sbsh.baek@samsung.com>
Wed, 26 Apr 2023 13:01:15 +0000 (22:01 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Mon, 8 May 2023 08:49:59 +0000 (17:49 +0900)
Change-Id: Ifa53a54742239d8c2913499de2e29757bf8e9f72
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/internal/event/animation/animation-impl.cpp
dali/internal/event/animation/animation-impl.h
dali/internal/event/animation/animator-connector.h
dali/internal/event/animation/key-frames-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
dali/public-api/animation/animation.cpp
dali/public-api/animation/animation.h

index e09941a..1ee61ec 100644 (file)
@@ -14921,3 +14921,434 @@ int UtcDaliKeyFramesGetTypeNegative(void)
   }
   END_TEST;
 }
+
+int UtcDaliAnimationSetGetBlendPoint(void)
+{
+  TestApplication application;
+
+
+  Animation animation = Animation::New(1.0f);
+  DALI_TEST_EQUALS(animation.GetBlendPoint(), 0.0f, 0.01f, TEST_LOCATION);
+
+  animation.SetBlendPoint(0.5f);
+
+  DALI_TEST_EQUALS(animation.GetBlendPoint(), 0.5f, 0.01f, TEST_LOCATION);
+
+  animation.SetBlendPoint(-0.5f);
+
+  DALI_TEST_EQUALS(animation.GetBlendPoint(), 0.5f, 0.01f, TEST_LOCATION);
+
+  animation.SetBlendPoint(1.5f);
+
+  DALI_TEST_EQUALS(animation.GetBlendPoint(), 0.5f, 0.01f, TEST_LOCATION);
+
+  animation.SetBlendPoint(0.7f);
+
+  DALI_TEST_EQUALS(animation.GetBlendPoint(), 0.7f, 0.01f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendFloatCubic(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0.0f);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 3.0f);
+  keyframes.Add(0.4f, 1.0f);
+  keyframes.Add(0.6f, 1.0f);
+  keyframes.Add(1.0f, 3.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR, Animation::Interpolation::CUBIC);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  float value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 0.989258f, 0.05f, TEST_LOCATION); // original value : 1.603516 (Same value as when progress is 0.75.)
+                                                            // current value : 0.0f
+                                                            // value when progress is 0.5 : 0.75
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 0.750000f, 0.05f, TEST_LOCATION); // value is less than 1.0f
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.603516f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 3.0f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendFloat1(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0.0f);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 1.0f);
+  keyframes.Add(0.2f, 2.0f);
+  keyframes.Add(0.4f, 3.0f);
+  keyframes.Add(0.6f, 4.0f);
+  keyframes.Add(0.8f, 5.0f);
+  keyframes.Add(1.0f, 6.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.9f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  float value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.728395f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 3.302469f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 4.722222f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 6.0f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendFloat2(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0.0f);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 0.0f);
+  keyframes.Add(1.0f, 1.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  float value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 0.25f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 0.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendFloat3(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0.0f);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 1.0f);
+  keyframes.Add(1.0f, 2.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  float value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.0f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendFloat4(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0.0f);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 1.0f);
+  keyframes.Add(1.0f, 2.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  float value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.0f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.5f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(550);
+
+  actor.SetProperty(index, 0.0f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.0f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<float>(index);
+  DALI_TEST_EQUALS(value, 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendInt(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", 0);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, 100);
+  keyframes.Add(1.0f, 200);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  int32_t value = actor.GetCurrentProperty<int32_t>(index);
+  DALI_TEST_EQUALS(value, 100, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<int32_t>(index);
+  DALI_TEST_EQUALS(value, 150, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendVector2(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", Vector2::ZERO);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, Vector2::ONE);
+  keyframes.Add(1.0f, Vector2::ONE * 2.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  Vector2 value = actor.GetCurrentProperty<Vector2>(index);
+  DALI_TEST_EQUALS(value, Vector2::ONE, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<Vector2>(index);
+  DALI_TEST_EQUALS(value, Vector2::ONE * 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendVector3(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", Vector3::ZERO);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, Vector3::ONE);
+  keyframes.Add(1.0f, Vector3::ONE * 2.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  Vector3 value = actor.GetCurrentProperty<Vector3>(index);
+  DALI_TEST_EQUALS(value, Vector3::ONE, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<Vector3>(index);
+  DALI_TEST_EQUALS(value, Vector3::ONE * 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendVector4(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", Vector4::ZERO);
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, Vector4::ONE);
+  keyframes.Add(1.0f, Vector4::ONE * 2.0f);
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  Vector4 value = actor.GetCurrentProperty<Vector4>(index);
+  DALI_TEST_EQUALS(value, Vector4::ONE, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<Vector4>(index);
+  DALI_TEST_EQUALS(value, Vector4::ONE * 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationPlayBlendQuaternion(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+  Property::Index index = actor.RegisterProperty("property", Quaternion(Dali::Radian(0.0f), Vector3::ZAXIS));
+
+  Animation animation = Animation::New(1.0f);
+  KeyFrames keyframes = KeyFrames::New();
+  keyframes.Add(0.0f, Quaternion(Dali::Radian(1.0f), Vector3::ZAXIS));
+  keyframes.Add(1.0f, Quaternion(Dali::Radian(2.0f), Vector3::ZAXIS));
+  animation.AnimateBetween(Property(actor, index), keyframes, AlphaFunction::LINEAR);
+
+  application.SendNotification();
+  application.Render(20);
+
+  animation.SetBlendPoint(0.5f);
+  animation.Play();
+
+  application.SendNotification();
+  application.Render(250);
+
+  Quaternion value = actor.GetCurrentProperty<Quaternion>(index);
+  Vector3 axis;
+  Dali::Radian angle;
+  DALI_TEST_EQUALS(value.ToAxisAngle(axis, angle), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(angle.radian, 1.0f, 0.05f, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render(250);
+
+  value = actor.GetCurrentProperty<Quaternion>(index);
+  DALI_TEST_EQUALS(value.ToAxisAngle(axis, angle), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(angle.radian, 1.5f, 0.05f, TEST_LOCATION);
+
+  END_TEST;
+}
index 60e54f9..59a77db 100644 (file)
@@ -885,7 +885,7 @@ void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward,
 
   PathPtr pathCopy = Path::Clone(path);
 
-  //Position animation
+  // Position animation
   AddAnimatorConnector(AnimatorConnector<Vector3>::New(actor,
                                                        Dali::Actor::Property::POSITION,
                                                        Property::INVALID_COMPONENT_INDEX,
@@ -893,10 +893,10 @@ void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward,
                                                        alpha,
                                                        period));
 
-  //If forward is zero, PathRotationFunctor will always return the unit quaternion
+  // If forward is zero, PathRotationFunctor will always return the unit quaternion
   if(forward != Vector3::ZERO)
   {
-    //Rotation animation
+    // Rotation animation
     AddAnimatorConnector(AnimatorConnector<Quaternion>::New(actor,
                                                             Dali::Actor::Property::ORIENTATION,
                                                             Property::INVALID_COMPONENT_INDEX,
@@ -1010,11 +1010,11 @@ float Animation::GetSpeedFactor() const
 
 void Animation::SetPlayRange(const Vector2& range)
 {
-  //Make sure the range specified is between 0.0 and 1.0
+  // 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 the range is not in order swap values
     if(range.x > range.y)
     {
       orderedRange = Vector2(range.y, range.x);
@@ -1033,6 +1033,24 @@ Vector2 Animation::GetPlayRange() const
   return mPlayRange;
 }
 
+void Animation::SetBlendPoint(float blendPoint)
+{
+  if(blendPoint >= 0.0f && blendPoint <= 1.0f)
+  {
+    mBlendPoint = blendPoint;
+    SetBlendPointMessage(mEventThreadServices, *mAnimation, mBlendPoint);
+  }
+  else
+  {
+    DALI_LOG_ERROR("Blend Point should be a value between 0 and 1.\n");
+  }
+}
+
+float Animation::GetBlendPoint() const
+{
+  return mBlendPoint;
+}
+
 void Animation::SetLoopingMode(Dali::Animation::LoopingMode loopingMode)
 {
   mAutoReverseEnabled = (loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE);
index 966638e..e2cc571 100644 (file)
@@ -382,6 +382,16 @@ public:
   Vector2 GetPlayRange() const;
 
   /**
+   * @copydoc Dali::Animation::SetBlendPoint()
+   */
+  void SetBlendPoint(float blendPoint);
+
+  /**
+   * @copydoc Dali::Animation::GetBlendPoint()
+   */
+  float GetBlendPoint() const;
+
+  /**
    * @copydoc Dali::Animation::SetLoopingMode()
    */
   void SetLoopingMode(Dali::Animation::LoopingMode loopingMode);
@@ -554,6 +564,7 @@ private:
 
   AlphaFunction          mDefaultAlpha;
   Vector2                mPlayRange{0.0f, 1.0f};
+  float                  mBlendPoint{0.0f};
   float                  mDurationSeconds;
   float                  mSpeedFactor{1.0f};
   int32_t                mNotificationCount{0}; ///< Keep track of how many Finished signals have been emitted.
index e4c739a..0468b9c 100644 (file)
@@ -42,7 +42,7 @@ namespace Internal
 template<typename PropertyType>
 class AnimatorConnector : public AnimatorConnectorBase
 {
-  using AnimatorFunction = std::function<PropertyType(float, const PropertyType&)>;
+  using AnimatorFunction = std::function<PropertyType(float, float, const PropertyType&)>;
 
   AnimatorFunction mAnimatorFunction;
 
@@ -150,7 +150,7 @@ private:
 template<>
 class AnimatorConnector<float> : public AnimatorConnectorBase
 {
-  using AnimatorFunction = std::function<float(float, const float&)>;
+  using AnimatorFunction = std::function<float(float, float, const float&)>;
 
   AnimatorFunction mAnimatorFunction;
 
index 9c84d06..597fb4c 100644 (file)
@@ -216,7 +216,7 @@ public:
 
 using KeyFrameNumber     = KeyFrameBaseSpec<float>;
 using KeyFrameBoolean    = KeyFrameBaseSpec<bool>;
-using KeyFrameInteger    = KeyFrameBaseSpec<int>;
+using KeyFrameInteger    = KeyFrameBaseSpec<int32_t>;
 using KeyFrameVector2    = KeyFrameBaseSpec<Vector2>;
 using KeyFrameVector3    = KeyFrameBaseSpec<Vector3>;
 using KeyFrameVector4    = KeyFrameBaseSpec<Vector4>;
index 9d8e4de..9e38f49 100644 (file)
@@ -25,7 +25,7 @@
 #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
+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;
@@ -68,6 +68,7 @@ Animation::Animation(float durationSeconds, float speedFactor, const Vector2& pl
   mElapsedSeconds(playRange.x * mDurationSeconds),
   mSpeedFactor(speedFactor),
   mProgressMarker(0.0f),
+  mBlendPoint(0.0f),
   mPlayedCount(0),
   mLoopCount(loopCount),
   mCurrentLoop(0),
@@ -77,7 +78,8 @@ Animation::Animation(float durationSeconds, float speedFactor, const Vector2& pl
   mProgressReachedSignalRequired(false),
   mAutoReverseEnabled(false),
   mAnimatorSortRequired(false),
-  mIsActive{false}
+  mIsActive{false},
+  mIsFirstLoop{true}
 {
 }
 
@@ -146,6 +148,11 @@ void Animation::SetPlayRange(const Vector2& range)
   }
 }
 
+void Animation::SetBlendPoint(float blendPoint)
+{
+  mBlendPoint = blendPoint;
+}
+
 void Animation::Play()
 {
   if(mAnimatorSortRequired)
@@ -218,7 +225,7 @@ void Animation::Bake(BufferIndex bufferIndex, EndAction action)
     }
     else
     {
-      mElapsedSeconds = mPlayRange.x * mDurationSeconds - 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
     }
   }
 
@@ -259,6 +266,7 @@ bool Animation::Stop(BufferIndex bufferIndex)
 
   mElapsedSeconds = mPlayRange.x * mDurationSeconds;
   mState          = Stopped;
+  mIsFirstLoop    = true;
 
   return animationFinished;
 }
@@ -375,6 +383,7 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
       // Recalculate elapsedFactor here
       elapsedFactor = signSpeedFactor * mElapsedSeconds;
 
+      mIsFirstLoop = false;
       if(mLoopCount != 0)
       {
         // Check If this animation is finished
@@ -394,6 +403,7 @@ void Animation::Update(BufferIndex bufferIndex, float elapsedSeconds, bool& loop
           // After update animation, mElapsedSeconds must be begin of value
           mElapsedSeconds = playRangeStartSeconds + playRangeEndSeconds - edgeRangeSeconds;
           mState          = Stopped;
+          mIsFirstLoop    = true;
         }
       }
 
@@ -444,7 +454,7 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake, bool animati
 
   bool cleanup = false;
 
-  //Loop through all animators
+  // Loop through all animators
   for(auto& animator : mAnimators)
   {
     if(animator->Orphan())
@@ -467,7 +477,7 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake, bool animati
         {
           progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f, 1.0f);
         }
-        animator->Update(bufferIndex, progress, bake);
+        animator->Update(bufferIndex, progress, mIsFirstLoop ? mBlendPoint : 0.0f, bake);
 
         if(animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
         {
@@ -494,8 +504,9 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake, bool animati
 
   if(cleanup)
   {
-    //Remove animators whose PropertyOwner has been destroyed
-    mAnimators.EraseIf([](auto& animator) { return animator->Orphan(); });
+    // Remove animators whose PropertyOwner has been destroyed
+    mAnimators.EraseIf([](auto& animator)
+                       { return animator->Orphan(); });
 
     // Need to be re-sort if remained animators size is bigger than one.
     // Note that if animator contains only zero or one items, It is already sorted case.
index ac2a253..a0fa1c2 100644 (file)
@@ -193,6 +193,15 @@ public:
   void SetPlayRange(const Vector2& range);
 
   /**
+   * @brief Sets the blend point to interpolate animate property
+   *
+   * @param[in] blendPoint A value between [0,1], If the value of the keyframe whose progress is 0 is different from the current value,
+   * the property is animated as it smoothly blends until the progress reaches the blendPoint.
+   * @note The blendPoint only affects animation registered with AnimateBetween. Other animations operate the same as when Play() is called.
+   */
+  void SetBlendPoint(float blendPoint);
+
+  /**
    * Play the animation.
    */
   void Play();
@@ -337,6 +346,7 @@ protected:
   float mElapsedSeconds;
   float mSpeedFactor;
   float mProgressMarker; // Progress marker to trigger a notification
+  float mBlendPoint;
 
   int32_t mPlayedCount; // Incremented at end of animation or completion of all loops
                         // Never incremented when looping forever. Event thread tracks to signal end.
@@ -352,9 +362,10 @@ protected:
   bool mAutoReverseEnabled;            // Flag to identify that the looping mode is auto reverse.
   bool mAnimatorSortRequired;          // Flag to whether we need to sort animator or not.
   bool mIsActive[2];                   // Flag to indicate whether the animation is active in the current frame (which is double buffered)
+  bool mIsFirstLoop;
 };
 
-}; //namespace SceneGraph
+}; // namespace SceneGraph
 
 // value types used by messages
 template<>
@@ -454,6 +465,17 @@ inline void SetPlayRangeMessage(EventThreadServices& eventThreadServices, const
   new(slot) LocalType(&animation, &Animation::SetPlayRange, range);
 }
 
+inline void SetBlendPointMessage(EventThreadServices& eventThreadServices, const Animation& animation, float blendPoint)
+{
+  using LocalType = MessageValue1<Animation, float>;
+
+  // Reserve some memory inside the message queue
+  uint32_t* 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::SetBlendPoint, blendPoint);
+}
+
 inline void PlayAnimationMessage(EventThreadServices& eventThreadServices, const Animation& animation)
 {
   using LocalType = Message<Animation>;
@@ -464,7 +486,6 @@ inline void PlayAnimationMessage(EventThreadServices& eventThreadServices, const
   // Construct message in the message queue memory; note that delete should not be called on the return value
   new(slot) LocalType(&animation, &Animation::Play);
 }
-
 inline void PlayAnimationFromMessage(EventThreadServices& eventThreadServices, const Animation& animation, float progress)
 {
   using LocalType = MessageValue1<Animation, float>;
index 7be4a13..d3dcef1 100644 (file)
@@ -137,7 +137,7 @@ private: // From PropertyOwner::Observer
     if(mAnimationPlaying && mDisconnectAction != Dali::Animation::DISCARD)
     {
       // Bake to target-value if BakeFinal, otherwise bake current value
-      Update(bufferIndex, (mDisconnectAction == Dali::Animation::BAKE ? mCurrentProgress : 1.0f), true);
+      Update(bufferIndex, (mDisconnectAction == Dali::Animation::BAKE ? mCurrentProgress : 1.0f), 0.0f, true);
     }
 
     mEnabled = false;
@@ -353,15 +353,15 @@ public:
     }
     else
     {
-      //If progress is very close to 0 or very close to 1 we don't need to evaluate the curve as the result will
-      //be almost 0 or almost 1 respectively
+      // If progress is very close to 0 or very close to 1 we don't need to evaluate the curve as the result will
+      // be almost 0 or almost 1 respectively
       if((progress > Math::MACHINE_EPSILON_1) && ((1.0f - progress) > Math::MACHINE_EPSILON_1))
       {
         Dali::Vector4 controlPoints = mAlphaFunction.GetBezierControlPoints();
 
-        static const float tolerance = 0.001f; //10 iteration max
+        static const float tolerance = 0.001f; // 10 iteration max
 
-        //Perform a binary search on the curve
+        // Perform a binary search on the curve
         float lowerBound(0.0f);
         float upperBound(1.0f);
         float currentT(0.5f);
@@ -449,9 +449,10 @@ public:
    * Update the scene object attached to the animator.
    * @param[in] bufferIndex The buffer to animate.
    * @param[in] progress A value from 0 to 1, where 0 is the start of the animation, and 1 is the end point.
+   * @param[in] blendPoint A value between [0,1], The Animated property is animated as it blends until the progress reaches the blendPoint.
    * @param[in] bake Bake.
    */
-  void Update(BufferIndex bufferIndex, float progress, bool bake)
+  void Update(BufferIndex bufferIndex, float progress, float blendPoint, bool bake)
   {
     if(mLoopCount >= 0)
     {
@@ -467,7 +468,7 @@ public:
     float alpha = ApplyAlphaFunction(progress);
 
     // PropertyType specific part
-    DoUpdate(bufferIndex, bake, alpha);
+    DoUpdate(bufferIndex, bake, alpha, blendPoint);
 
     mCurrentProgress = progress;
   }
@@ -477,8 +478,9 @@ public:
    * @param bufferIndex index to use
    * @param bake whether to bake or not
    * @param alpha value from alpha based on progress
+   * @param blendPoint A value between [0,1], The Animated property is animated as it blends until the progress reaches the blendPoint.
    */
-  virtual void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha) = 0;
+  virtual void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha, float blendPoint) = 0;
 
 protected:
   /**
@@ -518,7 +520,7 @@ protected:
 template<typename PropertyType, typename PropertyAccessorType>
 class Animator final : public AnimatorBase
 {
-  using AnimatorFunction = std::function<PropertyType(float, const PropertyType&)>;
+  using AnimatorFunction = std::function<PropertyType(float, float, const PropertyType&)>;
 
   AnimatorFunction mAnimatorFunction;
 
@@ -548,12 +550,12 @@ public:
   /**
    * @copydoc AnimatorBase::DoUpdate( BufferIndex bufferIndex, bool bake, float alpha )
    */
-  void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha) final
+  void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha, float blendPoint) final
   {
     const PropertyType& current = mPropertyAccessor.Get(bufferIndex);
 
     // need to cast the return value in case property is integer
-    const PropertyType result = static_cast<PropertyType>(mAnimatorFunction(alpha, current));
+    const PropertyType result = static_cast<PropertyType>(mAnimatorFunction(alpha, blendPoint, current));
 
     if(bake)
     {
@@ -598,7 +600,7 @@ protected:
 template<typename PropertyType, typename PropertyAccessorType>
 class AnimatorTransformProperty final : public AnimatorBase
 {
-  using AnimatorFunction = std::function<PropertyType(float, const PropertyType&)>;
+  using AnimatorFunction = std::function<PropertyType(float, float, const PropertyType&)>;
 
   AnimatorFunction mAnimatorFunction;
 
@@ -628,12 +630,12 @@ public:
   /**
    * @copydoc AnimatorBase::DoUpdate( BufferIndex bufferIndex, bool bake, float alpha )
    */
-  void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha) final
+  void DoUpdate(BufferIndex bufferIndex, bool bake, float alpha, float blendPoint) final
   {
     const PropertyType& current = mPropertyAccessor.Get(bufferIndex);
 
     // need to cast the return value in case property is integer
-    const PropertyType result = static_cast<PropertyType>(mAnimatorFunction(alpha, current));
+    const PropertyType result = static_cast<PropertyType>(mAnimatorFunction(alpha, blendPoint, current));
 
     if(bake)
     {
@@ -663,8 +665,8 @@ private:
   }
 
   // Undefined
-  AnimatorTransformProperty()                                 = delete;
-  AnimatorTransformProperty(const AnimatorTransformProperty&) = delete;
+  AnimatorTransformProperty()                                            = delete;
+  AnimatorTransformProperty(const AnimatorTransformProperty&)            = delete;
   AnimatorTransformProperty& operator=(const AnimatorTransformProperty&) = delete;
 
 protected:
@@ -682,7 +684,7 @@ struct AnimateByInteger
   {
   }
 
-  float operator()(float alpha, const int32_t& property)
+  float operator()(float alpha, float blendPoint, const int32_t& property)
   {
     // integers need to be correctly rounded
     return roundf(static_cast<float>(property) + static_cast<float>(mRelative) * alpha);
@@ -698,7 +700,7 @@ struct AnimateToInteger
   {
   }
 
-  float operator()(float alpha, const int32_t& property)
+  float operator()(float alpha, float blendPoint, const int32_t& property)
   {
     // integers need to be correctly rounded
     return roundf(static_cast<float>(property) + (static_cast<float>(mTarget - property) * alpha));
@@ -714,7 +716,7 @@ struct AnimateByFloat
   {
   }
 
-  float operator()(float alpha, const float& property)
+  float operator()(float alpha, float blendPoint, const float& property)
   {
     return float(property + mRelative * alpha);
   }
@@ -729,7 +731,7 @@ struct AnimateToFloat
   {
   }
 
-  float operator()(float alpha, const float& property)
+  float operator()(float alpha, float blendPoint, const float& property)
   {
     return float(property + ((mTarget - property) * alpha));
   }
@@ -744,7 +746,7 @@ struct AnimateByVector2
   {
   }
 
-  Vector2 operator()(float alpha, const Vector2& property)
+  Vector2 operator()(float alpha, float blendPoint, const Vector2& property)
   {
     return Vector2(property + mRelative * alpha);
   }
@@ -759,7 +761,7 @@ struct AnimateToVector2
   {
   }
 
-  Vector2 operator()(float alpha, const Vector2& property)
+  Vector2 operator()(float alpha, float blendPoint, const Vector2& property)
   {
     return Vector2(property + ((mTarget - property) * alpha));
   }
@@ -774,7 +776,7 @@ struct AnimateByVector3
   {
   }
 
-  Vector3 operator()(float alpha, const Vector3& property)
+  Vector3 operator()(float alpha, float blendPoint, const Vector3& property)
   {
     return Vector3(property + mRelative * alpha);
   }
@@ -789,7 +791,7 @@ struct AnimateToVector3
   {
   }
 
-  Vector3 operator()(float alpha, const Vector3& property)
+  Vector3 operator()(float alpha, float blendPoint, const Vector3& property)
   {
     return Vector3(property + ((mTarget - property) * alpha));
   }
@@ -804,7 +806,7 @@ struct AnimateByVector4
   {
   }
 
-  Vector4 operator()(float alpha, const Vector4& property)
+  Vector4 operator()(float alpha, float blendPoint, const Vector4& property)
   {
     return Vector4(property + mRelative * alpha);
   }
@@ -819,7 +821,7 @@ struct AnimateToVector4
   {
   }
 
-  Vector4 operator()(float alpha, const Vector4& property)
+  Vector4 operator()(float alpha, float blendPoint, const Vector4& property)
   {
     return Vector4(property + ((mTarget - property) * alpha));
   }
@@ -834,7 +836,7 @@ struct AnimateByOpacity
   {
   }
 
-  Vector4 operator()(float alpha, const Vector4& property)
+  Vector4 operator()(float alpha, float blendPoint, const Vector4& property)
   {
     Vector4 result(property);
     result.a += mRelative * alpha;
@@ -852,7 +854,7 @@ struct AnimateToOpacity
   {
   }
 
-  Vector4 operator()(float alpha, const Vector4& property)
+  Vector4 operator()(float alpha, float blendPoint, const Vector4& property)
   {
     Vector4 result(property);
     result.a = property.a + ((mTarget - property.a) * alpha);
@@ -870,7 +872,7 @@ struct AnimateByBoolean
   {
   }
 
-  bool operator()(float alpha, const bool& property)
+  bool operator()(float alpha, float blendPoint, const bool& property)
   {
     // Alpha is not useful here, just keeping to the same template as other update functors
     return bool(alpha >= 1.0f ? (property || mRelative) : property);
@@ -886,7 +888,7 @@ struct AnimateToBoolean
   {
   }
 
-  bool operator()(float alpha, const bool& property)
+  bool operator()(float alpha, float blendPoint, const bool& property)
   {
     // Alpha is not useful here, just keeping to the same template as other update functors
     return bool(alpha >= 1.0f ? mTarget : property);
@@ -903,7 +905,7 @@ struct RotateByAngleAxis
   {
   }
 
-  Quaternion operator()(float alpha, const Quaternion& rotation)
+  Quaternion operator()(float alpha, float blendPoint, const Quaternion& rotation)
   {
     if(alpha > 0.0f)
     {
@@ -924,7 +926,7 @@ struct RotateToQuaternion
   {
   }
 
-  Quaternion operator()(float alpha, const Quaternion& rotation)
+  Quaternion operator()(float alpha, float blendPoint, const Quaternion& rotation)
   {
     return Quaternion::Slerp(rotation, mTarget, alpha);
   }
@@ -939,7 +941,7 @@ struct KeyFrameBooleanFunctor
   {
   }
 
-  bool operator()(float progress, const bool& property)
+  bool operator()(float progress, float blendPoint, const bool& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
@@ -959,13 +961,23 @@ struct KeyFrameIntegerFunctor
   {
   }
 
-  float operator()(float progress, const int32_t& property)
+  int32_t operator()(float progress, float blendPoint, const int32_t& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return static_cast<float>(mKeyFrames.GetValue(progress, mInterpolation));
+      if(progress < blendPoint)
+      {
+        float subProgress = progress / blendPoint;
+        float original    = static_cast<float>(mKeyFrames.GetValue(progress, mInterpolation));
+        float base        = Dali::Lerp(subProgress, static_cast<float>(property), static_cast<float>(mKeyFrames.GetValue(blendPoint, mInterpolation)));
+        return static_cast<int32_t>(Dali::Lerp(subProgress, base, original));
+      }
+      else
+      {
+        return static_cast<int32_t>(mKeyFrames.GetValue(progress, mInterpolation));
+      }
     }
-    return static_cast<float>(property);
+    return static_cast<int32_t>(property);
   }
 
   KeyFrameInteger mKeyFrames;
@@ -980,11 +992,21 @@ struct KeyFrameNumberFunctor
   {
   }
 
-  float operator()(float progress, const float& property)
+  float operator()(float progress, float blendPoint, const float& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return mKeyFrames.GetValue(progress, mInterpolation);
+      if(progress < blendPoint)
+      {
+        float subProgress = progress / blendPoint;
+        float original    = mKeyFrames.GetValue(progress, mInterpolation);
+        float base        = Dali::Lerp(subProgress, property, mKeyFrames.GetValue(blendPoint, mInterpolation));
+        return Dali::Lerp(subProgress, base, original);
+      }
+      else
+      {
+        return mKeyFrames.GetValue(progress, mInterpolation);
+      }
     }
     return property;
   }
@@ -1001,11 +1023,21 @@ struct KeyFrameVector2Functor
   {
   }
 
-  Vector2 operator()(float progress, const Vector2& property)
+  Vector2 operator()(float progress, float blendPoint, const Vector2& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return mKeyFrames.GetValue(progress, mInterpolation);
+      if(progress < blendPoint)
+      {
+        float   subProgress = progress / blendPoint;
+        Vector2 original    = mKeyFrames.GetValue(progress, mInterpolation);
+        Vector2 base        = Dali::Lerp(subProgress, property, mKeyFrames.GetValue(blendPoint, mInterpolation));
+        return Dali::Lerp(subProgress, base, original);
+      }
+      else
+      {
+        return mKeyFrames.GetValue(progress, mInterpolation);
+      }
     }
     return property;
   }
@@ -1022,11 +1054,21 @@ struct KeyFrameVector3Functor
   {
   }
 
-  Vector3 operator()(float progress, const Vector3& property)
+  Vector3 operator()(float progress, float blendPoint, const Vector3& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return mKeyFrames.GetValue(progress, mInterpolation);
+      if(progress < blendPoint)
+      {
+        float   subProgress = progress / blendPoint;
+        Vector3 original    = mKeyFrames.GetValue(progress, mInterpolation);
+        Vector3 base        = Dali::Lerp(subProgress, property, mKeyFrames.GetValue(blendPoint, mInterpolation));
+        return Dali::Lerp(subProgress, base, original);
+      }
+      else
+      {
+        return mKeyFrames.GetValue(progress, mInterpolation);
+      }
     }
     return property;
   }
@@ -1043,11 +1085,21 @@ struct KeyFrameVector4Functor
   {
   }
 
-  Vector4 operator()(float progress, const Vector4& property)
+  Vector4 operator()(float progress, float blendPoint, const Vector4& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return mKeyFrames.GetValue(progress, mInterpolation);
+      if(progress < blendPoint)
+      {
+        float   subProgress = progress / blendPoint;
+        Vector4 original    = mKeyFrames.GetValue(progress, mInterpolation);
+        Vector4 base        = Dali::Lerp(subProgress, property, mKeyFrames.GetValue(blendPoint, mInterpolation));
+        return Dali::Lerp(subProgress, base, original);
+      }
+      else
+      {
+        return mKeyFrames.GetValue(progress, mInterpolation);
+      }
     }
     return property;
   }
@@ -1063,11 +1115,21 @@ struct KeyFrameQuaternionFunctor
   {
   }
 
-  Quaternion operator()(float progress, const Quaternion& property)
+  Quaternion operator()(float progress, float blendPoint, const Quaternion& property)
   {
     if(mKeyFrames.IsActive(progress))
     {
-      return mKeyFrames.GetValue(progress, Dali::Animation::LINEAR);
+      if(progress < blendPoint)
+      {
+        float      subProgress = progress / blendPoint;
+        Quaternion original    = mKeyFrames.GetValue(progress, Dali::Animation::LINEAR);
+        Quaternion base        = Quaternion::Slerp(property, mKeyFrames.GetValue(blendPoint, Dali::Animation::LINEAR), subProgress);
+        return Quaternion::Slerp(base, original, subProgress);
+      }
+      else
+      {
+        return mKeyFrames.GetValue(progress, Dali::Animation::LINEAR);
+      }
     }
     return property;
   }
@@ -1082,7 +1144,7 @@ struct PathPositionFunctor
   {
   }
 
-  Vector3 operator()(float progress, const Vector3& property)
+  Vector3 operator()(float progress, float blendPoint, const Vector3& property)
   {
     Vector3 position(property);
     static_cast<void>(mPath->SamplePosition(progress, position));
@@ -1101,7 +1163,7 @@ struct PathRotationFunctor
     mForward.Normalize();
   }
 
-  Quaternion operator()(float progress, const Quaternion& property)
+  Quaternion operator()(float progress, float blendPoint, const Quaternion& property)
   {
     Vector3 tangent;
     if(mPath->SampleTangent(progress, tangent))
index d1987c5..c95d573 100644 (file)
@@ -313,4 +313,14 @@ Vector2 Animation::GetPlayRange() const
   return GetImplementation(*this).GetPlayRange();
 }
 
+void Animation::SetBlendPoint(float blendPoint)
+{
+  GetImplementation(*this).SetBlendPoint(blendPoint);
+}
+
+float Animation::GetBlendPoint() const
+{
+  return GetImplementation(*this).GetBlendPoint();
+}
+
 } // namespace Dali
index 73599f4..c1a203f 100644 (file)
@@ -125,7 +125,7 @@ class DALI_CORE_API Animation : public BaseHandle
 public:
   using AnimationSignalType = Signal<void(Animation&)>; ///< Animation finished signal type @SINCE_1_0.0
 
-  using AnyFunction = Any; ///< Interpolation function @SINCE_1_0.0
+  using AnyFunction = Any;                              ///< Interpolation function @SINCE_1_0.0
 
   /**
    * @brief Enumeration for what to do when the animation ends, is stopped, or is destroyed.
@@ -384,11 +384,11 @@ public:
   void SetCurrentProgress(float progress);
 
   /**
-  * @brief Retrieves the current progress of the animation.
-  *
-  * @SINCE_1_0.0
-  * @return The current progress as a normalized value between [0,1]
-  */
+   * @brief Retrieves the current progress of the animation.
+   *
+   * @SINCE_1_0.0
+   * @return The current progress as a normalized value between [0,1]
+   */
   float GetCurrentProgress();
 
   /**
@@ -432,6 +432,26 @@ public:
   Vector2 GetPlayRange() const;
 
   /**
+   * @brief Sets the blend point to interpolate animate property
+   *
+   * @SINCE_2_2.26
+   * @param[in] blendPoint A value between [0,1], If the value of the keyframe whose progress is 0 is different from the current value,
+   * the property is animated as it smoothly blends until the progress reaches the blendPoint.
+   * @note The blend point only affects animation registered with AnimateBetween. Other animations operate the same as when Play() is called.
+   * And the blend point needs to be set before this animation plays. If the blend point changes after playback, animation continuity cannot be guaranteed.
+   * @note In the case of a looping animation, the animation is blended only in the first loop.
+   */
+  void SetBlendPoint(float blendPoint);
+
+  /**
+   * @brief Gets the Blend Point
+   *
+   * @SINCE_2_2.26
+   * @return The blend point to interpolate animate property
+   */
+  float GetBlendPoint() const;
+
+  /**
    * @brief Play the animation.
    * @SINCE_1_0.0
    */