From: Ferran Sole Date: Fri, 12 Sep 2014 10:35:02 +0000 (+0100) Subject: Added api function to specify speed factor of an animation X-Git-Tag: dali_1.0.9~1^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F17%2F27417%2F7;p=platform%2Fcore%2Fuifw%2Fdali-core.git Added api function to specify speed factor of an animation The speed factor is a multiplier of the normal velocity of the animation. Values between [0-1] will slow down the animation and values above 1 will speed it up. It is also possible to specify a negative multiplier so the animation will play in reverse. Change-Id: I4e74cb3cd5372f4c141089a05be8325e0892ce7f --- diff --git a/automated-tests/src/dali/utc-Dali-Animation.cpp b/automated-tests/src/dali/utc-Dali-Animation.cpp index 5287188..9bf8296 100644 --- a/automated-tests/src/dali/utc-Dali-Animation.cpp +++ b/automated-tests/src/dali/utc-Dali-Animation.cpp @@ -643,6 +643,183 @@ int UtcDaliAnimationPlay(void) END_TEST; } +int UtcDaliAnimationSetSpeedFactor(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + Stage::GetCurrent().Add(actor); + + // Build the animation + float durationSeconds(1.0f); + Animation animation = Animation::New(durationSeconds); + + const Vector3 initialPosition(0.0f, 0.0f, 0.0f); + const Vector3 targetPosition(100.0f, 100.0f, 100.0f); + + KeyFrames keyframes = KeyFrames::New(); + keyframes.Add( 0.0f, initialPosition); + keyframes.Add( 1.0f, targetPosition ); + animation.AnimateBetween( Property(actor, Actor::POSITION), keyframes, AlphaFunctions::Linear); + + //Set speed to be x2 + animation.SetSpeedFactor(2.0f); + + // Start the animation + animation.Play(); + + bool signalReceived(false); + AnimationFinishCheck finishCheck(signalReceived); + animation.FinishedSignal().Connect(&application, finishCheck); + + application.SendNotification(); + application.Render(static_cast(durationSeconds*200.0f)/* 40% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.4f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 80% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f) + 1u/*just beyond half the duration*/); + + // We did expect the animation to finish + application.SendNotification(); + finishCheck.CheckSignalReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), targetPosition, TEST_LOCATION ); + + // Check that nothing has changed after a couple of buffer swaps + application.Render(0); + DALI_TEST_EQUALS( targetPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + application.Render(0); + DALI_TEST_EQUALS( targetPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + + finishCheck.Reset(); + + //Test -1 speed factor. Animation will play in reverse at normal speed + animation.SetSpeedFactor( -1.0f ); + + // Start the animation + animation.Play(); + + application.SendNotification(); + application.Render(static_cast(durationSeconds*200.0f)/* 80% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 60% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.6f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 40% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.4f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 20% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.2f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f) + 1u/*just beyond the animation duration*/); + + // We did expect the animation to finish + application.SendNotification(); + finishCheck.CheckSignalReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), initialPosition, TEST_LOCATION ); + + // Check that nothing has changed after a couple of buffer swaps + application.Render(0); + DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + application.Render(0); + DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + + + //Test change speed factor on the fly + finishCheck.Reset(); + + //Set speed to be half of normal speed + animation.SetSpeedFactor( 0.5f ); + + // Start the animation + animation.Play(); + + application.SendNotification(); + application.Render(static_cast(durationSeconds*200.0f)/* 10% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.1f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 20% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.2f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*200.0f)/* 30% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.3f), TEST_LOCATION ); + + //Change speed factor while animation still playing. + animation.SetSpeedFactor(-1.0f); + application.SendNotification(); + application.Render(static_cast(durationSeconds*200.0f)/* 10% progress */); + + // We didn't expect the animation to finish yet + application.SendNotification(); + finishCheck.CheckSignalNotReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.1f), TEST_LOCATION ); + + application.Render(static_cast(durationSeconds*100.0f) + 1u/*just beyond the animation duration*/); + + // We did expect the animation to finish + application.SendNotification(); + finishCheck.CheckSignalReceived(); + DALI_TEST_EQUALS( actor.GetCurrentPosition(), initialPosition, TEST_LOCATION ); + + // Check that nothing has changed after a couple of buffer swaps + application.Render(0); + DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + application.Render(0); + DALI_TEST_EQUALS( initialPosition, actor.GetCurrentPosition(), TEST_LOCATION ); + END_TEST; +} + +int UtcDaliAnimationGetSpeedFactor(void) +{ + TestApplication application; + + Animation animation = Animation::New(1.0f); + animation.SetSpeedFactor(0.5f); + DALI_TEST_EQUALS(animation.GetSpeedFactor(), 0.5f, TEST_LOCATION); + + animation.SetSpeedFactor(-2.5f); + DALI_TEST_EQUALS(animation.GetSpeedFactor(), -2.5f, TEST_LOCATION); + END_TEST; +} + int UtcDaliAnimationPlayOffStage(void) { // Test that an animation can be played, when the actor is off-stage. diff --git a/dali/internal/event/animation/animation-impl.cpp b/dali/internal/event/animation/animation-impl.cpp index d5bc8f9..3f680a5 100644 --- a/dali/internal/event/animation/animation-impl.cpp +++ b/dali/internal/event/animation/animation-impl.cpp @@ -101,6 +101,7 @@ Animation::Animation( UpdateManager& updateManager, AnimationPlaylist& playlist, mFinishedCallback( NULL ), mFinishedCallbackObject( NULL ), mDurationSeconds( durationSeconds ), + mSpeedFactor(1.0f), mIsLooping( false ), mEndAction( endAction ), mDestroyAction( destroyAction ), @@ -137,7 +138,7 @@ void Animation::CreateSceneObject() DALI_ASSERT_DEBUG( mAnimation == NULL ); // Create a new animation, temporarily owned - SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mIsLooping, mEndAction, mDestroyAction ); + SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mIsLooping, mEndAction, mDestroyAction ); // Keep a const pointer to the animation. mAnimation = animation; @@ -1234,6 +1235,22 @@ void Animation::ExtendDuration( const TimePeriod& timePeriod ) } } +void Animation::SetSpeedFactor( float factor ) +{ + if( mAnimation ) + { + mSpeedFactor = factor; + SetSpeedFactorMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, factor ); + } +} + +float Animation::GetSpeedFactor() const +{ + return mSpeedFactor; +} + + + } // namespace Internal } // namespace Dali diff --git a/dali/internal/event/animation/animation-impl.h b/dali/internal/event/animation/animation-impl.h index 775637c..7405824 100644 --- a/dali/internal/event/animation/animation-impl.h +++ b/dali/internal/event/animation/animation-impl.h @@ -797,6 +797,16 @@ public: */ void SetCurrentProgress(float progress); + /* + * @copydoc Dali::Animation::SetSpeedFactor() + */ + void SetSpeedFactor( float factor ); + + /* + * @copydoc Dali::Animation::GetSpeedFactor() + */ + float GetSpeedFactor() const; + public: // For connecting animators to animations /** @@ -893,6 +903,7 @@ private: // Cached for public getters float mDurationSeconds; + float mSpeedFactor; bool mIsLooping; EndAction mEndAction; EndAction mDestroyAction; diff --git a/dali/internal/update/animation/scene-graph-animation.cpp b/dali/internal/update/animation/scene-graph-animation.cpp index 8b1b925..f6a290a 100644 --- a/dali/internal/update/animation/scene-graph-animation.cpp +++ b/dali/internal/update/animation/scene-graph-animation.cpp @@ -40,8 +40,9 @@ float DefaultAlphaFunc(float progress) return progress; // linear } -Animation::Animation(float durationSeconds, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction) +Animation::Animation(float durationSeconds, float speedFactor, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction) : mDurationSeconds(durationSeconds), + mSpeedFactor( speedFactor ), mLooping(isLooping), mEndAction(endAction), mDestroyAction(destroyAction), @@ -80,6 +81,11 @@ void Animation::SetDestroyAction(Dali::Animation::EndAction action) void Animation::Play() { mState = Playing; + + if ( mSpeedFactor < 0.0f && mElapsedSeconds <= 0.0f ) + { + mElapsedSeconds = mDurationSeconds; + } } void Animation::PlayFrom( float progress ) @@ -112,7 +118,15 @@ bool Animation::Stop(BufferIndex bufferIndex) { if( mEndAction == Dali::Animation::BakeFinal ) { - mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end + if( mSpeedFactor > 0.0f ) + { + mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end + } + else + { + mElapsedSeconds = 0.0f - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning + } + } UpdateAnimators(bufferIndex, true/*bake the final result*/); } @@ -158,18 +172,26 @@ bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds) // The animation must still be applied when Paused/Stopping if (mState == Playing) { - mElapsedSeconds += elapsedSeconds; + mElapsedSeconds += elapsedSeconds * mSpeedFactor; } if (mLooping) { - if (mElapsedSeconds > mDurationSeconds) + if (mElapsedSeconds > mDurationSeconds ) { mElapsedSeconds = fmod(mElapsedSeconds, mDurationSeconds); } + else if( mElapsedSeconds < 0.0f ) + { + mElapsedSeconds = mDurationSeconds - fmod(mElapsedSeconds, mDurationSeconds); + } } - const bool animationFinished(mState == Playing && mElapsedSeconds > mDurationSeconds); + + const bool animationFinished(mState == Playing && + (( mSpeedFactor > 0.0f && mElapsedSeconds > mDurationSeconds ) || + ( mSpeedFactor < 0.0f && mElapsedSeconds < 0.0f )) + ); UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard)); @@ -195,14 +217,14 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake) AnimatorBase *animator = *iter; const float initialDelay(animator->GetInitialDelay()); - if (mElapsedSeconds >= initialDelay) + if (mElapsedSeconds >= initialDelay || mSpeedFactor < 0.0f ) { // Calculate a progress specific to each individual animator float progress(1.0f); const float animatorDuration = animator->GetDuration(); if (animatorDuration > 0.0f) // animators can be "immediate" { - progress = min(1.0f, (mElapsedSeconds - initialDelay) / animatorDuration); + progress = Clamp((mElapsedSeconds - initialDelay) / animatorDuration, 0.0f , 1.0f ); } applied = animator->Update(bufferIndex, progress, bake); diff --git a/dali/internal/update/animation/scene-graph-animation.h b/dali/internal/update/animation/scene-graph-animation.h index 302763a..afce4f9 100644 --- a/dali/internal/update/animation/scene-graph-animation.h +++ b/dali/internal/update/animation/scene-graph-animation.h @@ -63,14 +63,15 @@ public: /** * Construct a new Animation. * @param[in] durationSeconds The duration of the animation in seconds. + * @param[in] speedFactor Multiplier to the animation velocity. * @param[in] isLooping Whether the animation will loop. * @param[in] endAction The action to perform when the animation ends. * @param[in] destroyAction The action to perform when the animation is destroyed. * @return A new Animation */ - static Animation* New( float durationSeconds, bool isLooping, EndAction endAction, EndAction destroyAction ) + static Animation* New( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction ) { - return new Animation( durationSeconds, isLooping, endAction, destroyAction ); + return new Animation( durationSeconds, speedFactor, isLooping, endAction, destroyAction ); } /** @@ -117,6 +118,11 @@ public: mElapsedSeconds = mDurationSeconds * progress; } + void SetSpeedFactor( float factor ) + { + mSpeedFactor = factor; + } + /** * Set whether the animation will loop. * @param[in] looping True if the animation will loop. @@ -245,7 +251,7 @@ protected: /** * Protected constructor. See New() */ - Animation( float durationSeconds, bool isLooping, EndAction endAction, EndAction destroyAction ); + Animation( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction ); private: @@ -266,6 +272,7 @@ private: protected: float mDurationSeconds; + float mSpeedFactor; bool mLooping; EndAction mEndAction; EndAction mDestroyAction; @@ -342,6 +349,17 @@ inline void SetCurrentProgressMessage( EventToUpdate& eventToUpdate, const Anima new (slot) LocalType( &animation, &Animation::SetCurrentProgress, progress ); } +inline void SetSpeedFactorMessage( EventToUpdate& eventToUpdate, const Animation& animation, float factor ) +{ + typedef MessageValue1< Animation, float > LocalType; + + // Reserve some memory inside the message queue + unsigned int* slot = eventToUpdate.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::SetSpeedFactor, factor ); +} + inline void PlayAnimationMessage( EventToUpdate& eventToUpdate, const Animation& animation ) { typedef Message< Animation > LocalType; diff --git a/dali/public-api/animation/animation.cpp b/dali/public-api/animation/animation.cpp index 44ee44f..5d908d6 100644 --- a/dali/public-api/animation/animation.cpp +++ b/dali/public-api/animation/animation.cpp @@ -498,4 +498,14 @@ void Animation::SetCurrentProgress( float progress ) return GetImplementation(*this).SetCurrentProgress( progress ); } +void Animation::SetSpeedFactor( float factor ) +{ + GetImplementation(*this).SetSpeedFactor( factor ); +} + +float Animation::GetSpeedFactor() const +{ + return GetImplementation(*this).GetSpeedFactor(); +} + } // namespace Dali diff --git a/dali/public-api/animation/animation.h b/dali/public-api/animation/animation.h index db2b23e..828c405 100644 --- a/dali/public-api/animation/animation.h +++ b/dali/public-api/animation/animation.h @@ -281,6 +281,24 @@ public: */ float GetCurrentProgress(); + /** + * @brief Specifies an speed factor for the animation. + * + * The speed factor is a multiplier of the normal velocity of the animation. Values between [0,1] will + * slow down the animation and values above one will speed up the animation. It is also possible to specify a negative multiplier + * to play the animation in reverse. + * + * @param[in] factor A value which will multiply the velocity. + */ + void SetSpeedFactor( float factor ); + + /** + * @brief Retrieve the speed factor of the animation + * + * @return speed factor + */ + float GetSpeedFactor() const; + /* * @brief Sets the progress of the animation. * The animation will play (or continue playing) from this point