END_TEST;
}
+int UtcDaliAnimationPlayRange(void)
+{
+ TestApplication application;
+
+ Actor actor = Actor::New();
+ Stage::GetCurrent().Add(actor);
+
+ // Build the animation
+ float durationSeconds(1.0f);
+ Animation animation = Animation::New(durationSeconds);
+ Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+ KeyFrames keyframes = KeyFrames::New();
+ keyframes.Add( 0.0f , Vector3(0.0f,0.0f,0.0f ) );
+ keyframes.Add( 1.0f , Vector3(100.0f,100.0f,100.0f ) );
+
+ animation.AnimateBetween( Property( actor, Actor::POSITION), keyframes );
+
+ // Set range between 0.4 and 0.8
+ animation.SetPlayRange( Vector2(0.4f,0.8f) );
+ animation.Play();
+
+ bool signalReceived(false);
+ AnimationFinishCheck finishCheck(signalReceived);
+ animation.FinishedSignal().Connect(&application, finishCheck);
+
+ //Test that setting progress outside the range doesn't work
+ animation.SetCurrentProgress( 0.9f );
+ application.SendNotification();
+ application.Render(0);
+ DALI_TEST_EQUALS( animation.GetCurrentProgress(), 0.4f, TEST_LOCATION );
+ animation.SetCurrentProgress( 0.2f );
+ application.SendNotification();
+ application.Render(0);
+ DALI_TEST_EQUALS( animation.GetCurrentProgress(), 0.4f, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(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 );
+
+ animation.Play(); // Test that calling play has no effect, when animation is already playing
+ application.SendNotification();
+ application.Render(static_cast<unsigned int>(durationSeconds*200.0f) + 1u/* 80% progress */);
+
+ // We did expect the animation to finish
+ application.SendNotification();
+ finishCheck.CheckSignalReceived();
+ DALI_TEST_EQUALS( actor.GetCurrentPosition(), (targetPosition * 0.8f), TEST_LOCATION );
+
+ // Check that nothing has changed after a couple of buffer swaps
+ application.Render(0);
+ DALI_TEST_EQUALS( targetPosition * 0.8f, actor.GetCurrentPosition(), TEST_LOCATION );
+ application.Render(0);
+ DALI_TEST_EQUALS( targetPosition * 0.8f, actor.GetCurrentPosition(), TEST_LOCATION );
+
+
+ //Loop inside the range
+ finishCheck.Reset();
+ animation.SetLooping( true );
+ animation.Play();
+ application.SendNotification();
+ float intervalSeconds = 0.1f;
+ float progress = 0.4f;
+ for (int iterations = 0; iterations < 10; ++iterations )
+ {
+ application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+ progress += intervalSeconds;
+ if (progress > 0.8f)
+ {
+ progress = progress - 0.4f;
+ }
+
+ DALI_TEST_EQUALS( targetPosition*progress, actor.GetCurrentPosition(), 0.001f, TEST_LOCATION );
+ }
+
+ // We didn't expect the animation to finish yet
+ application.SendNotification();
+ finishCheck.CheckSignalNotReceived();
+
+
+ //Test change range on the fly
+ animation.SetPlayRange( Vector2( 0.2f, 0.9f ) );
+ application.SendNotification();
+
+ for (int iterations = 0; iterations < 10; ++iterations )
+ {
+ application.Render(static_cast<unsigned int>(durationSeconds*intervalSeconds*1000.0f));
+
+ progress += intervalSeconds;
+ if (progress > 0.9f)
+ {
+ progress = progress - 0.7f;
+ }
+
+ DALI_TEST_EQUALS( targetPosition*progress, actor.GetCurrentPosition(), 0.001f, TEST_LOCATION );
+ }
+
+ END_TEST;
+}
+
+int UtcDaliAnimationSetPlayRange(void)
+{
+ TestApplication application;
+
+ Actor actor = Actor::New();
+ Stage::GetCurrent().Add(actor);
+
+ // Build the animation
+ Animation animation = Animation::New(0);
+ application.SendNotification();
+
+ //If PlayRange not specified it should be 0.0-1.0 by default
+ DALI_TEST_EQUALS( Vector2(0.0,1.0), animation.GetPlayRange(), TEST_LOCATION );
+
+ //PlayRange out of bounds
+ animation.SetPlayRange( Vector2(-1.0f,1.0f) );
+ application.SendNotification();
+ DALI_TEST_EQUALS( Vector2(0.0f,1.0f), animation.GetPlayRange(), TEST_LOCATION );
+ animation.SetPlayRange( Vector2(0.0f,2.0f) );
+ application.SendNotification();
+ DALI_TEST_EQUALS( Vector2(0.0f,1.0f), animation.GetPlayRange(), TEST_LOCATION );
+
+ //If playRange is not in the correct order it has to be ordered
+ animation.SetPlayRange( Vector2(0.8f,0.2f) );
+ application.SendNotification();
+ DALI_TEST_EQUALS( Vector2(0.2f,0.8f), animation.GetPlayRange(), TEST_LOCATION );
+
+ // Set range between 0.4 and 0.8
+ animation.SetPlayRange( Vector2(0.4f,0.8f) );
+ application.SendNotification();
+ DALI_TEST_EQUALS( Vector2(0.4f,0.8f), animation.GetPlayRange(), TEST_LOCATION );
+
+ END_TEST;
+}
int UtcDaliAnimationPause(void)
{
mDurationSeconds( durationSeconds ),
mSpeedFactor(1.0f),
mIsLooping( false ),
+ mPlayRange( Vector2(0.0f,1.0f)),
mEndAction( endAction ),
mDestroyAction( destroyAction ),
mDefaultAlpha( defaultAlpha )
DALI_ASSERT_DEBUG( mAnimation == NULL );
// Create a new animation, temporarily owned
- SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mIsLooping, mEndAction, mDestroyAction );
+ SceneGraph::Animation* animation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mIsLooping, mEndAction, mDestroyAction );
// Keep a const pointer to the animation.
mAnimation = animation;
void Animation::PlayFrom( float progress )
{
- if( progress >= 0.0f && progress <= 1.0f )
+ if( progress >= mPlayRange.x && progress <= mPlayRange.y )
{
// Update the current playlist
mPlaylist.OnPlay( *this );
return done;
}
-float Animation::GetCurrentProgress()
+void Animation::SetCurrentProgress(float progress)
{
- if( mAnimation )
+ if( mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y )
{
- return mAnimation->GetCurrentProgress();
+ // mAnimation is being used in a separate thread; queue a message to set the current progress
+ SetCurrentProgressMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, progress );
}
-
- return 0.0f;
}
-void Animation::SetCurrentProgress(float progress)
+float Animation::GetCurrentProgress()
{
- if( mAnimation && progress >= 0.0f && progress <= 1.0f )
+ if( mAnimation )
{
- // mAnimation is being used in a separate thread; queue a message to set the current progress
- SetCurrentProgressMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, progress );
+ return mAnimation->GetCurrentProgress();
}
+
+ return 0.0f;
}
void Animation::ExtendDuration( const TimePeriod& timePeriod )
return mSpeedFactor;
}
+void Animation::SetPlayRange( const Vector2& range)
+{
+ //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( range.x > range.y )
+ {
+ orderedRange = Vector2(range.y, range.x);
+ }
+
+ // Cache for public getters
+ mPlayRange = orderedRange;
+
+ // mAnimation is being used in a separate thread; queue a message to set play range
+ SetPlayRangeMessage( mUpdateManager.GetEventToUpdate(), *mAnimation, orderedRange );
+ }
+}
+
+Vector2 Animation::GetPlayRange() const
+{
+ return mPlayRange;
+}
} // namespace Internal
void Resize(Actor& actor, const Vector3& size, AlphaFunction alpha, float delaySeconds, float durationSeconds);
/*
- * @copydoc Dali::Animation::GetCurrentProgress()
+ * @copydoc Dali::Animation::SetCurrentProgress()
*/
- float GetCurrentProgress();
+ void SetCurrentProgress(float progress);
/*
- * @copydoc Dali::Animation::SetCurrentProgress()
+ * @copydoc Dali::Animation::GetCurrentProgress()
*/
- void SetCurrentProgress(float progress);
+ float GetCurrentProgress();
/*
* @copydoc Dali::Animation::SetSpeedFactor()
*/
float GetSpeedFactor() const;
+ /*
+ * @copydoc Dali::Animation::SetPlayRange()
+ */
+ void SetPlayRange( const Vector2& range );
+
+ /*
+ * @copydoc Dali::Animation::GetPlayRange
+ */
+ Vector2 GetPlayRange() const;
+
public: // For connecting animators to animations
/**
float mDurationSeconds;
float mSpeedFactor;
bool mIsLooping;
+ Vector2 mPlayRange;
EndAction mEndAction;
EndAction mDestroyAction;
AlphaFunction mDefaultAlpha;
return progress; // linear
}
-Animation::Animation(float durationSeconds, float speedFactor, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction)
+Animation::Animation(float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction destroyAction)
: mDurationSeconds(durationSeconds),
mSpeedFactor( speedFactor ),
mLooping(isLooping),
mEndAction(endAction),
mDestroyAction(destroyAction),
mState(Stopped),
- mElapsedSeconds(0.0f),
- mPlayCount(0)
+ mElapsedSeconds(playRange.x*mDurationSeconds),
+ mPlayCount(0),
+ mPlayRange( playRange )
{
}
mDestroyAction = action;
}
+void Animation::SetPlayRange( const Vector2& range )
+{
+ mPlayRange = range;
+
+ //Make sure mElapsedSeconds is within the new range
+ mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
+}
+
void Animation::Play()
{
mState = Playing;
- if ( mSpeedFactor < 0.0f && mElapsedSeconds <= 0.0f )
+ if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
{
- mElapsedSeconds = mDurationSeconds;
+ mElapsedSeconds = mPlayRange.y * mDurationSeconds;
}
}
{
if( mSpeedFactor > 0.0f )
{
- mElapsedSeconds = mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
+ mElapsedSeconds = mPlayRange.y*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
+ mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
}
}
++mPlayCount;
}
- mElapsedSeconds = 0.0f;
+ mElapsedSeconds = mPlayRange.x*mDurationSeconds;
mState = Stopped;
return animationFinished;
mElapsedSeconds += elapsedSeconds * mSpeedFactor;
}
+ Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
if (mLooping)
{
- if (mElapsedSeconds > mDurationSeconds )
+ if (mElapsedSeconds > playRangeSeconds.y )
{
- mElapsedSeconds = fmod(mElapsedSeconds, mDurationSeconds);
+ mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
}
- else if( mElapsedSeconds < 0.0f )
+ else if( mElapsedSeconds < playRangeSeconds.x )
{
- mElapsedSeconds = mDurationSeconds - fmod(mElapsedSeconds, mDurationSeconds);
+ mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
}
}
-
- const bool animationFinished(mState == Playing &&
- (( mSpeedFactor > 0.0f && mElapsedSeconds > mDurationSeconds ) ||
- ( mSpeedFactor < 0.0f && mElapsedSeconds < 0.0f ))
+ const bool animationFinished(mState == Playing &&
+ (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y ) ||
+ ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
);
UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard));
// The animation has now been played to completion
++mPlayCount;
- mElapsedSeconds = 0.0f;
+ mElapsedSeconds = playRangeSeconds.x;
mState = Stopped;
}
void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake)
{
+ float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
{
// If an animator is not successfully applied, then it has been orphaned
AnimatorBase *animator = *iter;
const float initialDelay(animator->GetInitialDelay());
- if (mElapsedSeconds >= initialDelay || mSpeedFactor < 0.0f )
+ if (elapsedSecondsClamped >= 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 = Clamp((mElapsedSeconds - initialDelay) / animatorDuration, 0.0f , 1.0f );
+ progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
}
applied = animator->Update(bufferIndex, progress, bake);
* Construct a new Animation.
* @param[in] durationSeconds The duration of the animation in seconds.
* @param[in] speedFactor Multiplier to the animation velocity.
+ * @param[in] playRange Minimum and maximum progress between which the animation will play.
* @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, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction )
+ static Animation* New( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction destroyAction )
{
- return new Animation( durationSeconds, speedFactor, isLooping, endAction, destroyAction );
+ return new Animation( durationSeconds, speedFactor, playRange, isLooping, endAction, destroyAction );
}
/**
}
/**
+ * Set the playing range. The animation will only play between the minimum and maximum progress
+ * speficied.
+ *
+ * @param[in] range Two values between [0,1] to specify minimum and maximum progress.
+ */
+ void SetPlayRange( const Vector2& range );
+
+ /**
* Play the animation.
*/
void Play();
/**
* Protected constructor. See New()
*/
- Animation( float durationSeconds, float speedFactor, bool isLooping, EndAction endAction, EndAction destroyAction );
+ Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction destroyAction );
private:
float mElapsedSeconds;
int mPlayCount;
+ Vector2 mPlayRange;
AnimatorContainer mAnimators;
};
new (slot) LocalType( &animation, &Animation::SetSpeedFactor, factor );
}
+inline void SetPlayRangeMessage( EventToUpdate& eventToUpdate, const Animation& animation, const Vector2& range )
+{
+ typedef MessageValue1< Animation, Vector2 > 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::SetPlayRange, range );
+}
+
inline void PlayAnimationMessage( EventToUpdate& eventToUpdate, const Animation& animation )
{
typedef Message< Animation > LocalType;
GetImplementation(*this).Resize(GetImplementation(actor), size, alpha, delaySeconds, durationSeconds);
}
-float Animation::GetCurrentProgress()
+void Animation::SetCurrentProgress( float progress )
{
- return GetImplementation(*this).GetCurrentProgress();
+ return GetImplementation(*this).SetCurrentProgress( progress );
}
-void Animation::SetCurrentProgress( float progress )
+float Animation::GetCurrentProgress()
{
- return GetImplementation(*this).SetCurrentProgress( progress );
+ return GetImplementation(*this).GetCurrentProgress();
}
void Animation::SetSpeedFactor( float factor )
return GetImplementation(*this).GetSpeedFactor();
}
+void Animation::SetPlayRange( const Vector2& range )
+{
+ GetImplementation(*this).SetPlayRange(range);
+}
+
+Vector2 Animation::GetPlayRange() const
+{
+ return GetImplementation(*this).GetPlayRange();
+}
+
} // namespace Dali
*/
AlphaFunction GetDefaultAlphaFunction() const;
+ /*
+ * @brief Sets the progress of the animation.
+ * The animation will play (or continue playing) from this point. The progress
+ * must be in the 0-1 interval or in the play range interval if defined ( See SetPlayRange ),
+ * otherwise, it will be ignored.
+ *
+ * @param[in] progress The new progress as a normalized value between [0,1] or between the
+ * play range if specified.
+ */
+ void SetCurrentProgress( float progress );
+
/**
* @brief Retrieve the current progress of the animation.
*
*/
float GetSpeedFactor() const;
- /*
- * @brief Sets the progress of the animation.
- * The animation will play (or continue playing) from this point
+ /**
+ * @brief Set the playing range.
+ * Animation will play between the values specified. Both values ( range.x and range.y ) should be between 0-1,
+ * otherwise they will be ignored. If the range provided is not in proper order ( minimum,maximum ), it will be reordered.
*
- * @param[in] progress The new progress as a normalized value between [0,1].
+ * @param[in] range Two values between [0,1] to specify minimum and maximum progress. The
+ * animation will play between those values.
*/
- void SetCurrentProgress( float progress );
+ void SetPlayRange( const Vector2& range );
+
+ /**
+ * @brief Get the playing range
+ *
+ * @return The play range defined for the animation.
+ */
+ Vector2 GetPlayRange() const;
/**
* @brief Play the animation.
/**
* @brief Play the animation from a given point.
- * @param[in] progress A value between [0,1] form where the animation should start playing
+ * The progress must be in the 0-1 interval or in the play range interval if defined ( See SetPlayRange ),
+ * otherwise, it will be ignored.
+ *
+ * @param[in] progress A value between [0,1], or between the play range if specified, form where the animation should start playing
*/
void PlayFrom( float progress );