From 61565fdb199985199d2b629723dce43257a2afd3 Mon Sep 17 00:00:00 2001 From: Ferran Sole Date: Tue, 2 Dec 2014 16:46:20 +0000 Subject: [PATCH] Added cubic interpolation for key-frame animations [Problem] Key-frame animations only supports linear interpolation between key values [Cause] n/a [Solution] Allow the user to specify which interpolation method to use [Linear,Cubic] Added unit tests for cubic interpolation Change-Id: I152fbd2211d4b7fa52fd44f315c05869cccdd684 --- automated-tests/src/dali/utc-Dali-Animation.cpp | 328 ++++++++++++++++++++- dali/internal/event/animation/animation-impl.cpp | 37 ++- dali/internal/event/animation/animation-impl.h | 21 ++ dali/internal/event/animation/key-frame-channel.h | 39 ++- dali/internal/event/animation/key-frames-impl.h | 4 +- dali/internal/event/animation/progress-value.h | 105 +++++-- .../update/animation/scene-graph-animator.h | 41 +-- dali/public-api/animation/animation.cpp | 26 +- dali/public-api/animation/animation.h | 52 +++- 9 files changed, 597 insertions(+), 56 deletions(-) diff --git a/automated-tests/src/dali/utc-Dali-Animation.cpp b/automated-tests/src/dali/utc-Dali-Animation.cpp index 69b2a56..84e541c 100644 --- a/automated-tests/src/dali/utc-Dali-Animation.cpp +++ b/automated-tests/src/dali/utc-Dali-Animation.cpp @@ -6589,6 +6589,100 @@ int UtcDaliAnimationAnimateBetweenActorColorAlpha(void) END_TEST; } +int UtcDaliAnimationAnimateBetweenActorColorAlphaCubic(void) +{ + TestApplication application; + + float startValue(1.0f); + Actor actor = Actor::New(); + actor.SetColor(Vector4(startValue, startValue, startValue, startValue)); + Stage::GetCurrent().Add(actor); + + DALI_TEST_EQUALS( actor.GetCurrentColor().a, startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), startValue, TEST_LOCATION ); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + KeyFrames keyFrames = KeyFrames::New(); + keyFrames.Add(0.0f, 0.1f); + keyFrames.Add(0.2f, 0.5f); + keyFrames.Add(0.4f, 0.0f); + keyFrames.Add(0.6f, 1.0f); + keyFrames.Add(0.8f, 0.7f); + keyFrames.Add(1.0f, 0.9f); + + animation.AnimateBetween( Property(actor, Actor::COLOR_ALPHA), keyFrames, Animation::Cubic ); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + application.SendNotification(); + application.Render(0); + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.1f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f)/* 10% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.36f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.36f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 30% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.21f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.21f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f)/* 40% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.0f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.0f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*400.0f)/* 80% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.7f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.7f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f)/* 90% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.76f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.76f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f)+1/* 100% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.9f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetCurrentColor().a, 0.9f, 0.01f, TEST_LOCATION ); + + // We did expect the animation to finish + + finishCheck.CheckSignalReceived(); + END_TEST; +} int UtcDaliAnimationAnimateBetweenActorColor(void) { @@ -6665,7 +6759,82 @@ int UtcDaliAnimationAnimateBetweenActorColor(void) END_TEST; } -int UtcDaliAnimationAnimateBetweenActorVisible01(void) +int UtcDaliAnimationAnimateBetweenActorColorCubic(void) +{ + TestApplication application; + + float startValue(1.0f); + Actor actor = Actor::New(); + actor.SetColor(Vector4(startValue, startValue, startValue, startValue)); + Stage::GetCurrent().Add(actor); + + DALI_TEST_EQUALS( actor.GetCurrentColor().a, startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), startValue, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), startValue, TEST_LOCATION ); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + KeyFrames keyFrames = KeyFrames::New(); + keyFrames.Add(0.0f, Vector4(0.1f, 0.2f, 0.3f, 0.4f)); + keyFrames.Add(0.5f, Vector4(0.9f, 0.8f, 0.7f, 0.6f)); + keyFrames.Add(1.0f, Vector4(1.0f, 1.0f, 1.0f, 1.0f)); + + animation.AnimateBetween( Property(actor, Actor::COLOR), keyFrames, Animation::Cubic ); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + application.SendNotification(); + application.Render(0); + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), 0.1f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), 0.2f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), 0.3f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.4f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 25% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), 0.55f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), 0.525f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), 0.506f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.4875f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 50% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), 0.9f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), 0.8f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), 0.7f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.6f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 75% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), 0.99375f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), 0.925f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), 0.85625f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 0.7875f, 0.01f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)+1/* 100% progress */); + application.SendNotification(); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_RED), 1.0f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_GREEN), 1.0f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_BLUE), 1.0f, 0.01f, TEST_LOCATION ); + DALI_TEST_EQUALS( actor.GetProperty(Actor::COLOR_ALPHA), 1.0f, 0.01f, TEST_LOCATION ); + + // We did expect the animation to finish + + finishCheck.CheckSignalReceived(); + END_TEST; +} + +int UtcDaliAnimationAnimateBetweenActorVisible(void) { TestApplication application; @@ -6710,6 +6879,52 @@ int UtcDaliAnimationAnimateBetweenActorVisible01(void) END_TEST; } +int UtcDaliAnimationAnimateBetweenActorVisibleCubic(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + AngleAxis aa(Degree(90), Vector3::XAXIS); + actor.SetRotation(aa.angle, aa.axis); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(0); + + DALI_TEST_EQUALS( actor.IsVisible(), true, TEST_LOCATION ); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + KeyFrames keyFrames = KeyFrames::New(); + keyFrames.Add(0.0f, false); + keyFrames.Add(0.2f, true); + keyFrames.Add(0.4f, true); + keyFrames.Add(0.8f, false); + keyFrames.Add(1.0f, true); + + //Cubic interpolation for boolean values should be ignored + animation.AnimateBetween( Property(actor, Actor::VISIBLE), keyFrames, Animation::Cubic ); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + application.SendNotification(); + application.SendNotification(); + application.Render(static_cast(durationSeconds*500.0f)); + application.SendNotification(); + application.Render(static_cast(durationSeconds*500.0f)+1); + application.SendNotification(); + + DALI_TEST_EQUALS( actor.IsVisible(), true, TEST_LOCATION); + finishCheck.CheckSignalReceived(); + END_TEST; +} + int UtcDaliAnimationAnimateBetweenActorRotation01(void) { TestApplication application; @@ -6818,6 +7033,117 @@ int UtcDaliAnimationAnimateBetweenActorRotation02(void) END_TEST; } +int UtcDaliAnimationAnimateBetweenActorRotation01Cubic(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + AngleAxis aa(Degree(90), Vector3::XAXIS); + actor.SetRotation(aa.angle, aa.axis); + Stage::GetCurrent().Add(actor); + + application.SendNotification(); + application.Render(0); + Quaternion start(Radian(aa.angle), aa.axis); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), start, 0.001f, TEST_LOCATION ); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + KeyFrames keyFrames = KeyFrames::New(); + keyFrames.Add(0.0f, AngleAxis(Degree(60), Vector3::ZAXIS)); + + //Cubic interpolation should be ignored for quaternions + animation.AnimateBetween( Property(actor, Actor::ROTATION), keyFrames, Animation::Cubic ); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + application.SendNotification(); + application.SendNotification(); + application.Render(static_cast(durationSeconds*500.0f)); + application.SendNotification(); + application.Render(static_cast(durationSeconds*500.0f)+1); + application.SendNotification(); + + Quaternion check = Quaternion::FromAxisAngle(Vector4::ZAXIS, Radian(Degree(60))); + + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + finishCheck.CheckSignalReceived(); + END_TEST; +} + +int UtcDaliAnimationAnimateBetweenActorRotation02Cubic(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + AngleAxis aa(Degree(90), Vector3::XAXIS); + actor.SetRotation(aa.angle, aa.axis); + application.SendNotification(); + application.Render(0); + Stage::GetCurrent().Add(actor); + + Quaternion start(Radian(aa.angle), aa.axis); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), start, 0.001f, TEST_LOCATION ); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + KeyFrames keyFrames = KeyFrames::New(); + keyFrames.Add(0.0f, AngleAxis(Degree(60), Vector3::XAXIS)); + keyFrames.Add(0.5f, AngleAxis(Degree(120), Vector3::XAXIS)); + keyFrames.Add(1.0f, AngleAxis(Degree(120), Vector3::YAXIS)); + + //Cubic interpolation should be ignored for quaternions + animation.AnimateBetween( Property(actor, Actor::ROTATION), keyFrames, Animation::Cubic ); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + application.SendNotification(); + application.Render(0); + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + + Quaternion check(Radian(Degree(60)), Vector3::XAXIS); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 25% progress */); + application.SendNotification(); + check = Quaternion::FromAxisAngle(Vector4::XAXIS, Radian(Degree(90))); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 50% progress */); + application.SendNotification(); + check = Quaternion::FromAxisAngle(Vector4::XAXIS, Radian(Degree(120))); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)/* 75% progress */); + application.SendNotification(); + check = Quaternion::FromAxisAngle(Vector4(0.5f, 0.5f, 0.0f, 0.0f), Radian(Degree(101.5))); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*250.0f)+1/* 100% progress */); + application.SendNotification(); + check = Quaternion::FromAxisAngle(Vector4::YAXIS, Radian(Degree(120))); + DALI_TEST_EQUALS( actor.GetCurrentRotation(), check, 0.001f, TEST_LOCATION ); + + // We did expect the animation to finish + + finishCheck.CheckSignalReceived(); + END_TEST; +} + + int UtcDaliAnimationMoveByFloat3(void) { TestApplication application; diff --git a/dali/internal/event/animation/animation-impl.cpp b/dali/internal/event/animation/animation-impl.cpp index dad1f27..7a04d21 100644 --- a/dali/internal/event/animation/animation-impl.cpp +++ b/dali/internal/event/animation/animation-impl.cpp @@ -65,6 +65,7 @@ TypeAction action3( mType, Dali::Animation::ACTION_PAUSE, &Animation::DoAction ) const Dali::Animation::EndAction DEFAULT_END_ACTION( Dali::Animation::Bake ); const Dali::Animation::EndAction DEFAULT_DISCONNECT_ACTION( Dali::Animation::BakeFinal ); +const Dali::Animation::Interpolation DEFAULT_INTERPOLATION( Dali::Animation::Linear ); } // anon namespace @@ -504,21 +505,41 @@ void Animation::AnimateTo(ProxyObject& targetObject, Property::Index targetPrope void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames) { - AnimateBetween(target, keyFrames, mDefaultAlpha, mDurationSeconds); + AnimateBetween(target, keyFrames, mDefaultAlpha, mDurationSeconds, DEFAULT_INTERPOLATION ); +} + +void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Interpolation interpolation ) +{ + AnimateBetween(target, keyFrames, mDefaultAlpha, mDurationSeconds, interpolation ); } void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period) { - AnimateBetween(target, keyFrames, mDefaultAlpha, period); + AnimateBetween(target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION); +} + +void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation) +{ + AnimateBetween(target, keyFrames, mDefaultAlpha, period, interpolation); } void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha) { - AnimateBetween(target, keyFrames, alpha, mDurationSeconds); + AnimateBetween(target, keyFrames, alpha, mDurationSeconds, DEFAULT_INTERPOLATION); +} + +void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation) +{ + AnimateBetween(target, keyFrames, alpha, mDurationSeconds, interpolation); } void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period) { + AnimateBetween(target, keyFrames, alpha, period, DEFAULT_INTERPOLATION); +} + +void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation) +{ ProxyObject& proxy = dynamic_cast( GetImplementation(target.object) ); ExtendDuration( period ); @@ -547,7 +568,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph AddAnimatorConnector( AnimatorConnector::New( proxy, target.propertyIndex, target.componentIndex, - KeyFrameNumberFunctor(kfCopy), + KeyFrameNumberFunctor(kfCopy,interpolation), alpha, period ) ); break; @@ -561,7 +582,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph AddAnimatorConnector( AnimatorConnector::New( proxy, target.propertyIndex, target.componentIndex, - KeyFrameIntegerFunctor(kfCopy), + KeyFrameIntegerFunctor(kfCopy,interpolation), alpha, period ) ); break; @@ -575,7 +596,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph AddAnimatorConnector( AnimatorConnector::New( proxy, target.propertyIndex, target.componentIndex, - KeyFrameVector2Functor(kfCopy), + KeyFrameVector2Functor(kfCopy,interpolation), alpha, period ) ); break; @@ -589,7 +610,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph AddAnimatorConnector( AnimatorConnector::New( proxy, target.propertyIndex, target.componentIndex, - KeyFrameVector3Functor(kfCopy), + KeyFrameVector3Functor(kfCopy,interpolation), alpha, period ) ); break; @@ -603,7 +624,7 @@ void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Alph AddAnimatorConnector( AnimatorConnector::New( proxy, target.propertyIndex, target.componentIndex, - KeyFrameVector4Functor(kfCopy), + KeyFrameVector4Functor(kfCopy,interpolation), alpha, period ) ); break; diff --git a/dali/internal/event/animation/animation-impl.h b/dali/internal/event/animation/animation-impl.h index 7d10295..43baa8f 100644 --- a/dali/internal/event/animation/animation-impl.h +++ b/dali/internal/event/animation/animation-impl.h @@ -61,6 +61,7 @@ class Animation : public BaseObject public: typedef Dali::Animation::EndAction EndAction; + typedef Dali::Animation::Interpolation Interpolation; typedef void (*FinishedCallback)(Object* object); @@ -253,20 +254,40 @@ public: void AnimateBetween(Property target, const KeyFrames& keyFrames); /** + * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, Interpolation interpolation) + */ + void AnimateBetween(Property target, const KeyFrames& keyFrames, Interpolation interpolation ); + + /** * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod period) */ void AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period); /** + * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation) + */ + void AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation); + + /** * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha) */ void AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha); /** + * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation) + */ + void AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation); + + /** * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period) */ void AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period); + /** + * @copydoc Dali::Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation ) + */ + void AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation ); + // Actor-specific convenience functions /** diff --git a/dali/internal/event/animation/key-frame-channel.h b/dali/internal/event/animation/key-frame-channel.h index 8c7b570..bba814f 100644 --- a/dali/internal/event/animation/key-frame-channel.h +++ b/dali/internal/event/animation/key-frame-channel.h @@ -19,8 +19,9 @@ */ // INTERNAL INCLUDES -#include #include +#include +#include namespace Dali { @@ -75,7 +76,7 @@ public: bool IsActive (float progress); - V GetValue(float progress) const; + V GetValue(float progress, Dali::Animation::Interpolation interpolation) const; bool FindInterval(typename ProgressValues::iterator& start, typename ProgressValues::iterator& end, @@ -139,7 +140,7 @@ bool KeyFrameChannel::FindInterval( } template -V KeyFrameChannel::GetValue (float progress) const +V KeyFrameChannel::GetValue (float progress, Dali::Animation::Interpolation interpolation) const { ProgressValue& firstPV = mValues.front(); @@ -155,7 +156,37 @@ V KeyFrameChannel::GetValue (float progress) const { float frameProgress = (progress - start->GetProgress()) / (end->GetProgress() - start->GetProgress()); - interpolatedV = Interpolate(*start, *end, frameProgress); + if( interpolation == Dali::Animation::Linear ) + { + Interpolate(interpolatedV, start->GetValue(), end->GetValue(), frameProgress); + } + else + { + //Calculate prev and next values + V prev; + if( start != mValues.begin() ) + { + prev = (start-1)->GetValue(); + } + else + { + //Project next value through start point + prev = start->GetValue() + (start->GetValue()-(start+1)->GetValue()); + } + + V next; + if( end != mValues.end()-1) + { + next = (end+1)->GetValue(); + } + else + { + //Project prev value through end point + next = end->GetValue() + (end->GetValue()-(end-1)->GetValue()); + } + + CubicInterpolate(interpolatedV, prev, start->GetValue(), end->GetValue(), next, frameProgress); + } } return interpolatedV; diff --git a/dali/internal/event/animation/key-frames-impl.h b/dali/internal/event/animation/key-frames-impl.h index b102b88..4f55c9f 100644 --- a/dali/internal/event/animation/key-frames-impl.h +++ b/dali/internal/event/animation/key-frames-impl.h @@ -232,9 +232,9 @@ public: * @param[in] progress The progress to test * @return The interpolated value */ - V GetValue(float progress) const + V GetValue(float progress, Dali::Animation::Interpolation interpolation) const { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, interpolation); } }; diff --git a/dali/internal/event/animation/progress-value.h b/dali/internal/event/animation/progress-value.h index b7fd320..30b6877 100644 --- a/dali/internal/event/animation/progress-value.h +++ b/dali/internal/event/animation/progress-value.h @@ -86,53 +86,116 @@ typedef std::vector ProgressVector3Container typedef ProgressValue ProgressVector4; typedef std::vector ProgressVector4Container; -inline Quaternion Interpolate (ProgressQuaternion& a, ProgressQuaternion& b, float progress) +inline void Interpolate (Quaternion& result, const Quaternion& a, const Quaternion& b, float progress) { - return Quaternion::Slerp(a.GetValue(), b.GetValue(), progress); + result = Quaternion::Slerp(a, b, progress); } -inline AngleAxis Interpolate (ProgressAngleAxis& a, ProgressAngleAxis& b, float progress) +inline void Interpolate (AngleAxis& result, const AngleAxis& a, const AngleAxis& b, float progress) { - AngleAxis av(a.GetValue()); - AngleAxis bv(b.GetValue()); - Quaternion q1(Radian(av.angle), av.axis); - Quaternion q2(Radian(bv.angle), bv.axis); + Quaternion q1(Radian(a.angle), a.axis); + Quaternion q2(Radian(b.angle), b.axis); Quaternion iq = Quaternion::Slerp(q1, q2, progress); - AngleAxis result; iq.ToAxisAngle(result.axis, result.angle); - return result; } -inline bool Interpolate (ProgressBoolean& a, ProgressBoolean& b, float progress) +inline void Interpolate (bool& result, bool a, bool b, float progress) { - return progress < 0.5f ? a.GetValue() : b.GetValue(); + result = progress < 0.5f ? a : b; } -inline float Interpolate (ProgressNumber& a, ProgressNumber& b, float progress) +inline void Interpolate (float& result, float a, float b, float progress) { - return (a.GetValue() + (b.GetValue() - a.GetValue()) * progress); + result = a + (b-a) * progress; } -inline int Interpolate (ProgressInteger& a, ProgressInteger& b, float progress) +inline void Interpolate (int& result, int a, int b, float progress) { - return static_cast(a.GetValue() + (b.GetValue() - a.GetValue()) * progress + 0.5f); + result = static_cast(a + (b - a) * progress + 0.5f); } -inline Vector2 Interpolate (ProgressVector2& a, ProgressVector2& b, float progress) +inline void Interpolate (Vector2& result, const Vector2& a, const Vector2& b, float progress) { - return (a.GetValue() + (b.GetValue() - a.GetValue()) * progress); + result = a + (b-a) * progress; } -inline Vector3 Interpolate (ProgressVector3& a, ProgressVector3& b, float progress) +inline void Interpolate (Vector3& result, const Vector3& a, const Vector3& b, float progress) { - return (a.GetValue() + (b.GetValue() - a.GetValue()) * progress); + result = a + (b-a) * progress; } -inline Vector4 Interpolate (ProgressVector4& a, ProgressVector4& b, float progress) +inline void Interpolate (Vector4& result, const Vector4& a, const Vector4& b, float progress) { - return (a.GetValue() + (b.GetValue() - a.GetValue()) * progress); + result = a + (b-a) * progress; +} + +/* Cubic Interpolation (Catmull-Rom spline) between values p1 and p2. p0 and p3 are prev and next values + * and are used as control points to calculate tangent of the curve at interpolation points. + * + * f(t) = a3*t^3 + a2*t^2 + a1*t + a0 + * Restrictions: f(0)=p1 f(1)=p2 f'(0)=(p2-p0)*0.5 f'(1)=(p3-p1)*0.5 + */ + +inline void CubicInterpolate( int& result, int p0, int p1, int p2, int p3, float progress ) +{ + float a3 = p3*0.5f - p2*1.5f + p1*1.5f - p0*0.5f; + float a2 = p0 - p1*2.5f + p2*2.0f - p3*0.5f; + float a1 = (p2-p0)*0.5f; + + result = static_cast( a3*progress*progress*progress + a2*progress*progress + a1*progress + p1 + 0.5f ); +} + +inline void CubicInterpolate( float& result, float p0, float p1, float p2, float p3, float progress ) +{ + float a3 = p3*0.5f - p2*1.5f + p1*1.5f - p0*0.5f; + float a2 = p0 - p1*2.5f + p2*2.0f - p3*0.5f; + float a1 = (p2-p0)*0.5f; + + result = a3*progress*progress*progress + a2*progress*progress + a1*progress + p1; +} + +inline void CubicInterpolate( Vector2& result, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float progress ) +{ + Vector2 a3 = p3*0.5f - p2*1.5f + p1*1.5f - p0*0.5f; + Vector2 a2 = p0 - p1*2.5f + p2*2.0f - p3*0.5f; + Vector2 a1 = (p2-p0)*0.5f; + + result = a3*progress*progress*progress + a2*progress*progress + a1*progress + p1; +} + +inline void CubicInterpolate( Vector3& result, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float progress ) +{ + Vector3 a3 = p3*0.5f - p2*1.5f + p1*1.5f - p0*0.5f; + Vector3 a2 = p0 - p1*2.5f + p2*2.0f - p3*0.5f; + Vector3 a1 = (p2-p0)*0.5f; + + result = a3*progress*progress*progress + a2*progress*progress + a1*progress + p1; +} + +inline void CubicInterpolate( Vector4& result, const Vector4& p0, const Vector4& p1, const Vector4& p2, const Vector4& p3, float progress ) +{ + Vector4 a3 = p3*0.5f - p2*1.5f + p1*1.5f - p0*0.5f; + Vector4 a2 = p0 - p1*2.5f + p2*2.0f - p3*0.5f; + Vector4 a1 = (p2-p0)*0.5f; + + result = a3*progress*progress*progress + a2*progress*progress + a1*progress + p1; +} + +inline void CubicInterpolate( bool& result, bool p0, bool p1, bool p2, bool p3, float progress ) +{ + Interpolate( result, p1, p2, progress); +} + +inline void CubicInterpolate( Quaternion& result, const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float progress ) +{ + Interpolate( result, p1, p2, progress); +} + +inline void CubicInterpolate( AngleAxis& result, const AngleAxis& p0, const AngleAxis& p1, const AngleAxis& p2, const AngleAxis& p3, float progress ) +{ + Interpolate( result, p1, p2, progress); } } // namespace Internal diff --git a/dali/internal/update/animation/scene-graph-animator.h b/dali/internal/update/animation/scene-graph-animator.h index 13c245f..d6ee8f1 100644 --- a/dali/internal/update/animation/scene-graph-animator.h +++ b/dali/internal/update/animation/scene-graph-animator.h @@ -40,6 +40,8 @@ namespace Dali namespace Internal { +typedef Dali::Animation::Interpolation Interpolation; + namespace SceneGraph { @@ -635,7 +637,7 @@ struct KeyFrameBooleanFunctor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, Dali::Animation::Linear); } return property; } @@ -645,8 +647,8 @@ struct KeyFrameBooleanFunctor struct KeyFrameNumberFunctor { - KeyFrameNumberFunctor(KeyFrameNumberPtr keyFrames) - : mKeyFrames(keyFrames) + KeyFrameNumberFunctor(KeyFrameNumberPtr keyFrames, Interpolation interpolation) + : mKeyFrames(keyFrames),mInterpolation(interpolation) { } @@ -654,18 +656,19 @@ struct KeyFrameNumberFunctor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, mInterpolation); } return property; } KeyFrameNumberPtr mKeyFrames; + Interpolation mInterpolation; }; struct KeyFrameIntegerFunctor { - KeyFrameIntegerFunctor(KeyFrameIntegerPtr keyFrames) - : mKeyFrames(keyFrames) + KeyFrameIntegerFunctor(KeyFrameIntegerPtr keyFrames, Interpolation interpolation) + : mKeyFrames(keyFrames),mInterpolation(interpolation) { } @@ -673,18 +676,19 @@ struct KeyFrameIntegerFunctor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, mInterpolation); } return property; } KeyFrameIntegerPtr mKeyFrames; + Interpolation mInterpolation; }; struct KeyFrameVector2Functor { - KeyFrameVector2Functor(KeyFrameVector2Ptr keyFrames) - : mKeyFrames(keyFrames) + KeyFrameVector2Functor(KeyFrameVector2Ptr keyFrames, Interpolation interpolation) + : mKeyFrames(keyFrames),mInterpolation(interpolation) { } @@ -692,19 +696,20 @@ struct KeyFrameVector2Functor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, mInterpolation); } return property; } KeyFrameVector2Ptr mKeyFrames; + Interpolation mInterpolation; }; struct KeyFrameVector3Functor { - KeyFrameVector3Functor(KeyFrameVector3Ptr keyFrames) - : mKeyFrames(keyFrames) + KeyFrameVector3Functor(KeyFrameVector3Ptr keyFrames, Interpolation interpolation) + : mKeyFrames(keyFrames),mInterpolation(interpolation) { } @@ -712,18 +717,19 @@ struct KeyFrameVector3Functor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, mInterpolation); } return property; } KeyFrameVector3Ptr mKeyFrames; + Interpolation mInterpolation; }; struct KeyFrameVector4Functor { - KeyFrameVector4Functor(KeyFrameVector4Ptr keyFrames) - : mKeyFrames(keyFrames) + KeyFrameVector4Functor(KeyFrameVector4Ptr keyFrames, Interpolation interpolation) + : mKeyFrames(keyFrames),mInterpolation(interpolation) { } @@ -731,12 +737,13 @@ struct KeyFrameVector4Functor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, mInterpolation); } return property; } KeyFrameVector4Ptr mKeyFrames; + Interpolation mInterpolation; }; struct KeyFrameQuaternionFunctor @@ -750,7 +757,7 @@ struct KeyFrameQuaternionFunctor { if(mKeyFrames->IsActive(progress)) { - return mKeyFrames->GetValue(progress); + return mKeyFrames->GetValue(progress, Dali::Animation::Linear); } return property; } diff --git a/dali/public-api/animation/animation.cpp b/dali/public-api/animation/animation.cpp index 40c1e26..b07cb72 100644 --- a/dali/public-api/animation/animation.cpp +++ b/dali/public-api/animation/animation.cpp @@ -194,9 +194,14 @@ void Animation::AnimateTo(Property target, Property::Value destinationValue, Alp GetImplementation(*this).AnimateTo(target, destinationValue, alpha, period); } -void Animation::AnimateBetween(Property target, KeyFrames& keyFrames) +void Animation::AnimateBetween(Property target, KeyFrames& keyFrames ) { - GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames)); + GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames) ); +} + +void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, Interpolation interpolation) +{ + GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), interpolation ); } void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod period) @@ -204,16 +209,33 @@ void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), period); } +void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation) +{ + GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), period, interpolation ); +} + void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha) { GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), alpha); } +void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation) +{ + GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), alpha, interpolation); +} + + void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period) { GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), alpha, period); } +void Animation::AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation) +{ + GetImplementation(*this).AnimateBetween(target, GetImplementation(keyFrames), alpha, period, interpolation); +} + + // Actor specific animations void Animation::Animate( Actor actor, Path path, const Vector3& forward ) diff --git a/dali/public-api/animation/animation.h b/dali/public-api/animation/animation.h index 51470f1..17a2789 100644 --- a/dali/public-api/animation/animation.h +++ b/dali/public-api/animation/animation.h @@ -116,6 +116,15 @@ public: BakeFinal ///< If the animation is stopped, the animated property values are saved as if the animation had run to completion, otherwise behaves like Bake. }; + /** + * @brief What interpolation method to use on key-frame animations + */ + enum Interpolation + { + Linear, ///< Values in between key frames are interpolated using a linear polynomial. (Default) + Cubic ///< Values in between key frames are interpolated using a cubic polynomial. + }; + //Signal Names static const char* const SIGNAL_FINISHED; ///< name "finished" @@ -423,7 +432,7 @@ public: */ void AnimateTo(Property target, Property::Value destinationValue, AlphaFunction alpha, TimePeriod period); - /** + /** * @brief Animate a property between keyframes. * * @param [in] target The target object/property to animate. @@ -434,6 +443,15 @@ public: /** * @brief Animate a property between keyframes. * + * @param [in] target The target object + property to animate + * @param [in] keyFrames The set of time / value pairs between which to animate. + * @param [in] interpolation The method used to interpolate between values. + */ + void AnimateBetween(Property target, KeyFrames& keyFrames, Interpolation interpolation); + + /** + * @brief Animate a property between keyframes. + * * @param [in] target The target object/property to animate. * @param [in] keyFrames The key frames * @param [in] alpha The alpha function to apply. @@ -443,6 +461,16 @@ public: /** * @brief Animate a property between keyframes. * + * @param [in] target The target object + property to animate + * @param [in] keyFrames The set of time / value pairs between which to animate. + * @param [in] alpha The alpha function to apply. + * @param [in] interpolation The method used to interpolate between values. + */ + void AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation); + + /** + * @brief Animate a property between keyframes. + * * @param [in] target The target object/property to animate. * @param [in] keyFrames The key frames * @param [in] period The effect will occur during this time period. @@ -452,6 +480,16 @@ public: /** * @brief Animate a property between keyframes. * + * @param [in] target The target object + property to animate + * @param [in] keyFrames The set of time / value pairs between which to animate. + * @param [in] period The effect will occur duing this time period. + * @param [in] interpolation The method used to interpolate between values. + */ + void AnimateBetween(Property target, KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation); + + /** + * @brief Animate a property between keyframes. + * * @param [in] target The target object/property to animate. * @param [in] keyFrames The key frames * @param [in] alpha The alpha function to apply. @@ -459,6 +497,18 @@ public: */ void AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period); + /** + * @brief Animate a property between keyframes. + * + * @param [in] target The target object + property to animate + * @param [in] keyFrames The set of time / value pairs between which to animate. + * @param [in] alpha The alpha function to apply to the overall progress. + * @param [in] period The effect will occur duing this time period. + * @param [in] interpolation The method used to interpolate between values. + */ + void AnimateBetween(Property target, KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation); + + // Actor-specific convenience methods /** -- 2.7.4