2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/event/animation/animation-impl.h>
20 #include <dali/public-api/object/property-map.h>
25 #include <dali/internal/event/animation/animation-playlist.h>
26 #include <dali/internal/event/animation/animator-connector.h>
27 #include <dali/internal/event/animation/key-frames-impl.h>
28 #include <dali/internal/event/animation/path-impl.h>
29 #include <dali/internal/event/common/notification-manager.h>
30 #include <dali/internal/event/common/property-helper.h>
31 #include <dali/internal/event/common/stage-impl.h>
32 #include <dali/internal/event/common/thread-local-storage.h>
33 #include <dali/internal/update/animation/scene-graph-animator.h>
34 #include <dali/internal/update/manager/update-manager.h>
35 #include <dali/public-api/animation/alpha-function.h>
36 #include <dali/public-api/animation/time-period.h>
37 #include <dali/public-api/common/dali-common.h>
38 #include <dali/public-api/math/radian.h>
39 #include <dali/public-api/math/vector2.h>
40 #include <dali/public-api/object/type-registry.h>
42 using Dali::Internal::SceneGraph::AnimatorBase;
43 using Dali::Internal::SceneGraph::Shader;
44 using Dali::Internal::SceneGraph::UpdateManager;
50 static bool SHOW_VALUE = true;
51 static bool HIDE_VALUE = false;
57 static constexpr std::string_view SIGNAL_FINISHED = "finished";
61 static constexpr std::string_view ACTION_PLAY = "play";
62 static constexpr std::string_view ACTION_STOP = "stop";
63 static constexpr std::string_view ACTION_PAUSE = "pause";
67 return Dali::Animation::New(0.f);
70 TypeRegistration mType(typeid(Dali::Animation), typeid(Dali::BaseHandle), Create);
72 SignalConnectorType signalConnector1(mType, std::string(SIGNAL_FINISHED), &Animation::DoConnectSignal);
74 TypeAction action1(mType, std::string(ACTION_PLAY), &Animation::DoAction);
75 TypeAction action2(mType, std::string(ACTION_STOP), &Animation::DoAction);
76 TypeAction action3(mType, std::string(ACTION_PAUSE), &Animation::DoAction);
78 const Dali::Animation::EndAction DEFAULT_END_ACTION(Dali::Animation::BAKE);
79 const Dali::Animation::EndAction DEFAULT_DISCONNECT_ACTION(Dali::Animation::BAKE_FINAL);
80 const Dali::Animation::Interpolation DEFAULT_INTERPOLATION(Dali::Animation::LINEAR);
81 const Dali::AlphaFunction DEFAULT_ALPHA_FUNCTION(Dali::AlphaFunction::DEFAULT);
84 * Helper to tell if a property is animatable (if we have animators for it)
86 * @param type type to check
87 * @return true if animatable
89 inline bool IsAnimatable(Property::Type type)
91 bool animatable = false;
94 case Property::BOOLEAN:
96 case Property::INTEGER:
97 case Property::VECTOR2:
98 case Property::VECTOR3:
99 case Property::VECTOR4:
100 case Property::ROTATION:
105 case Property::MATRIX: // matrix is allowed as a scene graph property but there's no animators for it
106 case Property::MATRIX3: // matrix3 is allowed as a scene graph property but there's no animators for it
108 case Property::RECTANGLE:
109 case Property::STRING:
110 case Property::ARRAY:
112 case Property::EXTENTS:
121 * Helper to validate animation input values
123 * @param propertyType type of the property that is being animated
124 * @param destinationType type of the target
125 * @param period time period of the animation
127 void ValidateParameters(Property::Type propertyType, Property::Type destinationType, const TimePeriod& period)
129 // destination value has to be animatable
130 DALI_ASSERT_ALWAYS(IsAnimatable(propertyType) && "Property type is not animatable");
131 DALI_ASSERT_ALWAYS(IsAnimatable(destinationType) && "Target value is not animatable");
132 DALI_ASSERT_ALWAYS(propertyType == destinationType && "Property and target types don't match");
133 DALI_ASSERT_ALWAYS(period.durationSeconds >= 0 && "Duration must be >=0");
136 } // anonymous namespace
138 AnimationPtr Animation::New(float durationSeconds)
140 if(durationSeconds < 0.0f)
142 DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
143 durationSeconds = 0.0f;
146 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
147 AnimationPtr animation = new Animation(tls.GetEventThreadServices(), tls.GetAnimationPlaylist(), durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION);
149 // Second-phase construction
150 animation->Initialize();
155 Animation::Animation(EventThreadServices& eventThreadServices, AnimationPlaylist& playlist, float durationSeconds, EndAction endAction, EndAction disconnectAction, AlphaFunction defaultAlpha)
156 : mEventThreadServices(eventThreadServices),
158 mDefaultAlpha(defaultAlpha),
159 mDurationSeconds(durationSeconds),
160 mEndAction(endAction),
161 mDisconnectAction(disconnectAction)
165 void Animation::Initialize()
167 // Connect to the animation playlist
168 mPlaylist.AnimationCreated(*this);
175 Animation::~Animation()
177 // Guard to allow handle destruction after Core has been destroyed
178 if(Stage::IsInstalled())
180 // Disconnect from the animation playlist
181 mPlaylist.AnimationDestroyed(*this);
183 DestroySceneObject();
189 void Animation::CreateSceneObject()
191 DALI_ASSERT_DEBUG(mAnimation == NULL);
193 // Create a new animation, Keep a const pointer to the animation.
194 mAnimation = SceneGraph::Animation::New(mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction);
195 OwnerPointer<SceneGraph::Animation> transferOwnership(const_cast<SceneGraph::Animation*>(mAnimation));
196 AddAnimationMessage(mEventThreadServices.GetUpdateManager(), transferOwnership);
199 void Animation::DestroySceneObject()
201 if(mAnimation != nullptr)
203 // Remove animation using a message to the update manager
204 RemoveAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
205 mAnimation = nullptr;
209 void Animation::SetDuration(float seconds)
213 DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
217 mDurationSeconds = seconds;
219 // mAnimation is being used in a separate thread; queue a message to set the value
220 SetDurationMessage(mEventThreadServices, *mAnimation, seconds);
223 void Animation::SetProgressNotification(float progress)
225 // mAnimation is being used in a separate thread; queue a message to set the value
226 mProgressReachedMarker = progress;
229 float Animation::GetProgressNotification()
231 return mProgressReachedMarker;
234 float Animation::GetDuration() const
236 // This is not animatable; the cached value is up-to-date.
237 return mDurationSeconds;
240 void Animation::SetLooping(bool on)
242 SetLoopCount(on ? 0 : 1);
245 void Animation::SetLoopCount(int32_t count)
247 // Cache for public getters
250 // mAnimation is being used in a separate thread; queue a message to set the value
251 SetLoopingMessage(mEventThreadServices, *mAnimation, mLoopCount);
254 int32_t Animation::GetLoopCount()
259 int32_t Animation::GetCurrentLoop()
264 bool Animation::IsLooping() const
266 return mLoopCount != 1;
269 void Animation::SetEndAction(EndAction action)
271 // Cache for public getters
274 // mAnimation is being used in a separate thread; queue a message to set the value
275 SetEndActionMessage(mEventThreadServices, *mAnimation, action);
278 Dali::Animation::EndAction Animation::GetEndAction() const
280 // This is not animatable; the cached value is up-to-date.
284 void Animation::SetDisconnectAction(EndAction action)
286 // Cache for public getters
287 mDisconnectAction = action;
289 // mAnimation is being used in a separate thread; queue a message to set the value
290 SetDisconnectActionMessage(mEventThreadServices, *mAnimation, action);
293 Dali::Animation::EndAction Animation::GetDisconnectAction() const
295 // This is not animatable; the cached value is up-to-date.
296 return mDisconnectAction;
299 void Animation::Play()
301 // Update the current playlist
302 mPlaylist.OnPlay(*this);
304 mState = Dali::Animation::PLAYING;
306 NotifyObjects(Notify::USE_TARGET_VALUE);
308 SendFinalProgressNotificationMessage();
310 // mAnimation is being used in a separate thread; queue a Play message
311 PlayAnimationMessage(mEventThreadServices, *mAnimation);
314 void Animation::PlayFrom(float progress)
316 if(progress >= mPlayRange.x && progress <= mPlayRange.y)
318 // Update the current playlist
319 mPlaylist.OnPlay(*this);
321 mState = Dali::Animation::PLAYING;
323 NotifyObjects(Notify::USE_TARGET_VALUE);
325 SendFinalProgressNotificationMessage();
327 // mAnimation is being used in a separate thread; queue a Play message
328 PlayAnimationFromMessage(mEventThreadServices, *mAnimation, progress);
332 void Animation::PlayAfter(float delaySeconds)
334 // The negative delay means play immediately.
335 delaySeconds = std::max(0.f, delaySeconds);
337 mDelaySeconds = delaySeconds;
339 // Update the current playlist
340 mPlaylist.OnPlay(*this);
342 mState = Dali::Animation::PLAYING;
344 NotifyObjects(Notify::USE_TARGET_VALUE);
346 SendFinalProgressNotificationMessage();
348 // mAnimation is being used in a separate thread; queue a message to set the value
349 PlayAfterMessage(mEventThreadServices, *mAnimation, delaySeconds);
352 void Animation::Pause()
354 mState = Dali::Animation::PAUSED;
356 // mAnimation is being used in a separate thread; queue a Pause message
357 PauseAnimationMessage(mEventThreadServices, *mAnimation);
359 // Notify the objects with the _paused_, i.e. current values
360 NotifyObjects(Notify::FORCE_CURRENT_VALUE);
363 Dali::Animation::State Animation::GetState() const
368 void Animation::Stop()
370 mState = Dali::Animation::STOPPED;
372 // mAnimation is being used in a separate thread; queue a Stop message
373 StopAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
375 // Only notify the objects with the _stopped_, i.e. current values if the end action is set to BAKE
376 if(mEndAction == EndAction::BAKE)
378 NotifyObjects(Notify::USE_CURRENT_VALUE);
382 void Animation::Clear()
384 DALI_ASSERT_DEBUG(mAnimation);
386 // Only notify the objects with the current values if the end action is set to BAKE
387 if(mEndAction == EndAction::BAKE && mState != Dali::Animation::STOPPED)
389 NotifyObjects(Notify::USE_CURRENT_VALUE);
392 // Remove all the connectors
395 // Reset the connector target values
396 mConnectorTargetValues.clear();
397 mConnectorTargetValuesSortRequired = false;
399 // Replace the old scene-object with a new one
400 DestroySceneObject();
403 // Reset the notification count, since the new scene-object has never been played
404 mNotificationCount = 0;
406 // Update the current playlist
407 mPlaylist.OnClear(*this);
410 void Animation::AnimateBy(Property& target, Property::Value relativeValue)
412 AnimateBy(target, std::move(relativeValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
415 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha)
417 AnimateBy(target, std::move(relativeValue), alpha, TimePeriod(mDurationSeconds));
420 void Animation::AnimateBy(Property& target, Property::Value relativeValue, TimePeriod period)
422 AnimateBy(target, std::move(relativeValue), mDefaultAlpha, period);
425 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha, TimePeriod period)
427 Object& object = GetImplementation(target.object);
428 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
429 const Property::Type destinationType = relativeValue.GetType();
431 // validate animation parameters, if component index is set then use float as checked type
432 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
436 ExtendDuration(period);
438 // keep the current count.
439 auto connectorIndex = mConnectors.Count();
441 // using destination type so component animation gets correct type
442 switch(destinationType)
444 case Property::BOOLEAN:
446 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
447 target.propertyIndex,
448 target.componentIndex,
449 AnimateByBoolean(relativeValue.Get<bool>()),
455 case Property::INTEGER:
457 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
458 target.propertyIndex,
459 target.componentIndex,
460 AnimateByInteger(relativeValue.Get<int32_t>()),
466 case Property::FLOAT:
468 AddAnimatorConnector(AnimatorConnector<float>::New(object,
469 target.propertyIndex,
470 target.componentIndex,
471 AnimateByFloat(relativeValue.Get<float>()),
477 case Property::VECTOR2:
479 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
480 target.propertyIndex,
481 target.componentIndex,
482 AnimateByVector2(relativeValue.Get<Vector2>()),
488 case Property::VECTOR3:
490 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
491 target.propertyIndex,
492 target.componentIndex,
493 AnimateByVector3(relativeValue.Get<Vector3>()),
499 case Property::VECTOR4:
501 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
502 target.propertyIndex,
503 target.componentIndex,
504 AnimateByVector4(relativeValue.Get<Vector4>()),
510 case Property::ROTATION:
512 AngleAxis angleAxis = relativeValue.Get<AngleAxis>();
514 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
515 target.propertyIndex,
516 target.componentIndex,
517 RotateByAngleAxis(angleAxis.angle, angleAxis.axis),
525 DALI_ASSERT_DEBUG(false && "Property not supported");
529 AppendConnectorTargetValues({std::move(relativeValue), period, connectorIndex, Animation::BY});
532 void Animation::AnimateTo(Property& target, Property::Value destinationValue)
534 AnimateTo(target, std::move(destinationValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
537 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha)
539 AnimateTo(target, std::move(destinationValue), alpha, TimePeriod(mDurationSeconds));
542 void Animation::AnimateTo(Property& target, Property::Value destinationValue, TimePeriod period)
544 AnimateTo(target, std::move(destinationValue), mDefaultAlpha, period);
547 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha, TimePeriod period)
549 Object& object = GetImplementation(target.object);
550 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
551 const Property::Type destinationType = destinationValue.GetType();
553 // validate animation parameters, if component index is set then use float as checked type
554 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
558 ExtendDuration(period);
560 // keep the current count.
561 auto connectorIndex = mConnectors.Count();
563 // using destination type so component animation gets correct type
564 switch(destinationType)
566 case Property::BOOLEAN:
568 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
569 target.propertyIndex,
570 target.componentIndex,
571 AnimateToBoolean(destinationValue.Get<bool>()),
577 case Property::INTEGER:
579 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
580 target.propertyIndex,
581 target.componentIndex,
582 AnimateToInteger(destinationValue.Get<int32_t>()),
588 case Property::FLOAT:
590 AddAnimatorConnector(AnimatorConnector<float>::New(object,
591 target.propertyIndex,
592 target.componentIndex,
593 AnimateToFloat(destinationValue.Get<float>()),
599 case Property::VECTOR2:
601 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
602 target.propertyIndex,
603 target.componentIndex,
604 AnimateToVector2(destinationValue.Get<Vector2>()),
610 case Property::VECTOR3:
612 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
613 target.propertyIndex,
614 target.componentIndex,
615 AnimateToVector3(destinationValue.Get<Vector3>()),
621 case Property::VECTOR4:
623 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
624 target.propertyIndex,
625 target.componentIndex,
626 AnimateToVector4(destinationValue.Get<Vector4>()),
632 case Property::ROTATION:
634 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
635 target.propertyIndex,
636 target.componentIndex,
637 RotateToQuaternion(destinationValue.Get<Quaternion>()),
645 DALI_ASSERT_DEBUG(false && "Property not supported");
649 AppendConnectorTargetValues({std::move(destinationValue), period, connectorIndex, Animation::TO});
652 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames)
654 AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
657 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, Interpolation interpolation)
659 AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation);
662 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, TimePeriod period)
664 AnimateBetween(target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION);
667 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, TimePeriod period, Interpolation interpolation)
669 AnimateBetween(target, keyFrames, mDefaultAlpha, period, interpolation);
672 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha)
674 AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
677 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, Interpolation interpolation)
679 AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation);
682 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, TimePeriod period)
684 AnimateBetween(target, keyFrames, alpha, period, DEFAULT_INTERPOLATION);
687 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation)
689 Object& object = GetImplementation(target.object);
690 const KeyFrames& keyFramesImpl = GetImplementation(keyFrames);
692 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
693 const Property::Type destinationType = keyFramesImpl.GetType();
695 // validate animation parameters, if component index is set then use float as checked type
696 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
700 ExtendDuration(period);
702 AppendConnectorTargetValues({keyFramesImpl.GetLastKeyFrameValue(), period, mConnectors.Count(), BETWEEN});
704 // using destination type so component animation gets correct type
705 switch(destinationType)
707 case Dali::Property::BOOLEAN:
709 auto kf = GetSpecialization<const KeyFrameBoolean*>(keyFramesImpl);
710 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
711 target.propertyIndex,
712 target.componentIndex,
713 KeyFrameBooleanFunctor(*kf), // takes a copy of the keyframe
719 case Dali::Property::INTEGER:
721 auto kf = GetSpecialization<const KeyFrameInteger*>(keyFramesImpl);
722 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
723 target.propertyIndex,
724 target.componentIndex,
725 KeyFrameIntegerFunctor(*kf, interpolation), // takes a copy of the keyframe
731 case Dali::Property::FLOAT:
733 auto kf = GetSpecialization<const KeyFrameNumber*>(keyFramesImpl);
734 AddAnimatorConnector(AnimatorConnector<float>::New(object,
735 target.propertyIndex,
736 target.componentIndex,
737 KeyFrameNumberFunctor(*kf, interpolation), // takes a copy of the keyframe
743 case Dali::Property::VECTOR2:
745 auto kf = GetSpecialization<const KeyFrameVector2*>(keyFramesImpl);
746 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
747 target.propertyIndex,
748 target.componentIndex,
749 KeyFrameVector2Functor(*kf, interpolation), // takes a copy of the keyframe
755 case Dali::Property::VECTOR3:
757 auto kf = GetSpecialization<const KeyFrameVector3*>(keyFramesImpl);
758 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
759 target.propertyIndex,
760 target.componentIndex,
761 KeyFrameVector3Functor(*kf, interpolation), // takes a copy of the keyframe
767 case Dali::Property::VECTOR4:
769 auto kf = GetSpecialization<const KeyFrameVector4*>(keyFramesImpl);
770 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
771 target.propertyIndex,
772 target.componentIndex,
773 KeyFrameVector4Functor(*kf, interpolation), // takes a copy of the keyframe
779 case Dali::Property::ROTATION:
781 auto kf = GetSpecialization<const KeyFrameQuaternion*>(keyFramesImpl);
782 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
783 target.propertyIndex,
784 target.componentIndex,
785 KeyFrameQuaternionFunctor(*kf), // takes a copy of the keyframe
793 DALI_ASSERT_DEBUG(false && "Property not supported");
798 bool Animation::HasFinished()
800 bool hasFinished(false);
801 const int32_t playedCount(mAnimation->GetPlayedCount());
803 // If the play count has been incremented, then another notification is required
804 mCurrentLoop = mAnimation->GetCurrentLoop();
806 if(playedCount > mNotificationCount)
808 // Note that only one signal is emitted, if the animation has been played repeatedly
809 mNotificationCount = playedCount;
813 mState = Dali::Animation::STOPPED;
819 Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
821 return mFinishedSignal;
824 Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
826 return mProgressReachedSignal;
829 void Animation::EmitSignalFinish()
831 if(!mFinishedSignal.Empty())
833 Dali::Animation handle(this);
834 mFinishedSignal.Emit(handle);
838 void Animation::EmitSignalProgressReached()
840 if(!mProgressReachedSignal.Empty())
842 Dali::Animation handle(this);
843 mProgressReachedSignal.Emit(handle);
847 bool Animation::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
849 bool connected(false);
850 Animation* animation = static_cast<Animation*>(object); // TypeRegistry guarantees that this is the correct type.
852 if(SIGNAL_FINISHED == signalName)
854 animation->FinishedSignal().Connect(tracker, functor);
861 void Animation::AddAnimatorConnector(AnimatorConnectorBase* connector)
863 DALI_ASSERT_DEBUG(NULL != connector);
865 connector->SetParent(*this);
867 mConnectors.PushBack(connector);
870 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward)
872 Animate(actor, path, forward, mDefaultAlpha, TimePeriod(mDurationSeconds));
875 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha)
877 Animate(actor, path, forward, alpha, TimePeriod(mDurationSeconds));
880 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, TimePeriod period)
882 Animate(actor, path, forward, mDefaultAlpha, period);
885 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha, TimePeriod period)
887 ExtendDuration(period);
889 PathPtr pathCopy = Path::Clone(path);
891 // Position animation
892 AddAnimatorConnector(AnimatorConnector<Vector3>::New(actor,
893 Dali::Actor::Property::POSITION,
894 Property::INVALID_COMPONENT_INDEX,
895 PathPositionFunctor(pathCopy),
899 // If forward is zero, PathRotationFunctor will always return the unit quaternion
900 if(forward != Vector3::ZERO)
902 // Rotation animation
903 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(actor,
904 Dali::Actor::Property::ORIENTATION,
905 Property::INVALID_COMPONENT_INDEX,
906 PathRotationFunctor(pathCopy, forward),
912 void Animation::Show(Actor& actor, float delaySeconds)
914 ExtendDuration(TimePeriod(delaySeconds, 0));
916 AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
917 Dali::Actor::Property::VISIBLE,
918 Property::INVALID_COMPONENT_INDEX,
919 AnimateToBoolean(SHOW_VALUE),
921 TimePeriod(delaySeconds, 0.0f /*immediate*/)));
924 void Animation::Hide(Actor& actor, float delaySeconds)
926 ExtendDuration(TimePeriod(delaySeconds, 0));
928 AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
929 Dali::Actor::Property::VISIBLE,
930 Property::INVALID_COMPONENT_INDEX,
931 AnimateToBoolean(HIDE_VALUE),
933 TimePeriod(delaySeconds, 0.0f /*immediate*/)));
936 bool Animation::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
939 Animation* animation = dynamic_cast<Animation*>(object);
943 std::string_view name(actionName);
945 if(name == ACTION_PLAY)
947 if(Property::Value* value = attributes.Find("duration", Property::FLOAT))
949 animation->SetDuration(value->Get<float>());
955 else if(name == ACTION_STOP)
960 else if(name == ACTION_PAUSE)
970 void Animation::SetCurrentProgress(float progress)
972 if(mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y)
974 // mAnimation is being used in a separate thread; queue a message to set the current progress
975 SetCurrentProgressMessage(mEventThreadServices, *mAnimation, progress);
979 float Animation::GetCurrentProgress()
981 float progress = 0.f;
982 if(mAnimation) // always exists in practice
984 progress = mAnimation->GetCurrentProgress();
990 void Animation::ExtendDuration(const TimePeriod& timePeriod)
992 float duration = timePeriod.delaySeconds + timePeriod.durationSeconds;
994 if(duration > mDurationSeconds)
996 SetDuration(duration);
1000 void Animation::SetSpeedFactor(float factor)
1004 mSpeedFactor = factor;
1005 SetSpeedFactorMessage(mEventThreadServices, *mAnimation, factor);
1009 float Animation::GetSpeedFactor() const
1011 return mSpeedFactor;
1014 void Animation::SetPlayRange(const Vector2& range)
1016 // Make sure the range specified is between 0.0 and 1.0
1017 if(range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f)
1019 Vector2 orderedRange(range);
1020 // If the range is not in order swap values
1021 if(range.x > range.y)
1023 orderedRange = Vector2(range.y, range.x);
1026 // Cache for public getters
1027 mPlayRange = orderedRange;
1029 // mAnimation is being used in a separate thread; queue a message to set play range
1030 SetPlayRangeMessage(mEventThreadServices, *mAnimation, orderedRange);
1034 Vector2 Animation::GetPlayRange() const
1039 void Animation::SetBlendPoint(float blendPoint)
1041 if(blendPoint >= 0.0f && blendPoint <= 1.0f)
1043 mBlendPoint = blendPoint;
1044 SetBlendPointMessage(mEventThreadServices, *mAnimation, mBlendPoint);
1048 DALI_LOG_ERROR("Blend Point should be a value between 0 and 1.\n");
1052 float Animation::GetBlendPoint() const
1057 void Animation::SetLoopingMode(Dali::Animation::LoopingMode loopingMode)
1059 mAutoReverseEnabled = (loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE);
1061 // mAnimation is being used in a separate thread; queue a message to set play range
1062 SetLoopingModeMessage(mEventThreadServices, *mAnimation, mAutoReverseEnabled);
1065 Dali::Animation::LoopingMode Animation::GetLoopingMode() const
1067 return mAutoReverseEnabled ? Dali::Animation::AUTO_REVERSE : Dali::Animation::RESTART;
1070 bool Animation::CompareConnectorEndTimes(const Animation::ConnectorTargetValues& lhs, const Animation::ConnectorTargetValues& rhs)
1072 return ((lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds) < (rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds));
1075 void Animation::NotifyObjects(Animation::Notify notifyValueType)
1077 // If the animation is discarded, then we do not want to change the target values unless we want to force the current values
1078 if(mEndAction != EndAction::DISCARD || notifyValueType == Notify::FORCE_CURRENT_VALUE)
1080 // Sort according to end time with earlier end times coming first, if the end time is the same, then the connectors are not moved
1081 // Only do this if we're using the target value
1082 if(mConnectorTargetValuesSortRequired && notifyValueType == Notify::USE_TARGET_VALUE)
1084 std::stable_sort(mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes);
1086 // Now mConnectorTargetValues sorted. Reset flag.
1087 mConnectorTargetValuesSortRequired = false;
1090 // Loop through all connector target values sorted by increasing end time
1091 ConnectorTargetValuesContainer::const_iterator iter = mConnectorTargetValues.begin();
1092 const ConnectorTargetValuesContainer::const_iterator endIter = mConnectorTargetValues.end();
1093 for(; iter != endIter; ++iter)
1095 AnimatorConnectorBase* connector = mConnectors[iter->connectorIndex];
1097 Object* object = connector->GetObject();
1098 if(object && object->IsAnimationPossible())
1100 const auto propertyIndex = connector->GetPropertyIndex();
1101 object->NotifyPropertyAnimation(
1104 (notifyValueType == Notify::USE_TARGET_VALUE) ? iter->targetValue : object->GetCurrentProperty(propertyIndex),
1105 (notifyValueType == Notify::USE_TARGET_VALUE) ? iter->animatorType : Animation::TO); // If we're setting the current value, then use TO as we want to set, not adjust, the current value
1111 void Animation::SendFinalProgressNotificationMessage()
1113 if(mProgressReachedMarker > 0.0f)
1115 float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
1116 SetProgressNotificationMessage(mEventThreadServices, *mAnimation, progressMarkerSeconds);
1120 void Animation::AppendConnectorTargetValues(ConnectorTargetValues&& connectorTargetValues)
1122 // Check whether we need to sort mConnectorTargetValues or not.
1123 // Sort will be required only if new item is smaller than last value of container.
1124 if(!mConnectorTargetValuesSortRequired && !mConnectorTargetValues.empty())
1126 if(CompareConnectorEndTimes(connectorTargetValues, mConnectorTargetValues.back()))
1128 mConnectorTargetValuesSortRequired = true;
1132 // Store data to later notify the object that its property is being animated
1133 mConnectorTargetValues.push_back(std::move(connectorTargetValues));
1136 } // namespace Internal