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