7ef3323a9a90db4eb2040ddb8f3d93a2bb68fd7c
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / animation-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/animation/animation-impl.h>
20 #include <dali/public-api/object/property-map.h>
21
22 // EXTERNAL INCLUDES
23
24 // INTERNAL INCLUDES
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>
41
42 using Dali::Internal::SceneGraph::AnimatorBase;
43 using Dali::Internal::SceneGraph::Shader;
44 using Dali::Internal::SceneGraph::UpdateManager;
45
46 namespace Dali
47 {
48 namespace Internal
49 {
50 static bool SHOW_VALUE = true;
51 static bool HIDE_VALUE = false;
52
53 namespace
54 {
55 // Signals
56
57 static constexpr std::string_view SIGNAL_FINISHED = "finished";
58
59 // Actions
60
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";
64
65 BaseHandle Create()
66 {
67   return Dali::Animation::New(0.f);
68 }
69
70 TypeRegistration mType(typeid(Dali::Animation), typeid(Dali::BaseHandle), Create);
71
72 SignalConnectorType signalConnector1(mType, std::string(SIGNAL_FINISHED), &Animation::DoConnectSignal);
73
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);
77
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);
82
83 /**
84  * Helper to tell if a property is animatable (if we have animators for it)
85  *
86  * @param type type to check
87  * @return true if animatable
88  */
89 inline bool IsAnimatable(Property::Type type)
90 {
91   bool animatable = false;
92   switch(type)
93   {
94     case Property::BOOLEAN:
95     case Property::FLOAT:
96     case Property::INTEGER:
97     case Property::VECTOR2:
98     case Property::VECTOR3:
99     case Property::VECTOR4:
100     case Property::ROTATION:
101     {
102       animatable = true;
103       break;
104     }
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
107     case Property::NONE:
108     case Property::RECTANGLE:
109     case Property::STRING:
110     case Property::ARRAY:
111     case Property::MAP:
112     case Property::EXTENTS:
113     {
114       break;
115     }
116   }
117   return animatable;
118 }
119
120 /**
121  * Helper to validate animation input values
122  *
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
126  */
127 void ValidateParameters(Property::Type propertyType, Property::Type destinationType, const TimePeriod& period)
128 {
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");
134 }
135
136 } // anonymous namespace
137
138 AnimationPtr Animation::New(float durationSeconds)
139 {
140   if(durationSeconds < 0.0f)
141   {
142     DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
143     durationSeconds = 0.0f;
144   }
145
146   ThreadLocalStorage& tls       = ThreadLocalStorage::Get();
147   AnimationPtr        animation = new Animation(tls.GetEventThreadServices(), tls.GetAnimationPlaylist(), durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION);
148
149   // Second-phase construction
150   animation->Initialize();
151
152   return animation;
153 }
154
155 Animation::Animation(EventThreadServices& eventThreadServices, AnimationPlaylist& playlist, float durationSeconds, EndAction endAction, EndAction disconnectAction, AlphaFunction defaultAlpha)
156 : mEventThreadServices(eventThreadServices),
157   mPlaylist(playlist),
158   mDefaultAlpha(defaultAlpha),
159   mDurationSeconds(durationSeconds),
160   mEndAction(endAction),
161   mDisconnectAction(disconnectAction)
162 {
163 }
164
165 void Animation::Initialize()
166 {
167   // Connect to the animation playlist
168   mPlaylist.AnimationCreated(*this);
169
170   CreateSceneObject();
171
172   RegisterObject();
173 }
174
175 Animation::~Animation()
176 {
177   // Guard to allow handle destruction after Core has been destroyed
178   if(Stage::IsInstalled())
179   {
180     // Disconnect from the animation playlist
181     mPlaylist.AnimationDestroyed(*this);
182
183     DestroySceneObject();
184
185     UnregisterObject();
186   }
187 }
188
189 void Animation::CreateSceneObject()
190 {
191   DALI_ASSERT_DEBUG(mAnimation == NULL);
192
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);
197 }
198
199 void Animation::DestroySceneObject()
200 {
201   if(mAnimation != nullptr)
202   {
203     // Remove animation using a message to the update manager
204     RemoveAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
205     mAnimation = nullptr;
206   }
207 }
208
209 void Animation::SetDuration(float seconds)
210 {
211   if(seconds < 0.0f)
212   {
213     DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
214     seconds = 0.0f;
215   }
216
217   mDurationSeconds = seconds;
218
219   // mAnimation is being used in a separate thread; queue a message to set the value
220   SetDurationMessage(mEventThreadServices, *mAnimation, seconds);
221 }
222
223 void Animation::SetProgressNotification(float progress)
224 {
225   // mAnimation is being used in a separate thread; queue a message to set the value
226   mProgressReachedMarker = progress;
227 }
228
229 float Animation::GetProgressNotification()
230 {
231   return mProgressReachedMarker;
232 }
233
234 float Animation::GetDuration() const
235 {
236   // This is not animatable; the cached value is up-to-date.
237   return mDurationSeconds;
238 }
239
240 void Animation::SetLooping(bool on)
241 {
242   SetLoopCount(on ? 0 : 1);
243 }
244
245 void Animation::SetLoopCount(int32_t count)
246 {
247   // Cache for public getters
248   mLoopCount = count;
249
250   // mAnimation is being used in a separate thread; queue a message to set the value
251   SetLoopingMessage(mEventThreadServices, *mAnimation, mLoopCount);
252 }
253
254 int32_t Animation::GetLoopCount()
255 {
256   return mLoopCount;
257 }
258
259 int32_t Animation::GetCurrentLoop()
260 {
261   return mCurrentLoop;
262 }
263
264 bool Animation::IsLooping() const
265 {
266   return mLoopCount != 1;
267 }
268
269 void Animation::SetEndAction(EndAction action)
270 {
271   // Cache for public getters
272   mEndAction = action;
273
274   // mAnimation is being used in a separate thread; queue a message to set the value
275   SetEndActionMessage(mEventThreadServices, *mAnimation, action);
276 }
277
278 Dali::Animation::EndAction Animation::GetEndAction() const
279 {
280   // This is not animatable; the cached value is up-to-date.
281   return mEndAction;
282 }
283
284 void Animation::SetDisconnectAction(EndAction action)
285 {
286   // Cache for public getters
287   mDisconnectAction = action;
288
289   // mAnimation is being used in a separate thread; queue a message to set the value
290   SetDisconnectActionMessage(mEventThreadServices, *mAnimation, action);
291 }
292
293 Dali::Animation::EndAction Animation::GetDisconnectAction() const
294 {
295   // This is not animatable; the cached value is up-to-date.
296   return mDisconnectAction;
297 }
298
299 void Animation::Play()
300 {
301   // Update the current playlist
302   mPlaylist.OnPlay(*this);
303
304   mState = Dali::Animation::PLAYING;
305
306   NotifyObjects(Notify::USE_TARGET_VALUE);
307
308   SendFinalProgressNotificationMessage();
309
310   // mAnimation is being used in a separate thread; queue a Play message
311   PlayAnimationMessage(mEventThreadServices, *mAnimation);
312 }
313
314 void Animation::PlayFrom(float progress)
315 {
316   if(progress >= mPlayRange.x && progress <= mPlayRange.y)
317   {
318     // Update the current playlist
319     mPlaylist.OnPlay(*this);
320
321     mState = Dali::Animation::PLAYING;
322
323     NotifyObjects(Notify::USE_TARGET_VALUE);
324
325     SendFinalProgressNotificationMessage();
326
327     // mAnimation is being used in a separate thread; queue a Play message
328     PlayAnimationFromMessage(mEventThreadServices, *mAnimation, progress);
329   }
330 }
331
332 void Animation::PlayAfter(float delaySeconds)
333 {
334   // The negative delay means play immediately.
335   delaySeconds = std::max(0.f, delaySeconds);
336
337   mDelaySeconds = delaySeconds;
338
339   // Update the current playlist
340   mPlaylist.OnPlay(*this);
341
342   mState = Dali::Animation::PLAYING;
343
344   NotifyObjects(Notify::USE_TARGET_VALUE);
345
346   SendFinalProgressNotificationMessage();
347
348   // mAnimation is being used in a separate thread; queue a message to set the value
349   PlayAfterMessage(mEventThreadServices, *mAnimation, delaySeconds);
350 }
351
352 void Animation::Pause()
353 {
354   mState = Dali::Animation::PAUSED;
355
356   // mAnimation is being used in a separate thread; queue a Pause message
357   PauseAnimationMessage(mEventThreadServices, *mAnimation);
358
359   // Notify the objects with the _paused_, i.e. current values
360   NotifyObjects(Notify::FORCE_CURRENT_VALUE);
361 }
362
363 Dali::Animation::State Animation::GetState() const
364 {
365   return mState;
366 }
367
368 void Animation::Stop()
369 {
370   mState = Dali::Animation::STOPPED;
371
372   // mAnimation is being used in a separate thread; queue a Stop message
373   StopAnimationMessage(mEventThreadServices.GetUpdateManager(), *mAnimation);
374
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)
377   {
378     NotifyObjects(Notify::USE_CURRENT_VALUE);
379   }
380 }
381
382 void Animation::Clear()
383 {
384   DALI_ASSERT_DEBUG(mAnimation);
385
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)
388   {
389     NotifyObjects(Notify::USE_CURRENT_VALUE);
390   }
391
392   // Remove all the connectors
393   mConnectors.Clear();
394
395   // Reset the connector target values
396   mConnectorTargetValues.clear();
397   mConnectorTargetValuesSortRequired = false;
398
399   // Replace the old scene-object with a new one
400   DestroySceneObject();
401   CreateSceneObject();
402
403   // Reset the notification count, since the new scene-object has never been played
404   mNotificationCount = 0;
405
406   // Update the current playlist
407   mPlaylist.OnClear(*this);
408 }
409
410 void Animation::AnimateBy(Property& target, Property::Value relativeValue)
411 {
412   AnimateBy(target, std::move(relativeValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
413 }
414
415 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha)
416 {
417   AnimateBy(target, std::move(relativeValue), alpha, TimePeriod(mDurationSeconds));
418 }
419
420 void Animation::AnimateBy(Property& target, Property::Value relativeValue, TimePeriod period)
421 {
422   AnimateBy(target, std::move(relativeValue), mDefaultAlpha, period);
423 }
424
425 void Animation::AnimateBy(Property& target, Property::Value relativeValue, AlphaFunction alpha, TimePeriod period)
426 {
427   Object&              object          = GetImplementation(target.object);
428   const Property::Type propertyType    = object.GetPropertyType(target.propertyIndex);
429   const Property::Type destinationType = relativeValue.GetType();
430
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,
433                      destinationType,
434                      period);
435
436   ExtendDuration(period);
437
438   // keep the current count.
439   auto connectorIndex = mConnectors.Count();
440
441   // using destination type so component animation gets correct type
442   switch(destinationType)
443   {
444     case Property::BOOLEAN:
445     {
446       AddAnimatorConnector(AnimatorConnector<bool>::New(object,
447                                                         target.propertyIndex,
448                                                         target.componentIndex,
449                                                         AnimateByBoolean(relativeValue.Get<bool>()),
450                                                         alpha,
451                                                         period));
452       break;
453     }
454
455     case Property::INTEGER:
456     {
457       AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
458                                                            target.propertyIndex,
459                                                            target.componentIndex,
460                                                            AnimateByInteger(relativeValue.Get<int32_t>()),
461                                                            alpha,
462                                                            period));
463       break;
464     }
465
466     case Property::FLOAT:
467     {
468       AddAnimatorConnector(AnimatorConnector<float>::New(object,
469                                                          target.propertyIndex,
470                                                          target.componentIndex,
471                                                          AnimateByFloat(relativeValue.Get<float>()),
472                                                          alpha,
473                                                          period));
474       break;
475     }
476
477     case Property::VECTOR2:
478     {
479       AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
480                                                            target.propertyIndex,
481                                                            target.componentIndex,
482                                                            AnimateByVector2(relativeValue.Get<Vector2>()),
483                                                            alpha,
484                                                            period));
485       break;
486     }
487
488     case Property::VECTOR3:
489     {
490       AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
491                                                            target.propertyIndex,
492                                                            target.componentIndex,
493                                                            AnimateByVector3(relativeValue.Get<Vector3>()),
494                                                            alpha,
495                                                            period));
496       break;
497     }
498
499     case Property::VECTOR4:
500     {
501       AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
502                                                            target.propertyIndex,
503                                                            target.componentIndex,
504                                                            AnimateByVector4(relativeValue.Get<Vector4>()),
505                                                            alpha,
506                                                            period));
507       break;
508     }
509
510     case Property::ROTATION:
511     {
512       AngleAxis angleAxis = relativeValue.Get<AngleAxis>();
513
514       AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
515                                                               target.propertyIndex,
516                                                               target.componentIndex,
517                                                               RotateByAngleAxis(angleAxis.angle, angleAxis.axis),
518                                                               alpha,
519                                                               period));
520       break;
521     }
522
523     default:
524     {
525       DALI_ASSERT_DEBUG(false && "Property  not supported");
526     }
527   }
528
529   AppendConnectorTargetValues({std::move(relativeValue), period, connectorIndex, Animation::BY});
530 }
531
532 void Animation::AnimateTo(Property& target, Property::Value destinationValue)
533 {
534   AnimateTo(target, std::move(destinationValue), mDefaultAlpha, TimePeriod(mDurationSeconds));
535 }
536
537 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha)
538 {
539   AnimateTo(target, std::move(destinationValue), alpha, TimePeriod(mDurationSeconds));
540 }
541
542 void Animation::AnimateTo(Property& target, Property::Value destinationValue, TimePeriod period)
543 {
544   AnimateTo(target, std::move(destinationValue), mDefaultAlpha, period);
545 }
546
547 void Animation::AnimateTo(Property& target, Property::Value destinationValue, AlphaFunction alpha, TimePeriod period)
548 {
549   Object&              object          = GetImplementation(target.object);
550   const Property::Type propertyType    = object.GetPropertyType(target.propertyIndex);
551   const Property::Type destinationType = destinationValue.GetType();
552
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,
555                      destinationType,
556                      period);
557
558   ExtendDuration(period);
559
560   // keep the current count.
561   auto connectorIndex = mConnectors.Count();
562
563   // using destination type so component animation gets correct type
564   switch(destinationType)
565   {
566     case Property::BOOLEAN:
567     {
568       AddAnimatorConnector(AnimatorConnector<bool>::New(object,
569                                                         target.propertyIndex,
570                                                         target.componentIndex,
571                                                         AnimateToBoolean(destinationValue.Get<bool>()),
572                                                         alpha,
573                                                         period));
574       break;
575     }
576
577     case Property::INTEGER:
578     {
579       AddAnimatorConnector(AnimatorConnector<int32_t>::New(object,
580                                                            target.propertyIndex,
581                                                            target.componentIndex,
582                                                            AnimateToInteger(destinationValue.Get<int32_t>()),
583                                                            alpha,
584                                                            period));
585       break;
586     }
587
588     case Property::FLOAT:
589     {
590       AddAnimatorConnector(AnimatorConnector<float>::New(object,
591                                                          target.propertyIndex,
592                                                          target.componentIndex,
593                                                          AnimateToFloat(destinationValue.Get<float>()),
594                                                          alpha,
595                                                          period));
596       break;
597     }
598
599     case Property::VECTOR2:
600     {
601       AddAnimatorConnector(AnimatorConnector<Vector2>::New(object,
602                                                            target.propertyIndex,
603                                                            target.componentIndex,
604                                                            AnimateToVector2(destinationValue.Get<Vector2>()),
605                                                            alpha,
606                                                            period));
607       break;
608     }
609
610     case Property::VECTOR3:
611     {
612       AddAnimatorConnector(AnimatorConnector<Vector3>::New(object,
613                                                            target.propertyIndex,
614                                                            target.componentIndex,
615                                                            AnimateToVector3(destinationValue.Get<Vector3>()),
616                                                            alpha,
617                                                            period));
618       break;
619     }
620
621     case Property::VECTOR4:
622     {
623       AddAnimatorConnector(AnimatorConnector<Vector4>::New(object,
624                                                            target.propertyIndex,
625                                                            target.componentIndex,
626                                                            AnimateToVector4(destinationValue.Get<Vector4>()),
627                                                            alpha,
628                                                            period));
629       break;
630     }
631
632     case Property::ROTATION:
633     {
634       AddAnimatorConnector(AnimatorConnector<Quaternion>::New(object,
635                                                               target.propertyIndex,
636                                                               target.componentIndex,
637                                                               RotateToQuaternion(destinationValue.Get<Quaternion>()),
638                                                               alpha,
639                                                               period));
640       break;
641     }
642
643     default:
644     {
645       DALI_ASSERT_DEBUG(false && "Property  not supported");
646     }
647   }
648
649   AppendConnectorTargetValues({std::move(destinationValue), period, connectorIndex, Animation::TO});
650 }
651
652 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames)
653 {
654   AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
655 }
656
657 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, Interpolation interpolation)
658 {
659   AnimateBetween(target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation);
660 }
661
662 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, TimePeriod period)
663 {
664   AnimateBetween(target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION);
665 }
666
667 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, TimePeriod period, Interpolation interpolation)
668 {
669   AnimateBetween(target, keyFrames, mDefaultAlpha, period, interpolation);
670 }
671
672 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha)
673 {
674   AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION);
675 }
676
677 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, Interpolation interpolation)
678 {
679   AnimateBetween(target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation);
680 }
681
682 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, TimePeriod period)
683 {
684   AnimateBetween(target, keyFrames, alpha, period, DEFAULT_INTERPOLATION);
685 }
686
687 void Animation::AnimateBetween(Property target, Dali::KeyFrames keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation)
688 {
689   Object&          object        = GetImplementation(target.object);
690   const KeyFrames& keyFramesImpl = GetImplementation(keyFrames);
691
692   const Property::Type propertyType    = object.GetPropertyType(target.propertyIndex);
693   const Property::Type destinationType = keyFramesImpl.GetType();
694
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,
697                      destinationType,
698                      period);
699
700   ExtendDuration(period);
701
702   AppendConnectorTargetValues({keyFramesImpl.GetLastKeyFrameValue(), period, mConnectors.Count(), BETWEEN});
703
704   // using destination type so component animation gets correct type
705   switch(destinationType)
706   {
707     case Dali::Property::BOOLEAN:
708     {
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
714                                                         alpha,
715                                                         period));
716       break;
717     }
718
719     case Dali::Property::INTEGER:
720     {
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
726                                                            alpha,
727                                                            period));
728       break;
729     }
730
731     case Dali::Property::FLOAT:
732     {
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
738                                                          alpha,
739                                                          period));
740       break;
741     }
742
743     case Dali::Property::VECTOR2:
744     {
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
750                                                            alpha,
751                                                            period));
752       break;
753     }
754
755     case Dali::Property::VECTOR3:
756     {
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
762                                                            alpha,
763                                                            period));
764       break;
765     }
766
767     case Dali::Property::VECTOR4:
768     {
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
774                                                            alpha,
775                                                            period));
776       break;
777     }
778
779     case Dali::Property::ROTATION:
780     {
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
786                                                               alpha,
787                                                               period));
788       break;
789     }
790
791     default:
792     {
793       DALI_ASSERT_DEBUG(false && "Property  not supported");
794     }
795   }
796 }
797
798 bool Animation::HasFinished()
799 {
800   bool          hasFinished(false);
801   const int32_t playedCount(mAnimation->GetPlayedCount());
802
803   // If the play count has been incremented, then another notification is required
804   mCurrentLoop = mAnimation->GetCurrentLoop();
805
806   if(playedCount > mNotificationCount)
807   {
808     // Note that only one signal is emitted, if the animation has been played repeatedly
809     mNotificationCount = playedCount;
810
811     hasFinished = true;
812
813     mState = Dali::Animation::STOPPED;
814   }
815
816   return hasFinished;
817 }
818
819 Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
820 {
821   return mFinishedSignal;
822 }
823
824 Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
825 {
826   return mProgressReachedSignal;
827 }
828
829 void Animation::EmitSignalFinish()
830 {
831   if(!mFinishedSignal.Empty())
832   {
833     Dali::Animation handle(this);
834     mFinishedSignal.Emit(handle);
835   }
836 }
837
838 void Animation::EmitSignalProgressReached()
839 {
840   if(!mProgressReachedSignal.Empty())
841   {
842     Dali::Animation handle(this);
843     mProgressReachedSignal.Emit(handle);
844   }
845 }
846
847 bool Animation::DoConnectSignal(BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor)
848 {
849   bool       connected(false);
850   Animation* animation = static_cast<Animation*>(object); // TypeRegistry guarantees that this is the correct type.
851
852   if(SIGNAL_FINISHED == signalName)
853   {
854     animation->FinishedSignal().Connect(tracker, functor);
855     connected = true;
856   }
857
858   return connected;
859 }
860
861 void Animation::AddAnimatorConnector(AnimatorConnectorBase* connector)
862 {
863   DALI_ASSERT_DEBUG(NULL != connector);
864
865   connector->SetParent(*this);
866
867   mConnectors.PushBack(connector);
868 }
869
870 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward)
871 {
872   Animate(actor, path, forward, mDefaultAlpha, TimePeriod(mDurationSeconds));
873 }
874
875 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha)
876 {
877   Animate(actor, path, forward, alpha, TimePeriod(mDurationSeconds));
878 }
879
880 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, TimePeriod period)
881 {
882   Animate(actor, path, forward, mDefaultAlpha, period);
883 }
884
885 void Animation::Animate(Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha, TimePeriod period)
886 {
887   ExtendDuration(period);
888
889   PathPtr pathCopy = Path::Clone(path);
890
891   // Position animation
892   AddAnimatorConnector(AnimatorConnector<Vector3>::New(actor,
893                                                        Dali::Actor::Property::POSITION,
894                                                        Property::INVALID_COMPONENT_INDEX,
895                                                        PathPositionFunctor(pathCopy),
896                                                        alpha,
897                                                        period));
898
899   // If forward is zero, PathRotationFunctor will always return the unit quaternion
900   if(forward != Vector3::ZERO)
901   {
902     // Rotation animation
903     AddAnimatorConnector(AnimatorConnector<Quaternion>::New(actor,
904                                                             Dali::Actor::Property::ORIENTATION,
905                                                             Property::INVALID_COMPONENT_INDEX,
906                                                             PathRotationFunctor(pathCopy, forward),
907                                                             alpha,
908                                                             period));
909   }
910 }
911
912 void Animation::Show(Actor& actor, float delaySeconds)
913 {
914   ExtendDuration(TimePeriod(delaySeconds, 0));
915
916   AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
917                                                     Dali::Actor::Property::VISIBLE,
918                                                     Property::INVALID_COMPONENT_INDEX,
919                                                     AnimateToBoolean(SHOW_VALUE),
920                                                     mDefaultAlpha,
921                                                     TimePeriod(delaySeconds, 0.0f /*immediate*/)));
922 }
923
924 void Animation::Hide(Actor& actor, float delaySeconds)
925 {
926   ExtendDuration(TimePeriod(delaySeconds, 0));
927
928   AddAnimatorConnector(AnimatorConnector<bool>::New(actor,
929                                                     Dali::Actor::Property::VISIBLE,
930                                                     Property::INVALID_COMPONENT_INDEX,
931                                                     AnimateToBoolean(HIDE_VALUE),
932                                                     mDefaultAlpha,
933                                                     TimePeriod(delaySeconds, 0.0f /*immediate*/)));
934 }
935
936 bool Animation::DoAction(BaseObject* object, const std::string& actionName, const Property::Map& attributes)
937 {
938   bool       done      = false;
939   Animation* animation = dynamic_cast<Animation*>(object);
940
941   if(animation)
942   {
943     std::string_view name(actionName);
944
945     if(name == ACTION_PLAY)
946     {
947       if(Property::Value* value = attributes.Find("duration", Property::FLOAT))
948       {
949         animation->SetDuration(value->Get<float>());
950       }
951
952       animation->Play();
953       done = true;
954     }
955     else if(name == ACTION_STOP)
956     {
957       animation->Stop();
958       done = true;
959     }
960     else if(name == ACTION_PAUSE)
961     {
962       animation->Pause();
963       done = true;
964     }
965   }
966
967   return done;
968 }
969
970 void Animation::SetCurrentProgress(float progress)
971 {
972   if(mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y)
973   {
974     // mAnimation is being used in a separate thread; queue a message to set the current progress
975     SetCurrentProgressMessage(mEventThreadServices, *mAnimation, progress);
976   }
977 }
978
979 float Animation::GetCurrentProgress()
980 {
981   float progress = 0.f;
982   if(mAnimation) // always exists in practice
983   {
984     progress = mAnimation->GetCurrentProgress();
985   }
986
987   return progress;
988 }
989
990 void Animation::ExtendDuration(const TimePeriod& timePeriod)
991 {
992   float duration = timePeriod.delaySeconds + timePeriod.durationSeconds;
993
994   if(duration > mDurationSeconds)
995   {
996     SetDuration(duration);
997   }
998 }
999
1000 void Animation::SetSpeedFactor(float factor)
1001 {
1002   if(mAnimation)
1003   {
1004     mSpeedFactor = factor;
1005     SetSpeedFactorMessage(mEventThreadServices, *mAnimation, factor);
1006   }
1007 }
1008
1009 float Animation::GetSpeedFactor() const
1010 {
1011   return mSpeedFactor;
1012 }
1013
1014 void Animation::SetPlayRange(const Vector2& range)
1015 {
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)
1018   {
1019     Vector2 orderedRange(range);
1020     // If the range is not in order swap values
1021     if(range.x > range.y)
1022     {
1023       orderedRange = Vector2(range.y, range.x);
1024     }
1025
1026     // Cache for public getters
1027     mPlayRange = orderedRange;
1028
1029     // mAnimation is being used in a separate thread; queue a message to set play range
1030     SetPlayRangeMessage(mEventThreadServices, *mAnimation, orderedRange);
1031   }
1032 }
1033
1034 Vector2 Animation::GetPlayRange() const
1035 {
1036   return mPlayRange;
1037 }
1038
1039 void Animation::SetBlendPoint(float blendPoint)
1040 {
1041   if(blendPoint >= 0.0f && blendPoint <= 1.0f)
1042   {
1043     mBlendPoint = blendPoint;
1044     SetBlendPointMessage(mEventThreadServices, *mAnimation, mBlendPoint);
1045   }
1046   else
1047   {
1048     DALI_LOG_ERROR("Blend Point should be a value between 0 and 1.\n");
1049   }
1050 }
1051
1052 float Animation::GetBlendPoint() const
1053 {
1054   return mBlendPoint;
1055 }
1056
1057 void Animation::SetLoopingMode(Dali::Animation::LoopingMode loopingMode)
1058 {
1059   mAutoReverseEnabled = (loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE);
1060
1061   // mAnimation is being used in a separate thread; queue a message to set play range
1062   SetLoopingModeMessage(mEventThreadServices, *mAnimation, mAutoReverseEnabled);
1063 }
1064
1065 Dali::Animation::LoopingMode Animation::GetLoopingMode() const
1066 {
1067   return mAutoReverseEnabled ? Dali::Animation::AUTO_REVERSE : Dali::Animation::RESTART;
1068 }
1069
1070 bool Animation::CompareConnectorEndTimes(const Animation::ConnectorTargetValues& lhs, const Animation::ConnectorTargetValues& rhs)
1071 {
1072   return ((lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds) < (rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds));
1073 }
1074
1075 void Animation::NotifyObjects(Animation::Notify notifyValueType)
1076 {
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)
1079   {
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)
1083     {
1084       std::stable_sort(mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes);
1085
1086       // Now mConnectorTargetValues sorted. Reset flag.
1087       mConnectorTargetValuesSortRequired = false;
1088     }
1089
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)
1094     {
1095       AnimatorConnectorBase* connector = mConnectors[iter->connectorIndex];
1096
1097       Object* object = connector->GetObject();
1098       if(object && object->IsAnimationPossible())
1099       {
1100         const auto propertyIndex = connector->GetPropertyIndex();
1101         object->NotifyPropertyAnimation(
1102           *this,
1103           propertyIndex,
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
1106       }
1107     }
1108   }
1109 }
1110
1111 void Animation::SendFinalProgressNotificationMessage()
1112 {
1113   if(mProgressReachedMarker > 0.0f)
1114   {
1115     float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
1116     SetProgressNotificationMessage(mEventThreadServices, *mAnimation, progressMarkerSeconds);
1117   }
1118 }
1119
1120 void Animation::AppendConnectorTargetValues(ConnectorTargetValues&& connectorTargetValues)
1121 {
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())
1125   {
1126     if(CompareConnectorEndTimes(connectorTargetValues, mConnectorTargetValues.back()))
1127     {
1128       mConnectorTargetValuesSortRequired = true;
1129     }
1130   }
1131
1132   // Store data to later notify the object that its property is being animated
1133   mConnectorTargetValues.push_back(std::move(connectorTargetValues));
1134 }
1135
1136 } // namespace Internal
1137
1138 } // namespace Dali