Harmonize Animation API parameter checking and add test cases for them
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / animation-impl.cpp
1 /*
2  * Copyright (c) 2018 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::BakeFinal );
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   Stage* stage = Stage::GetCurrent();
144
145   if( stage )
146   {
147     AnimationPlaylist& playlist = stage->GetAnimationPlaylist();
148
149     if( durationSeconds < 0.0f )
150     {
151       DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
152       durationSeconds = 0.0f;
153     }
154
155     AnimationPtr animation = new Animation( *stage, playlist, durationSeconds, DEFAULT_END_ACTION, DEFAULT_DISCONNECT_ACTION, DEFAULT_ALPHA_FUNCTION );
156
157     // Second-phase construction
158     animation->Initialize();
159
160     return animation;
161   }
162   else
163   {
164     return NULL;
165   }
166 }
167
168 Animation::Animation( EventThreadServices& eventThreadServices, AnimationPlaylist& playlist, float durationSeconds, EndAction endAction, EndAction disconnectAction, AlphaFunction defaultAlpha )
169 : mAnimation( NULL ),
170   mEventThreadServices( eventThreadServices ),
171   mPlaylist( playlist ),
172   mFinishedSignal(),
173   mConnectors(),
174   mConnectorTargetValues(),
175   mPlayRange( Vector2(0.0f,1.0f)),
176   mDurationSeconds( durationSeconds ),
177   mSpeedFactor(1.0f),
178   mNotificationCount( 0 ),
179   mLoopCount(1),
180   mCurrentLoop(0),
181   mEndAction( endAction ),
182   mDisconnectAction( disconnectAction ),
183   mDefaultAlpha( defaultAlpha ),
184   mState(Dali::Animation::STOPPED),
185   mProgressReachedMarker( 0.0f ),
186   mDelaySeconds( 0.0f ),
187   mAutoReverseEnabled( false )
188 {
189 }
190
191 void Animation::Initialize()
192 {
193   // Connect to the animation playlist
194   mPlaylist.AnimationCreated( *this );
195
196   CreateSceneObject();
197
198   RegisterObject();
199 }
200
201 Animation::~Animation()
202 {
203   // Guard to allow handle destruction after Core has been destroyed
204   if ( Stage::IsInstalled() )
205   {
206     // Disconnect from the animation playlist
207     mPlaylist.AnimationDestroyed( *this );
208
209     DestroySceneObject();
210
211     UnregisterObject();
212   }
213 }
214
215 void Animation::CreateSceneObject()
216 {
217   DALI_ASSERT_DEBUG( mAnimation == NULL );
218
219   // Create a new animation, Keep a const pointer to the animation.
220   mAnimation = SceneGraph::Animation::New( mDurationSeconds, mSpeedFactor, mPlayRange, mLoopCount, mEndAction, mDisconnectAction );
221   OwnerPointer< SceneGraph::Animation > transferOwnership( const_cast< SceneGraph::Animation* >( mAnimation ) );
222   AddAnimationMessage( mEventThreadServices.GetUpdateManager(), transferOwnership );
223 }
224
225 void Animation::DestroySceneObject()
226 {
227   if ( mAnimation != NULL )
228   {
229     // Remove animation using a message to the update manager
230     RemoveAnimationMessage( mEventThreadServices.GetUpdateManager(), *mAnimation );
231     mAnimation = NULL;
232   }
233 }
234
235 void Animation::SetDuration(float seconds)
236 {
237   if( seconds < 0.0f )
238   {
239     DALI_LOG_WARNING("duration should be greater than 0.0f.\n");
240     seconds = 0.0f;
241   }
242
243   mDurationSeconds = seconds;
244
245   // mAnimation is being used in a separate thread; queue a message to set the value
246   SetDurationMessage( mEventThreadServices, *mAnimation, seconds );
247 }
248
249 void Animation::SetProgressNotification( float progress )
250 {
251   // mAnimation is being used in a separate thread; queue a message to set the value
252   mProgressReachedMarker = progress;
253 }
254
255 float Animation::GetProgressNotification()
256 {
257   return mProgressReachedMarker;
258 }
259
260 float Animation::GetDuration() const
261 {
262   // This is not animatable; the cached value is up-to-date.
263   return mDurationSeconds;
264 }
265
266 void Animation::SetLooping(bool on)
267 {
268   SetLoopCount( on ? 0 : 1 );
269 }
270
271 void Animation::SetLoopCount(int32_t count)
272 {
273   // Cache for public getters
274   mLoopCount = count;
275
276   // mAnimation is being used in a separate thread; queue a message to set the value
277   SetLoopingMessage( mEventThreadServices, *mAnimation, mLoopCount );
278 }
279
280 int32_t Animation::GetLoopCount()
281 {
282   return mLoopCount;
283 }
284
285 int32_t Animation::GetCurrentLoop()
286 {
287   return mCurrentLoop;
288 }
289
290 bool Animation::IsLooping() const
291 {
292   return mLoopCount != 1;
293 }
294
295 void Animation::SetEndAction(EndAction action)
296 {
297   // Cache for public getters
298   mEndAction = action;
299
300   // mAnimation is being used in a separate thread; queue a message to set the value
301   SetEndActionMessage( mEventThreadServices, *mAnimation, action );
302 }
303
304 Dali::Animation::EndAction Animation::GetEndAction() const
305 {
306   // This is not animatable; the cached value is up-to-date.
307   return mEndAction;
308 }
309
310 void Animation::SetDisconnectAction(EndAction action)
311 {
312   // Cache for public getters
313   mDisconnectAction = action;
314
315   // mAnimation is being used in a separate thread; queue a message to set the value
316   SetDisconnectActionMessage( mEventThreadServices, *mAnimation, action );
317 }
318
319 Dali::Animation::EndAction Animation::GetDisconnectAction() const
320 {
321   // This is not animatable; the cached value is up-to-date.
322   return mDisconnectAction;
323 }
324
325 void Animation::Play()
326 {
327   // Update the current playlist
328   mPlaylist.OnPlay( *this );
329
330   mState = Dali::Animation::PLAYING;
331
332   NotifyObjects();
333
334   SendFinalProgressNotificationMessage();
335
336   // mAnimation is being used in a separate thread; queue a Play message
337   PlayAnimationMessage( mEventThreadServices, *mAnimation );
338 }
339
340 void Animation::PlayFrom( float progress )
341 {
342   if( progress >= mPlayRange.x && progress <= mPlayRange.y )
343   {
344     // Update the current playlist
345     mPlaylist.OnPlay( *this );
346
347     mState = Dali::Animation::PLAYING;
348
349     NotifyObjects();
350
351     SendFinalProgressNotificationMessage();
352
353     // mAnimation is being used in a separate thread; queue a Play message
354     PlayAnimationFromMessage( mEventThreadServices, *mAnimation, progress );
355   }
356 }
357
358 void Animation::PlayAfter( float delaySeconds )
359 {
360   // The negative delay means play immediately.
361   delaySeconds = std::max( 0.f, delaySeconds );
362
363   mDelaySeconds = delaySeconds;
364
365   // Update the current playlist
366   mPlaylist.OnPlay( *this );
367
368   mState = Dali::Animation::PLAYING;
369
370   NotifyObjects();
371
372   SendFinalProgressNotificationMessage();
373
374   // mAnimation is being used in a separate thread; queue a message to set the value
375   PlayAfterMessage( mEventThreadServices, *mAnimation, delaySeconds );
376 }
377
378 void Animation::Pause()
379 {
380   mState = Dali::Animation::PAUSED;
381
382   // mAnimation is being used in a separate thread; queue a Pause message
383   PauseAnimationMessage( mEventThreadServices, *mAnimation );
384 }
385
386 Dali::Animation::State Animation::GetState() const
387 {
388   return mState;
389 }
390
391 void Animation::Stop()
392 {
393   mState = Dali::Animation::STOPPED;
394
395   // mAnimation is being used in a separate thread; queue a Stop message
396   StopAnimationMessage( mEventThreadServices.GetUpdateManager(), *mAnimation );
397 }
398
399 void Animation::Clear()
400 {
401   DALI_ASSERT_DEBUG(mAnimation);
402
403   // Remove all the connectors
404   mConnectors.Clear();
405
406   // Reset the connector target values
407   mConnectorTargetValues.clear();
408
409   // Replace the old scene-object with a new one
410   DestroySceneObject();
411   CreateSceneObject();
412
413   // Reset the notification count, since the new scene-object has never been played
414   mNotificationCount = 0;
415
416   // Update the current playlist
417   mPlaylist.OnClear( *this );
418 }
419
420 void Animation::AnimateBy( Property& target, Property::Value& relativeValue )
421 {
422   AnimateBy( target, relativeValue, mDefaultAlpha, TimePeriod(mDurationSeconds) );
423 }
424
425 void Animation::AnimateBy( Property& target, Property::Value& relativeValue, AlphaFunction alpha )
426 {
427   AnimateBy( target, relativeValue, alpha, TimePeriod(mDurationSeconds) );
428 }
429
430 void Animation::AnimateBy( Property& target, Property::Value& relativeValue, TimePeriod period )
431 {
432   AnimateBy( target, relativeValue, mDefaultAlpha, period );
433 }
434
435 void Animation::AnimateBy( Property& target, Property::Value& relativeValue, AlphaFunction alpha, TimePeriod period )
436 {
437   Object& object = GetImplementation(target.object);
438   const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
439   const Property::Type destinationType = relativeValue.GetType();
440
441   // validate animation parameters, if component index is set then use float as checked type
442   ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
443                       destinationType, period );
444
445   ExtendDuration(period);
446
447   // Store data to later notify the object that its property is being animated
448   ConnectorTargetValues connectorPair;
449   connectorPair.targetValue = relativeValue;
450   connectorPair.connectorIndex = mConnectors.Count();
451   connectorPair.timePeriod = period;
452   connectorPair.animatorType = Animation::BY;
453   mConnectorTargetValues.push_back( connectorPair );
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       // non animatable types handled already
540     }
541   }
542 }
543
544 void Animation::AnimateTo( Property& target, Property::Value& destinationValue )
545 {
546   AnimateTo( target, destinationValue, mDefaultAlpha, TimePeriod(mDurationSeconds) );
547 }
548
549 void Animation::AnimateTo( Property& target, Property::Value& destinationValue, AlphaFunction alpha )
550 {
551   AnimateTo( target, destinationValue, alpha, TimePeriod(mDurationSeconds));
552 }
553
554 void Animation::AnimateTo( Property& target, Property::Value& destinationValue, TimePeriod period )
555 {
556   AnimateTo( target, destinationValue, mDefaultAlpha, period );
557 }
558
559 void Animation::AnimateTo( Property& target, Property::Value& destinationValue, AlphaFunction alpha, TimePeriod period )
560 {
561   Object& object = GetImplementation( target.object );
562   const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
563   const Property::Type destinationType = destinationValue.GetType();
564
565   // validate animation parameters, if component index is set then use float as checked type
566   ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
567                       destinationType, period );
568
569   ExtendDuration( period );
570
571   // Store data to later notify the object that its property is being animated
572   ConnectorTargetValues connectorPair;
573   connectorPair.targetValue = destinationValue;
574   connectorPair.connectorIndex = mConnectors.Count();
575   connectorPair.timePeriod = period;
576   connectorPair.animatorType = Animation::TO;
577   mConnectorTargetValues.push_back( connectorPair );
578
579   // using destination type so component animation gets correct type
580   switch ( destinationType )
581   {
582     case Property::BOOLEAN:
583     {
584       AddAnimatorConnector( AnimatorConnector<bool>::New( object,
585                                                           target.propertyIndex,
586                                                           target.componentIndex,
587                                                           new AnimateToBoolean( destinationValue.Get<bool>() ),
588                                                           alpha,
589                                                           period ) );
590       break;
591     }
592
593     case Property::INTEGER:
594     {
595       AddAnimatorConnector( AnimatorConnector<int32_t>::New( object,
596                                                              target.propertyIndex,
597                                                              target.componentIndex,
598                                                              new AnimateToInteger( destinationValue.Get<int32_t>() ),
599                                                              alpha,
600                                                              period ) );
601       break;
602     }
603
604     case Property::FLOAT:
605     {
606       AddAnimatorConnector( AnimatorConnector<float>::New( object,
607                                                            target.propertyIndex,
608                                                            target.componentIndex,
609                                                            new AnimateToFloat( destinationValue.Get<float>() ),
610                                                            alpha,
611                                                            period ) );
612       break;
613     }
614
615     case Property::VECTOR2:
616     {
617       AddAnimatorConnector( AnimatorConnector<Vector2>::New( object,
618                                                              target.propertyIndex,
619                                                              target.componentIndex,
620                                                              new AnimateToVector2( destinationValue.Get<Vector2>() ),
621                                                              alpha,
622                                                              period ) );
623       break;
624     }
625
626     case Property::VECTOR3:
627     {
628       AddAnimatorConnector( AnimatorConnector<Vector3>::New( object,
629                                                              target.propertyIndex,
630                                                              target.componentIndex,
631                                                              new AnimateToVector3( destinationValue.Get<Vector3>() ),
632                                                              alpha,
633                                                              period ) );
634       break;
635     }
636
637     case Property::VECTOR4:
638     {
639       AddAnimatorConnector( AnimatorConnector<Vector4>::New( object,
640                                                              target.propertyIndex,
641                                                              target.componentIndex,
642                                                              new AnimateToVector4( destinationValue.Get<Vector4>() ),
643                                                              alpha,
644                                                              period ) );
645       break;
646     }
647
648     case Property::ROTATION:
649     {
650       AddAnimatorConnector( AnimatorConnector<Quaternion>::New( object,
651                                                                 target.propertyIndex,
652                                                                 target.componentIndex,
653                                                                 new RotateToQuaternion( destinationValue.Get<Quaternion>() ),
654                                                                 alpha,
655                                                                 period ) );
656       break;
657     }
658
659     default:
660     {
661       // non animatable types handled already
662     }
663   }
664 }
665
666 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames )
667 {
668   AnimateBetween( target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION );
669 }
670
671 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, Interpolation interpolation )
672 {
673   AnimateBetween( target, keyFrames, mDefaultAlpha, TimePeriod(mDurationSeconds), interpolation );
674 }
675
676 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, TimePeriod period )
677 {
678   AnimateBetween( target, keyFrames, mDefaultAlpha, period, DEFAULT_INTERPOLATION );
679 }
680
681 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, TimePeriod period, Interpolation interpolation )
682 {
683   AnimateBetween( target, keyFrames, mDefaultAlpha, period, interpolation );
684 }
685
686 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha )
687 {
688   AnimateBetween( target, keyFrames, alpha, TimePeriod(mDurationSeconds), DEFAULT_INTERPOLATION );
689 }
690
691 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, Interpolation interpolation )
692 {
693   AnimateBetween( target, keyFrames, alpha, TimePeriod(mDurationSeconds), interpolation );
694 }
695
696 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period )
697 {
698   AnimateBetween( target, keyFrames, alpha, period, DEFAULT_INTERPOLATION );
699 }
700
701 void Animation::AnimateBetween( Property target, const KeyFrames& keyFrames, AlphaFunction alpha, TimePeriod period, Interpolation interpolation )
702 {
703   Object& object = GetImplementation( target.object );
704   const Property::Type propertyType = object.GetPropertyType( target.propertyIndex );
705   const Property::Type destinationType = keyFrames.GetType();
706
707   // validate animation parameters, if component index is set then use float as checked type
708   ValidateParameters( (target.componentIndex == Property::INVALID_COMPONENT_INDEX) ? propertyType : Property::FLOAT,
709                       destinationType, period );
710
711   ExtendDuration( period );
712
713   // Store data to later notify the object that its property is being animated
714   ConnectorTargetValues connectorPair;
715   connectorPair.targetValue = keyFrames.GetLastKeyFrameValue();
716   connectorPair.connectorIndex = mConnectors.Count();
717   connectorPair.timePeriod = period;
718   connectorPair.animatorType = BETWEEN;
719   mConnectorTargetValues.push_back( connectorPair );
720
721   // using destination type so component animation gets correct type
722   switch( destinationType )
723   {
724     case Dali::Property::BOOLEAN:
725     {
726       const KeyFrameBoolean* kf;
727       GetSpecialization(keyFrames, kf);
728       KeyFrameBooleanPtr kfCopy = KeyFrameBoolean::Clone(*kf);
729       AddAnimatorConnector( AnimatorConnector<bool>::New( object,
730                                                           target.propertyIndex,
731                                                           target.componentIndex,
732                                                           new KeyFrameBooleanFunctor(kfCopy),
733                                                           alpha,
734                                                           period ) );
735       break;
736     }
737
738     case Dali::Property::INTEGER:
739     {
740       const KeyFrameInteger* kf;
741       GetSpecialization(keyFrames, kf);
742       KeyFrameIntegerPtr kfCopy = KeyFrameInteger::Clone(*kf);
743       AddAnimatorConnector( AnimatorConnector<int32_t>::New( object,
744                                                          target.propertyIndex,
745                                                          target.componentIndex,
746                                                          new KeyFrameIntegerFunctor(kfCopy,interpolation),
747                                                          alpha,
748                                                          period ) );
749       break;
750     }
751
752     case Dali::Property::FLOAT:
753     {
754       const KeyFrameNumber* kf;
755       GetSpecialization(keyFrames, kf);
756       KeyFrameNumberPtr kfCopy = KeyFrameNumber::Clone(*kf);
757       AddAnimatorConnector( AnimatorConnector<float>::New( object,
758                                                            target.propertyIndex,
759                                                            target.componentIndex,
760                                                            new KeyFrameNumberFunctor(kfCopy,interpolation),
761                                                            alpha,
762                                                            period ) );
763       break;
764     }
765
766     case Dali::Property::VECTOR2:
767     {
768       const KeyFrameVector2* kf;
769       GetSpecialization(keyFrames, kf);
770       KeyFrameVector2Ptr kfCopy = KeyFrameVector2::Clone(*kf);
771       AddAnimatorConnector( AnimatorConnector<Vector2>::New( object,
772                                                              target.propertyIndex,
773                                                              target.componentIndex,
774                                                              new KeyFrameVector2Functor(kfCopy,interpolation),
775                                                              alpha,
776                                                              period ) );
777       break;
778     }
779
780     case Dali::Property::VECTOR3:
781     {
782       const KeyFrameVector3* kf;
783       GetSpecialization(keyFrames, kf);
784       KeyFrameVector3Ptr kfCopy = KeyFrameVector3::Clone(*kf);
785       AddAnimatorConnector( AnimatorConnector<Vector3>::New( object,
786                                                              target.propertyIndex,
787                                                              target.componentIndex,
788                                                              new KeyFrameVector3Functor(kfCopy,interpolation),
789                                                              alpha,
790                                                              period ) );
791       break;
792     }
793
794     case Dali::Property::VECTOR4:
795     {
796       const KeyFrameVector4* kf;
797       GetSpecialization(keyFrames, kf);
798       KeyFrameVector4Ptr kfCopy = KeyFrameVector4::Clone(*kf);
799       AddAnimatorConnector( AnimatorConnector<Vector4>::New( object,
800                                                              target.propertyIndex,
801                                                              target.componentIndex,
802                                                              new KeyFrameVector4Functor(kfCopy,interpolation),
803                                                              alpha,
804                                                              period ) );
805       break;
806     }
807
808     case Dali::Property::ROTATION:
809     {
810       const KeyFrameQuaternion* kf;
811       GetSpecialization(keyFrames, kf);
812       KeyFrameQuaternionPtr kfCopy = KeyFrameQuaternion::Clone(*kf);
813       AddAnimatorConnector( AnimatorConnector<Quaternion>::New( object,
814                                                                 target.propertyIndex,
815                                                                 target.componentIndex,
816                                                                 new KeyFrameQuaternionFunctor(kfCopy),
817                                                                 alpha,
818                                                                 period ) );
819       break;
820     }
821
822     default:
823     {
824       // non animatable types handled by keyframes
825     }
826   }
827 }
828
829 bool Animation::HasFinished()
830 {
831   bool hasFinished(false);
832   const int32_t playedCount(mAnimation->GetPlayedCount());
833
834   // If the play count has been incremented, then another notification is required
835   mCurrentLoop = mAnimation->GetCurrentLoop();
836
837   if (playedCount > mNotificationCount)
838   {
839     // Note that only one signal is emitted, if the animation has been played repeatedly
840     mNotificationCount = playedCount;
841
842     hasFinished = true;
843
844     mState = Dali::Animation::STOPPED;
845   }
846
847   return hasFinished;
848 }
849
850 Dali::Animation::AnimationSignalType& Animation::FinishedSignal()
851 {
852   return mFinishedSignal;
853 }
854
855 Dali::Animation::AnimationSignalType& Animation::ProgressReachedSignal()
856 {
857   return mProgressReachedSignal;
858 }
859
860 void Animation::EmitSignalFinish()
861 {
862   if ( !mFinishedSignal.Empty() )
863   {
864     Dali::Animation handle( this );
865     mFinishedSignal.Emit( handle );
866   }
867 }
868
869 void Animation::EmitSignalProgressReached()
870 {
871   if ( !mProgressReachedSignal.Empty() )
872   {
873     Dali::Animation handle( this );
874     mProgressReachedSignal.Emit( handle );
875   }
876 }
877
878 bool Animation::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
879 {
880   bool connected( true );
881   Animation* animation = static_cast< Animation* >(object); // TypeRegistry guarantees that this is the correct type.
882
883   if( 0 == signalName.compare( SIGNAL_FINISHED ) )
884   {
885     animation->FinishedSignal().Connect( tracker, functor );
886   }
887   else
888   {
889     // signalName does not match any signal
890     connected = false;
891   }
892
893   return connected;
894 }
895
896 void Animation::AddAnimatorConnector( AnimatorConnectorBase* connector )
897 {
898   DALI_ASSERT_DEBUG( NULL != connector );
899
900   connector->SetParent(*this);
901
902   mConnectors.PushBack( connector );
903 }
904
905 void Animation::Animate( Actor& actor, const Path& path, const Vector3& forward )
906 {
907   Animate( actor, path, forward, mDefaultAlpha, TimePeriod(mDurationSeconds) );
908 }
909
910 void Animation::Animate( Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha )
911 {
912   Animate( actor, path, forward, alpha, TimePeriod(mDurationSeconds) );
913 }
914
915 void Animation::Animate( Actor& actor, const Path& path, const Vector3& forward, TimePeriod period )
916 {
917   Animate( actor, path, forward, mDefaultAlpha, period );
918 }
919
920 void Animation::Animate( Actor& actor, const Path& path, const Vector3& forward, AlphaFunction alpha, TimePeriod period)
921 {
922   ExtendDuration( period );
923
924   PathPtr pathCopy = Path::Clone(path);
925
926   //Position animation
927   AddAnimatorConnector( AnimatorConnector<Vector3>::New( actor,
928                                                          Dali::Actor::Property::POSITION,
929                                                          Property::INVALID_COMPONENT_INDEX,
930                                                          new PathPositionFunctor( pathCopy ),
931                                                          alpha,
932                                                          period ) );
933
934   //If forward is zero, PathRotationFunctor will always return the unit quaternion
935   if( forward != Vector3::ZERO )
936   {
937     //Rotation animation
938     AddAnimatorConnector( AnimatorConnector<Quaternion>::New( actor,
939                                                               Dali::Actor::Property::ORIENTATION,
940                                                               Property::INVALID_COMPONENT_INDEX,
941                                                               new PathRotationFunctor( pathCopy, forward ),
942                                                               alpha,
943                                                               period ) );
944   }
945 }
946
947 void Animation::Show(Actor& actor, float delaySeconds)
948 {
949   ExtendDuration( TimePeriod(delaySeconds, 0) );
950
951   AddAnimatorConnector( AnimatorConnector<bool>::New( actor,
952                                                       Dali::Actor::Property::VISIBLE,
953                                                       Property::INVALID_COMPONENT_INDEX,
954                                                       new AnimateToBoolean(SHOW_VALUE),
955                                                       mDefaultAlpha,
956                                                       TimePeriod(delaySeconds, 0.0f/*immediate*/) ) );
957 }
958
959 void Animation::Hide(Actor& actor, float delaySeconds)
960 {
961   ExtendDuration( TimePeriod(delaySeconds, 0) );
962
963   AddAnimatorConnector( AnimatorConnector<bool>::New( actor,
964                                                       Dali::Actor::Property::VISIBLE,
965                                                       Property::INVALID_COMPONENT_INDEX,
966                                                       new AnimateToBoolean(HIDE_VALUE),
967                                                       mDefaultAlpha,
968                                                       TimePeriod(delaySeconds, 0.0f/*immediate*/) ) );
969 }
970
971 bool Animation::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& attributes )
972 {
973   bool done = false;
974   Animation* animation = dynamic_cast<Animation*>( object );
975
976   if( animation )
977   {
978     if( 0 == actionName.compare( ACTION_PLAY ) )
979     {
980       if( Property::Value* value = attributes.Find("duration", Property::FLOAT) )
981       {
982         animation->SetDuration( value->Get<float>() );
983       }
984
985       animation->Play();
986       done = true;
987     }
988     else if( 0 == actionName.compare( ACTION_STOP ) )
989     {
990       animation->Stop();
991       done = true;
992     }
993     else if( 0 == actionName.compare( ACTION_PAUSE ) )
994     {
995       animation->Pause();
996       done = true;
997     }
998   }
999
1000   return done;
1001 }
1002
1003 void Animation::SetCurrentProgress(float progress)
1004 {
1005   if( mAnimation && progress >= mPlayRange.x && progress <= mPlayRange.y )
1006   {
1007     // mAnimation is being used in a separate thread; queue a message to set the current progress
1008     SetCurrentProgressMessage( mEventThreadServices, *mAnimation, progress );
1009   }
1010 }
1011
1012 float Animation::GetCurrentProgress()
1013 {
1014   if( mAnimation )
1015   {
1016     return mAnimation->GetCurrentProgress();
1017   }
1018
1019   return 0.0f;
1020 }
1021
1022 void Animation::ExtendDuration( const TimePeriod& timePeriod )
1023 {
1024   float duration = timePeriod.delaySeconds + timePeriod.durationSeconds;
1025
1026   if( duration > mDurationSeconds )
1027   {
1028     SetDuration( duration );
1029   }
1030 }
1031
1032 void Animation::SetSpeedFactor( float factor )
1033 {
1034   if( mAnimation )
1035   {
1036     mSpeedFactor = factor;
1037     SetSpeedFactorMessage( mEventThreadServices, *mAnimation, factor );
1038   }
1039 }
1040
1041 float Animation::GetSpeedFactor() const
1042 {
1043   return mSpeedFactor;
1044 }
1045
1046 void Animation::SetPlayRange( const Vector2& range)
1047 {
1048   //Make sure the range specified is between 0.0 and 1.0
1049   if( range.x >= 0.0f && range.x <= 1.0f && range.y >= 0.0f && range.y <= 1.0f )
1050   {
1051     Vector2 orderedRange( range );
1052     //If the range is not in order swap values
1053     if( range.x > range.y )
1054     {
1055       orderedRange = Vector2(range.y, range.x);
1056     }
1057
1058     // Cache for public getters
1059     mPlayRange = orderedRange;
1060
1061     // mAnimation is being used in a separate thread; queue a message to set play range
1062     SetPlayRangeMessage( mEventThreadServices, *mAnimation, orderedRange );
1063   }
1064 }
1065
1066 Vector2 Animation::GetPlayRange() const
1067 {
1068   return mPlayRange;
1069 }
1070
1071 void Animation::SetLoopingMode( Dali::Animation::LoopingMode loopingMode )
1072 {
1073   mAutoReverseEnabled = ( loopingMode == Dali::Animation::LoopingMode::AUTO_REVERSE );
1074
1075   // mAnimation is being used in a separate thread; queue a message to set play range
1076   SetLoopingModeMessage( mEventThreadServices, *mAnimation, mAutoReverseEnabled );
1077 }
1078
1079 Dali::Animation::LoopingMode Animation::GetLoopingMode() const
1080 {
1081   return mAutoReverseEnabled ? Dali::Animation::AUTO_REVERSE : Dali::Animation::RESTART;
1082 }
1083
1084 bool Animation::CompareConnectorEndTimes( const Animation::ConnectorTargetValues& lhs, const Animation::ConnectorTargetValues& rhs )
1085 {
1086   return ( ( lhs.timePeriod.delaySeconds + lhs.timePeriod.durationSeconds ) < ( rhs.timePeriod.delaySeconds + rhs.timePeriod.durationSeconds ) );
1087 }
1088
1089 void Animation::NotifyObjects()
1090 {
1091   if( mEndAction != EndAction::Discard ) // If the animation is discarded, then we do not want to change the target values
1092   {
1093     // Sort according to end time with earlier end times coming first, if the end time is the same, then the connectors are not moved
1094     std::stable_sort( mConnectorTargetValues.begin(), mConnectorTargetValues.end(), CompareConnectorEndTimes );
1095
1096     // Loop through all connector target values sorted by increasing end time
1097     ConnectorTargetValuesContainer::const_iterator iter = mConnectorTargetValues.begin();
1098     const ConnectorTargetValuesContainer::const_iterator endIter = mConnectorTargetValues.end();
1099     for( ; iter != endIter; ++iter )
1100     {
1101       AnimatorConnectorBase* connector = mConnectors[ iter->connectorIndex ];
1102
1103       Object* object = connector->GetObject();
1104       if( object )
1105       {
1106         object->NotifyPropertyAnimation( *this, connector->GetPropertyIndex(), iter->targetValue, iter->animatorType );
1107       }
1108     }
1109   }
1110 }
1111
1112
1113 void Animation::SendFinalProgressNotificationMessage()
1114 {
1115   if ( mProgressReachedMarker > 0.0f )
1116   {
1117     float progressMarkerSeconds = mDurationSeconds * mProgressReachedMarker;
1118     SetProgressNotificationMessage( mEventThreadServices, *mAnimation, progressMarkerSeconds );
1119   }
1120 }
1121
1122 } // namespace Internal
1123
1124 } // namespace Dali