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