2 * Copyright (c) 2021 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/path-impl.h>
28 #include <dali/internal/event/common/notification-manager.h>
29 #include <dali/internal/event/common/property-helper.h>
30 #include <dali/internal/event/common/stage-impl.h>
31 #include <dali/internal/event/common/thread-local-storage.h>
32 #include <dali/internal/update/animation/scene-graph-animator.h>
33 #include <dali/internal/update/manager/update-manager.h>
34 #include <dali/public-api/animation/alpha-function.h>
35 #include <dali/public-api/animation/time-period.h>
36 #include <dali/public-api/common/dali-common.h>
37 #include <dali/public-api/math/radian.h>
38 #include <dali/public-api/math/vector2.h>
39 #include <dali/public-api/object/type-registry.h>
41 using Dali::Internal::SceneGraph::AnimatorBase;
42 using Dali::Internal::SceneGraph::Shader;
43 using Dali::Internal::SceneGraph::UpdateManager;
49 static bool SHOW_VALUE = true;
50 static bool HIDE_VALUE = false;
56 static constexpr std::string_view SIGNAL_FINISHED = "finished";
60 static constexpr std::string_view ACTION_PLAY = "play";
61 static constexpr std::string_view ACTION_STOP = "stop";
62 static constexpr std::string_view ACTION_PAUSE = "pause";
66 return Dali::Animation::New(0.f);
69 TypeRegistration mType(typeid(Dali::Animation), typeid(Dali::BaseHandle), Create);
71 SignalConnectorType signalConnector1(mType, std::string(SIGNAL_FINISHED), &Animation::DoConnectSignal);
73 TypeAction action1(mType, std::string(ACTION_PLAY), &Animation::DoAction);
74 TypeAction action2(mType, std::string(ACTION_STOP), &Animation::DoAction);
75 TypeAction action3(mType, std::string(ACTION_PAUSE), &Animation::DoAction);
77 const Dali::Animation::EndAction DEFAULT_END_ACTION(Dali::Animation::BAKE);
78 const Dali::Animation::EndAction DEFAULT_DISCONNECT_ACTION(Dali::Animation::BAKE_FINAL);
79 const Dali::Animation::Interpolation DEFAULT_INTERPOLATION(Dali::Animation::LINEAR);
80 const Dali::AlphaFunction DEFAULT_ALPHA_FUNCTION(Dali::AlphaFunction::DEFAULT);
83 * Helper to tell if a property is animatable (if we have animators for it)
85 * @param type type to check
86 * @return true if animatable
88 inline bool IsAnimatable(Property::Type type)
90 bool animatable = false;
93 case Property::BOOLEAN:
95 case Property::INTEGER:
96 case Property::VECTOR2:
97 case Property::VECTOR3:
98 case Property::VECTOR4:
99 case Property::ROTATION:
104 case Property::MATRIX: // matrix is allowed as a scene graph property but there's no animators for it
105 case Property::MATRIX3: // matrix3 is allowed as a scene graph property but there's no animators for it
107 case Property::RECTANGLE:
108 case Property::STRING:
109 case Property::ARRAY:
111 case Property::EXTENTS:
120 * Helper to validate animation input values
122 * @param propertyType type of the property that is being animated
123 * @param destinationType type of the target
124 * @param period time period of the animation
126 void ValidateParameters(Property::Type propertyType, Property::Type destinationType, const TimePeriod& period)
128 // destination value has to be animatable
129 DALI_ASSERT_ALWAYS(IsAnimatable(propertyType) && "Property type is not animatable");
130 DALI_ASSERT_ALWAYS(IsAnimatable(destinationType) && "Target value is not animatable");
131 DALI_ASSERT_ALWAYS(propertyType == destinationType && "Property and target types don't match");
132 DALI_ASSERT_ALWAYS(period.durationSeconds >= 0 && "Duration must be >=0");
135 } // anonymous namespace
137 AnimationPtr Animation::New(float durationSeconds)
139 if(durationSeconds < 0.0f)
141 DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
142 durationSeconds = 0.0f;
145 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
146 AnimationPtr animation = new Animation(tls.GetEventThreadServices(), tls.GetAnimationPlaylist(), durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION);
148 // Second-phase construction
149 animation->Initialize();
154 Animation::Animation(EventThreadServices& eventThreadServices, AnimationPlaylist& playlist, float durationSeconds, EndAction endAction, EndAction disconnectAction, AlphaFunction defaultAlpha)
155 : mEventThreadServices(eventThreadServices),
157 mDefaultAlpha(defaultAlpha),
158 mDurationSeconds(durationSeconds),
159 mEndAction(endAction),
160 mDisconnectAction(disconnectAction)
164 void Animation::Initialize()
166 // Connect to the animation playlist
167 mPlaylist.AnimationCreated(*this);
174 Animation::~Animation()
176 // Guard to allow handle destruction after Core has been destroyed
177 if(Stage::IsInstalled())
179 // Disconnect from the animation playlist
180 mPlaylist.AnimationDestroyed(*this);
182 DestroySceneObject();
188 void Animation::CreateSceneObject()
190 DALI_ASSERT_DEBUG(mAnimation == NULL);
192 // Create a new animation, Keep a const pointer to the animation.
193 mAnimation = SceneGraph::Animation::New(mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction);
194 OwnerPointer<SceneGraph::Animation> transferOwnership(const_cast<SceneGraph::Animation*>(mAnimation));
195 AddAnimationMessage(mEventThreadServices.GetUpdateManager(), transferOwnership);
198 void Animation::DestroySceneObject()
200 if(mAnimation != nullptr)
202 // Remove animation using a message to the update manager
203 RemoveAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
204 mAnimation = nullptr;
208 void Animation::SetDuration(float seconds)
212 DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
216 mDurationSeconds = seconds;
218 // mAnimation is being used in a separate thread; queue a message to set the value
219 SetDurationMessage(mEventThreadServices, *mAnimation, seconds);
222 void Animation::SetProgressNotification(float progress)
224 // mAnimation is being used in a separate thread; queue a message to set the value
225 mProgressReachedMarker = progress;
228 float Animation::GetProgressNotification()
230 return mProgressReachedMarker;
233 float Animation::GetDuration() const
235 // This is not animatable; the cached value is up-to-date.
236 return mDurationSeconds;
239 void Animation::SetLooping(bool on)
241 SetLoopCount(on ? 0 : 1);
244 void Animation::SetLoopCount(int32_t count)
246 // Cache for public getters
249 // mAnimation is being used in a separate thread; queue a message to set the value
250 SetLoopingMessage(mEventThreadServices, *mAnimation, mLoopCount);
253 int32_t Animation::GetLoopCount()
258 int32_t Animation::GetCurrentLoop()
263 bool Animation::IsLooping() const
265 return mLoopCount != 1;
268 void Animation::SetEndAction(EndAction action)
270 // Cache for public getters
273 // mAnimation is being used in a separate thread; queue a message to set the value
274 SetEndActionMessage(mEventThreadServices, *mAnimation, action);
277 Dali::Animation::EndAction Animation::GetEndAction() const
279 // This is not animatable; the cached value is up-to-date.
283 void Animation::SetDisconnectAction(EndAction action)
285 // Cache for public getters
286 mDisconnectAction = action;
288 // mAnimation is being used in a separate thread; queue a message to set the value
289 SetDisconnectActionMessage(mEventThreadServices, *mAnimation, action);
292 Dali::Animation::EndAction Animation::GetDisconnectAction() const
294 // This is not animatable; the cached value is up-to-date.
295 return mDisconnectAction;
298 void Animation::Play()
300 // Update the current playlist
301 mPlaylist.OnPlay(*this);
303 mState = Dali::Animation::PLAYING;
305 NotifyObjects(Notify::USE_TARGET_VALUE);
307 SendFinalProgressNotificationMessage();
309 // mAnimation is being used in a separate thread; queue a Play message
310 PlayAnimationMessage(mEventThreadServices, *mAnimation);
313 void Animation::PlayFrom(float progress)
315 if(progress >= mPlayRange.x && progress <= mPlayRange.y)
317 // Update the current playlist
318 mPlaylist.OnPlay(*this);
320 mState = Dali::Animation::PLAYING;
322 NotifyObjects(Notify::USE_TARGET_VALUE);
324 SendFinalProgressNotificationMessage();
326 // mAnimation is being used in a separate thread; queue a Play message
327 PlayAnimationFromMessage(mEventThreadServices, *mAnimation, progress);
331 void Animation::PlayAfter(float delaySeconds)
333 // The negative delay means play immediately.
334 delaySeconds = std::max(0.f, delaySeconds);
336 mDelaySeconds = delaySeconds;
338 // Update the current playlist
339 mPlaylist.OnPlay(*this);
341 mState = Dali::Animation::PLAYING;
343 NotifyObjects(Notify::USE_TARGET_VALUE);
345 SendFinalProgressNotificationMessage();
347 // mAnimation is being used in a separate thread; queue a message to set the value
348 PlayAfterMessage(mEventThreadServices, *mAnimation, delaySeconds);
351 void Animation::Pause()
353 mState = Dali::Animation::PAUSED;
355 // mAnimation is being used in a separate thread; queue a Pause message
356 PauseAnimationMessage(mEventThreadServices, *mAnimation);
358 // Notify the objects with the _paused_, i.e. current values
359 NotifyObjects(Notify::FORCE_CURRENT_VALUE);
362 Dali::Animation::State Animation::GetState() const
367 void Animation::Stop()
369 mState = Dali::Animation::STOPPED;
371 // mAnimation is being used in a separate thread; queue a Stop message
372 StopAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
374 // Only notify the objects with the _stopped_, i.e. current values if the end action is set to BAKE
375 if(mEndAction == EndAction::BAKE)
377 NotifyObjects(Notify::USE_CURRENT_VALUE);
381 void Animation::Clear()
383 DALI_ASSERT_DEBUG(mAnimation);
385 // Only notify the objects with the current values if the end action is set to BAKE
386 if(mEndAction == EndAction::BAKE && mState != Dali::Animation::STOPPED)
388 NotifyObjects(Notify::USE_CURRENT_VALUE);
391 // Remove all the connectors
394 // Reset the connector target values
395 mConnectorTargetValues.clear();
397 // Replace the old scene-object with a new one
398 DestroySceneObject();
401 // Reset the notification count, since the new scene-object has never been played
402 mNotificationCount = 0;
404 // Update the current playlist
405 mPlaylist.OnClear(*this);
408 void Animation::AnimateBy(Property& target, Property::Value relativeValue)
410 AnimateBy(target, std::move(relativeValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
413 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha)
415 AnimateBy(target, std::move(relativeValue), alpha, TimePeriod(mDurationSeconds));
418 void Animation::AnimateBy(Property& target, Property::Value relativeValue, TimePeriod period)
420 AnimateBy(target, std::move(relativeValue), mDefaultAlpha, period);
423 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha, TimePeriod period)
425 Object& object = GetImplementation(target.object);
426 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
427 const Property::Type destinationType = relativeValue.GetType();
429 // validate animation parameters, if component index is set then use float as checked type
430 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
434 ExtendDuration(period);
436 // keep the current count.
437 auto connectorIndex = mConnectors.Count();
439 // using destination type so component animation gets correct type
440 switch(destinationType)
442 case Property::BOOLEAN:
444 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
445 target.propertyIndex,
446 target.componentIndex,
447 AnimateByBoolean(relativeValue.Get<bool>()),
453 case Property::INTEGER:
455 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
456 target.propertyIndex,
457 target.componentIndex,
458 AnimateByInteger(relativeValue.Get<int32_t>()),
464 case Property::FLOAT:
466 AddAnimatorConnector(AnimatorConnector<float>::New(object,
467 target.propertyIndex,
468 target.componentIndex,
469 AnimateByFloat(relativeValue.Get<float>()),
475 case Property::VECTOR2:
477 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
478 target.propertyIndex,
479 target.componentIndex,
480 AnimateByVector2(relativeValue.Get<Vector2>()),
486 case Property::VECTOR3:
488 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
489 target.propertyIndex,
490 target.componentIndex,
491 AnimateByVector3(relativeValue.Get<Vector3>()),
497 case Property::VECTOR4:
499 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
500 target.propertyIndex,
501 target.componentIndex,
502 AnimateByVector4(relativeValue.Get<Vector4>()),
508 case Property::ROTATION:
510 AngleAxis angleAxis = relativeValue.Get<AngleAxis>();
512 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
513 target.propertyIndex,
514 target.componentIndex,
515 RotateByAngleAxis(angleAxis.angle, angleAxis.axis),
523 DALI_ASSERT_DEBUG(false && "Property not supported");
526 // Store data to later notify the object that its property is being animated
527 mConnectorTargetValues.push_back({std::move(relativeValue), period, connectorIndex, Animation::BY});
530 void Animation::AnimateTo(Property& target, Property::Value destinationValue)
532 AnimateTo(target, std::move(destinationValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
535 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha)
537 AnimateTo(target, std::move(destinationValue), alpha, TimePeriod(mDurationSeconds));
540 void Animation::AnimateTo(Property& target, Property::Value destinationValue, TimePeriod period)
542 AnimateTo(target, std::move(destinationValue), mDefaultAlpha, period);
545 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha, TimePeriod period)
547 Object& object = GetImplementation(target.object);
548 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
549 const Property::Type destinationType = destinationValue.GetType();
551 // validate animation parameters, if component index is set then use float as checked type
552 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
556 ExtendDuration(period);
558 // keep the current count.
559 auto connectorIndex = mConnectors.Count();
561 // using destination type so component animation gets correct type
562 switch(destinationType)
564 case Property::BOOLEAN:
566 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
567 target.propertyIndex,
568 target.componentIndex,
569 AnimateToBoolean(destinationValue.Get<bool>()),
575 case Property::INTEGER:
577 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
578 target.propertyIndex,
579 target.componentIndex,
580 AnimateToInteger(destinationValue.Get<int32_t>()),
586 case Property::FLOAT:
588 AddAnimatorConnector(AnimatorConnector<float>::New(object,
589 target.propertyIndex,
590 target.componentIndex,
591 AnimateToFloat(destinationValue.Get<float>()),
597 case Property::VECTOR2:
599 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
600 target.propertyIndex,
601 target.componentIndex,
602 AnimateToVector2(destinationValue.Get<Vector2>()),
608 case Property::VECTOR3:
610 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
611 target.propertyIndex,
612 target.componentIndex,
613 AnimateToVector3(destinationValue.Get<Vector3>()),
619 case Property::VECTOR4:
621 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
622 target.propertyIndex,
623 target.componentIndex,
624 AnimateToVector4(destinationValue.Get<Vector4>()),
630 case Property::ROTATION:
632 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
633 target.propertyIndex,
634 target.componentIndex,
635 RotateToQuaternion(destinationValue.Get<Quaternion>()),
643 DALI_ASSERT_DEBUG(false && "Property not supported");
646 // Store data to later notify the object that its property is being animated
647 mConnectorTargetValues.push_back({std::move(destinationValue), period, connectorIndex, Animation::TO});
650 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames)
652 AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
655 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, Interpolation interpolation)
657 AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation);
660 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period)
662 AnimateBetween(target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION);
665 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation)
667 AnimateBetween(target, keyFrames, mDefaultAlpha, period, interpolation);
670 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha)
672 AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
675 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation)
677 AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation);
680 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period)
682 AnimateBetween(target, keyFrames, alpha, period, DEFAULT_INTERPOLATION);
685 void Animation::AnimateBetween(Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation)
687 Object& object = GetImplementation(target.object);
688 const Property::Type propertyType = object.GetPropertyType(target.propertyIndex);
689 const Property::Type destinationType = keyFrames.GetType();
691 // validate animation parameters, if component index is set then use float as checked type
692 ValidateParameters((target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
696 ExtendDuration(period);
698 // Store data to later notify the object that its property is being animated
699 mConnectorTargetValues.push_back({keyFrames.GetLastKeyFrameValue(), period, mConnectors.Count(), BETWEEN});
701 // using destination type so component animation gets correct type
702 switch(destinationType)
704 case Dali::Property::BOOLEAN:
706 auto kf = GetSpecialization<const KeyFrameBoolean*>(keyFrames);
707 AddAnimatorConnector(AnimatorConnector<bool>::New(object,
708 target.propertyIndex,
709 target.componentIndex,
710 KeyFrameBooleanFunctor(*kf), // takes a copy of the keyframe
716 case Dali::Property::INTEGER:
718 auto kf = GetSpecialization<const KeyFrameInteger*>(keyFrames);
719 AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
720 target.propertyIndex,
721 target.componentIndex,
722 KeyFrameIntegerFunctor(*kf, interpolation), // takes a copy of the keyframe
728 case Dali::Property::FLOAT:
730 auto kf = GetSpecialization<const KeyFrameNumber*>(keyFrames);
731 AddAnimatorConnector(AnimatorConnector<float>::New(object,
732 target.propertyIndex,
733 target.componentIndex,
734 KeyFrameNumberFunctor(*kf, interpolation), // takes a copy of the keyframe
740 case Dali::Property::VECTOR2:
742 auto kf = GetSpecialization<const KeyFrameVector2*>(keyFrames);
743 AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
744 target.propertyIndex,
745 target.componentIndex,
746 KeyFrameVector2Functor(*kf, interpolation), // takes a copy of the keyframe
752 case Dali::Property::VECTOR3:
754 auto kf = GetSpecialization<const KeyFrameVector3*>(keyFrames);
755 AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
756 target.propertyIndex,
757 target.componentIndex,
758 KeyFrameVector3Functor(*kf, interpolation), // takes a copy of the keyframe
764 case Dali::Property::VECTOR4:
766 auto kf = GetSpecialization<const KeyFrameVector4*>(keyFrames);
767 AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
768 target.propertyIndex,
769 target.componentIndex,
770 KeyFrameVector4Functor(*kf, interpolation), // takes a copy of the keyframe
776 case Dali::Property::ROTATION:
778 auto kf = GetSpecialization<const KeyFrameQuaternion*>(keyFrames);
779 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
780 target.propertyIndex,
781 target.componentIndex,
782 KeyFrameQuaternionFunctor(*kf), // takes a copy of the keyframe
790 DALI_ASSERT_DEBUG(false && "Property not supported");
795 bool Animation::HasFinished()
797 bool hasFinished(false);
798 const int32_t playedCount(mAnimation->GetPlayedCount());
800 // If the play count has been incremented, then another notification is required
801 mCurrentLoop = mAnimation->GetCurrentLoop();
803 if(playedCount > mNotificationCount)
805 // Note that only one signal is emitted, if the animation has been played repeatedly
806 mNotificationCount = playedCount;
810 mState = Dali::Animation::STOPPED;
816 Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
818 return mFinishedSignal;
821 Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
823 return mProgressReachedSignal;
826 void Animation::EmitSignalFinish()
828 if(!mFinishedSignal.Empty())
830 Dali::Animation handle(this);
831 mFinishedSignal.Emit(handle);
835 void Animation::EmitSignalProgressReached()
837 if(!mProgressReachedSignal.Empty())
839 Dali::Animation handle(this);
840 mProgressReachedSignal.Emit(handle);
844 bool Animation::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
846 bool connected(false);
847 Animation* animation = static_cast<Animation*>(object); // TypeRegistry guarantees that this is the correct type.
849 if(SIGNAL_FINISHED == signalName)
851 animation->FinishedSignal().Connect(tracker, functor);
858 void Animation::AddAnimatorConnector(AnimatorConnectorBase* connector)
860 DALI_ASSERT_DEBUG(NULL != connector);
862 connector->SetParent(*this);
864 mConnectors.PushBack(connector);
867 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward)
869 Animate(actor, path, forward, mDefaultAlpha, TimePeriod(mDurationSeconds));
872 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha)
874 Animate(actor, path, forward, alpha, TimePeriod(mDurationSeconds));
877 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, TimePeriod period)
879 Animate(actor, path, forward, mDefaultAlpha, period);
882 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha, TimePeriod period)
884 ExtendDuration(period);
886 PathPtr pathCopy = Path::Clone(path);
889 AddAnimatorConnector(AnimatorConnector<Vector3>::New(actor,
890 Dali::Actor::Property::POSITION,
891 Property::INVALID_COMPONENT_INDEX,
892 PathPositionFunctor(pathCopy),
896 //If forward is zero, PathRotationFunctor will always return the unit quaternion
897 if(forward != Vector3::ZERO)
900 AddAnimatorConnector(AnimatorConnector<Quaternion>::New(actor,
901 Dali::Actor::Property::ORIENTATION,
902 Property::INVALID_COMPONENT_INDEX,
903 PathRotationFunctor(pathCopy, forward),
909 void Animation::Show(Actor& actor, float delaySeconds)
911 ExtendDuration(TimePeriod(delaySeconds, 0));
913 AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
914 Dali::Actor::Property::VISIBLE,
915 Property::INVALID_COMPONENT_INDEX,
916 AnimateToBoolean(SHOW_VALUE),
918 TimePeriod(delaySeconds, 0.0f /*immediate*/)));
921 void Animation::Hide(Actor& actor, float delaySeconds)
923 ExtendDuration(TimePeriod(delaySeconds, 0));
925 AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
926 Dali::Actor::Property::VISIBLE,
927 Property::INVALID_COMPONENT_INDEX,
928 AnimateToBoolean(HIDE_VALUE),
930 TimePeriod(delaySeconds, 0.0f /*immediate*/)));
933 bool Animation::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
936 Animation* animation = dynamic_cast<Animation*>(object);
940 std::string_view name(actionName);
942 if(name == ACTION_PLAY)
944 if(Property::Value* value = attributes.Find("duration", Property::FLOAT))
946 animation->SetDuration(value->Get<float>());
952 else if(name == ACTION_STOP)
957 else if(name == ACTION_PAUSE)
967 void Animation::SetCurrentProgress(float progress)
969 if(mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y)
971 // mAnimation is being used in a separate thread; queue a message to set the current progress
972 SetCurrentProgressMessage(mEventThreadServices, *mAnimation, progress);
976 float Animation::GetCurrentProgress()
978 float progress = 0.f;
979 if(mAnimation) // always exists in practice
981 progress = mAnimation->GetCurrentProgress();
987 void Animation::ExtendDuration(const TimePeriod& timePeriod)
989 float duration = timePeriod.delaySeconds + timePeriod.durationSeconds;
991 if(duration > mDurationSeconds)
993 SetDuration(duration);
997 void Animation::SetSpeedFactor(float factor)
1001 mSpeedFactor = factor;
1002 SetSpeedFactorMessage(mEventThreadServices, *mAnimation, factor);
1006 float Animation::GetSpeedFactor() const
1008 return mSpeedFactor;
1011 void Animation::SetPlayRange(const Vector2& range)
1013 //Make sure the range specified is between 0.0 and 1.0
1014 if(range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f)
1016 Vector2 orderedRange(range);
1017 //If the range is not in order swap values
1018 if(range.x > range.y)
1020 orderedRange = Vector2(range.y, range.x);
1023 // Cache for public getters
1024 mPlayRange = orderedRange;
1026 // mAnimation is being used in a separate thread; queue a message to set play range
1027 SetPlayRangeMessage(mEventThreadServices, *mAnimation, orderedRange);
1031 Vector2 Animation::GetPlayRange() const
1036 void Animation::SetLoopingMode(Dali::Animation::LoopingMode loopingMode)
1038 mAutoReverseEnabled = (loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE);
1040 // mAnimation is being used in a separate thread; queue a message to set play range
1041 SetLoopingModeMessage(mEventThreadServices, *mAnimation, mAutoReverseEnabled);
1044 Dali::Animation::LoopingMode Animation::GetLoopingMode() const
1046 return mAutoReverseEnabled ? Dali::Animation::AUTO_REVERSE : Dali::Animation::RESTART;
1049 bool Animation::CompareConnectorEndTimes(const Animation::ConnectorTargetValues& lhs, const Animation::ConnectorTargetValues& rhs)
1051 return ((lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds) < (rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds));
1054 void Animation::NotifyObjects(Animation::Notify notifyValueType)
1056 // If the animation is discarded, then we do not want to change the target values unless we want to force the current values
1057 if(mEndAction != EndAction::DISCARD || notifyValueType == Notify::FORCE_CURRENT_VALUE)
1059 // Sort according to end time with earlier end times coming first, if the end time is the same, then the connectors are not moved
1060 // Only do this if we're using the target value
1061 if(notifyValueType == Notify::USE_TARGET_VALUE)
1063 std::stable_sort(mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes);
1066 // Loop through all connector target values sorted by increasing end time
1067 ConnectorTargetValuesContainer::const_iterator iter = mConnectorTargetValues.begin();
1068 const ConnectorTargetValuesContainer::const_iterator endIter = mConnectorTargetValues.end();
1069 for(; iter != endIter; ++iter)
1071 AnimatorConnectorBase* connector = mConnectors[iter->connectorIndex];
1073 Object* object = connector->GetObject();
1074 if(object && object->IsAnimationPossible())
1076 const auto propertyIndex = connector->GetPropertyIndex();
1077 object->NotifyPropertyAnimation(
1080 (notifyValueType == Notify::USE_TARGET_VALUE) ? iter->targetValue : object->GetCurrentProperty(propertyIndex),
1081 (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
1087 void Animation::SendFinalProgressNotificationMessage()
1089 if(mProgressReachedMarker > 0.0f)
1091 float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
1092 SetProgressNotificationMessage(mEventThreadServices, *mAnimation, progressMarkerSeconds);
1096 } // namespace Internal