Revert to tizen branch.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / builder / builder-animations.cpp
1 /*
2  * Copyright (c) 2014 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 // EXTERNAL INCLUDES
19 #include <dali/public-api/actors/layer.h>
20 #include <dali/public-api/actors/renderable-actor.h>
21 #include <dali/integration-api/debug.h>
22
23 // INTERNAL INCLUDES
24 #include <dali-toolkit/internal/builder/builder-impl.h>
25 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
26 #include <dali-toolkit/internal/builder/replacement.h>
27
28 namespace // unnamed namespace
29 {
30
31 using namespace Dali;
32
33 TimePeriod GetTimePeriod( const TreeNode& child, const Toolkit::Internal::Replacement& constant )
34 {
35   OptionalFloat delay      = constant.IsFloat( IsChild(child, "delay" ) );
36   OptionalFloat duration   = constant.IsFloat( IsChild(child, "duration" ) );
37   DALI_ASSERT_ALWAYS( duration && "Time period must have at least a duration" );
38
39   if( delay )
40   {
41     return TimePeriod( *delay, *duration );
42   }
43   else
44   {
45     return TimePeriod( *duration );
46   }
47 }
48
49 Property::Value GetPropertyValue( const Property::Type& propType, const TreeNode& child )
50 {
51   switch ( propType )
52   {
53     case Property::BOOLEAN:
54     {
55       return Property::Value( GetBoolean( child ) );
56     }
57
58     case Property::FLOAT:
59     {
60       return Property::Value( GetFloat( child ) );
61     }
62
63     case Property::VECTOR2:
64     {
65       return Property::Value( GetVector2( child ) );
66     }
67
68     case Property::VECTOR3:
69     {
70       return Property::Value( GetVector3( child ) );
71     }
72
73     case Property::VECTOR4:
74     {
75       return Property::Value( GetVector4( child ) );
76     }
77
78     case Property::ROTATION:
79     {
80       if( 4 == child.Size() )
81       {
82         Vector4 v(GetVector4(child));
83         // angle, axis as per spec
84         return Property::Value( Quaternion(Radian(Degree(v[3])),
85                                            Vector3(v[0],v[1],v[2])) );
86       }
87       else
88       {
89         // degrees as per spec
90         Vector3 rotation = GetVector3( child );
91         return Property::Value( Quaternion(Radian(Degree(rotation.x)),
92                                            Radian(Degree(rotation.y)),
93                                            Radian(Degree(rotation.z))) );
94       }
95     }
96
97     case Property::NONE: // fall
98     default:
99     {
100       DALI_ASSERT_ALWAYS( !"Property type incorrect" );
101     }
102   }
103 }
104
105 AlphaFunction GetAlphaFunction( const std::string& alphaFunction )
106 {
107   typedef std::map< const std::string, Dali::AlphaFunction > AlphaFunctionLut;
108   static AlphaFunctionLut alphaFunctionLut;
109
110   if( 0 == alphaFunctionLut.size() )
111   {
112     // coding convention is uppercase enums
113     alphaFunctionLut["DEFAULT"]                    = AlphaFunctions::Default;
114     alphaFunctionLut["LINEAR"]                     = AlphaFunctions::Linear;
115     alphaFunctionLut["SQUARE"]                     = AlphaFunctions::Square;
116     alphaFunctionLut["REVERSE"]                    = AlphaFunctions::Reverse;
117     alphaFunctionLut["EASE_IN"]                    = AlphaFunctions::EaseIn;
118     alphaFunctionLut["EASE_OUT"]                   = AlphaFunctions::EaseOut;
119     alphaFunctionLut["EASE_IN_OUT"]                = AlphaFunctions::EaseInOut;
120     alphaFunctionLut["EASE_IN_SINE"]               = AlphaFunctions::EaseInSine;
121     alphaFunctionLut["EASE_OUT_SINE"]              = AlphaFunctions::EaseOutSine;
122     alphaFunctionLut["EASE_IN_OUT_SINE"]           = AlphaFunctions::EaseInOutSine;
123     alphaFunctionLut["EASE_IN_SINE_33"]            = AlphaFunctions::EaseInSine33;
124     alphaFunctionLut["EASE_OUT_SINE_33"]           = AlphaFunctions::EaseOutSine33;
125     alphaFunctionLut["EASE_IN_OUT_SINE_33"]        = AlphaFunctions::EaseInOutSine33;
126     alphaFunctionLut["EASE_IN_OUT_SINE_50"]        = AlphaFunctions::EaseInOutSine50;
127     alphaFunctionLut["EASE_IN_OUT_SINE_60"]        = AlphaFunctions::EaseInOutSine60;
128     alphaFunctionLut["EASE_IN_OUT_SINE_70"]        = AlphaFunctions::EaseInOutSine70;
129     alphaFunctionLut["EASE_IN_OUT_SINE_80"]        = AlphaFunctions::EaseInOutSine80;
130     alphaFunctionLut["EASE_IN_OUT_SINE_90"]        = AlphaFunctions::EaseInOutSine90;
131     alphaFunctionLut["DOUBLE_EASE_IN_OUT_SINE_60"] = AlphaFunctions::DoubleEaseInOutSine60;
132     alphaFunctionLut["EASE_OUT_QUINT_50"]          = AlphaFunctions::EaseOutQuint50;
133     alphaFunctionLut["EASE_OUT_QUINT_80"]          = AlphaFunctions::EaseOutQuint80;
134     alphaFunctionLut["BOUNCE"]                     = AlphaFunctions::Bounce;
135     alphaFunctionLut["BOUNCE_BACK"]                = AlphaFunctions::BounceBack;
136     alphaFunctionLut["EASE_IN_BACK"]               = AlphaFunctions::EaseInBack;
137     alphaFunctionLut["EASE_OUT_BACK"]              = AlphaFunctions::EaseOutBack;
138     alphaFunctionLut["EASE_IN_OUT_BACK"]           = AlphaFunctions::EaseInOutBack;
139     alphaFunctionLut["SIN"]                        = AlphaFunctions::Sin;
140     alphaFunctionLut["SIN2X"]                      = AlphaFunctions::Sin2x;
141   }
142
143   const AlphaFunctionLut::const_iterator iter( alphaFunctionLut.find( alphaFunction ) );
144
145   if( iter != alphaFunctionLut.end() )
146   {
147     return iter->second;
148   }
149   else
150   {
151     DALI_ASSERT_ALWAYS( iter != alphaFunctionLut.end() && "Unknown Anchor Constant" );
152     return Dali::AlphaFunctions::Default;
153   }
154 }
155
156 } // unnamed namespace
157
158
159 namespace Dali
160 {
161
162 namespace Toolkit
163 {
164
165 namespace Internal
166 {
167
168 Animation CreateAnimation( const TreeNode& child, const Replacement& constant, Dali::Actor searchRoot, Builder* const builder )
169 {
170   float durationSum = 0.f;
171
172   Dali::Actor searchActor = searchRoot ? searchRoot : Dali::Stage::GetCurrent().GetRootLayer();
173
174   Animation animation( Animation::New( 0.f ) );
175
176   // duration needs to be set before AnimateTo calls for correct operation when AnimateTo has no "time-period".
177   OptionalFloat duration = constant.IsFloat( IsChild(child, "duration" ) );
178
179   if( duration )
180   {
181     animation.SetDuration( *duration );
182   }
183
184   if( OptionalBoolean looping = constant.IsBoolean(  IsChild(child, "loop" ) ) )
185   {
186     animation.SetLooping( *looping );
187   }
188
189   if( OptionalString endAction = constant.IsString(  IsChild(child, "end-action" ) ) )
190   {
191     if("BAKE" == *endAction)
192     {
193       animation.SetEndAction( Animation::Bake );
194     }
195     else if("DISCARD" == *endAction)
196     {
197       animation.SetEndAction( Animation::Discard );
198     }
199     else if("BAKE_FINAL" == *endAction)
200     {
201       animation.SetEndAction( Animation::BakeFinal );
202     }
203   }
204
205   if( OptionalString endAction = constant.IsString(  IsChild(child, "disconnect-action" ) ) )
206   {
207     if("BAKE" == *endAction)
208     {
209       animation.SetDisconnectAction( Animation::Bake );
210     }
211     else if("DISCARD" == *endAction)
212     {
213       animation.SetDisconnectAction( Animation::Discard );
214     }
215     else if("BAKE_FINAL" == *endAction)
216     {
217       animation.SetDisconnectAction( Animation::BakeFinal );
218     }
219   }
220
221   OptionalChild propertiesNode = IsChild(child, "properties" );
222   if(propertiesNode)
223   {
224     const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
225     for( TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter )
226     {
227       const TreeNode::KeyNodePair& pKeyChild = *iter;
228
229       OptionalString actorName( constant.IsString( IsChild(pKeyChild.second, "actor" ) ) );
230       OptionalString property(  constant.IsString( IsChild(pKeyChild.second, "property" ) ) );
231       DALI_ASSERT_ALWAYS( actorName && "Animation must specify actor name" );
232
233       Handle targetHandle = searchActor.FindChildByName( *actorName );
234       DALI_ASSERT_ALWAYS( targetHandle && "Actor must exist for property" );
235
236       Property::Value propValue;
237       Property::Index propIndex = Property::INVALID_INDEX;
238       if( property )
239       {
240         propIndex = targetHandle.GetPropertyIndex( *property );
241
242         // if the property is not found from the (actor) handle, try to downcast it to renderable actor
243         // to allow animating shader uniforms
244         if( propIndex == Property::INVALID_INDEX )
245         {
246           RenderableActor renderable = RenderableActor::DownCast( targetHandle );
247           if( renderable )
248           {
249             // A limitation here is that its possible that between creation of animation
250             // and running it the ShaderEffect of the actor has been changed.
251             // However this is a unlikely use case especially when using scripts.
252             if( ShaderEffect effect = renderable.GetShaderEffect() )
253             {
254               propIndex = effect.GetPropertyIndex( *property );
255               if(propIndex != Property::INVALID_INDEX)
256               {
257                 targetHandle = effect;
258               }
259               else
260               {
261                 DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
262                 continue;
263               }
264             }
265           }
266           else
267           {
268             DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
269             continue;
270           }
271         }
272
273         if( propIndex == Property::INVALID_INDEX)
274         {
275           DALI_ASSERT_ALWAYS( propIndex != Property::INVALID_INDEX && "Animation must specify a valid property" );
276           continue;
277         }
278       }
279
280       // these are the defaults
281       AlphaFunction alphaFunction( AlphaFunctions::Default );
282       TimePeriod timePeriod( 0.f );
283
284       OptionalChild timeChild = IsChild( pKeyChild.second, "time-period" );
285
286       if( timeChild )
287       {
288         timePeriod = GetTimePeriod( *timeChild, constant );
289       }
290
291       durationSum = std::max( durationSum, timePeriod.delaySeconds + timePeriod.durationSeconds );
292
293       if( OptionalString alphaChild = constant.IsString( IsChild(pKeyChild.second, "alpha-function" ) ) )
294       {
295         alphaFunction = GetAlphaFunction( *alphaChild );
296       }
297
298       if( OptionalChild keyFrameChild = IsChild(pKeyChild.second, "key-frames") )
299       {
300         DALI_ASSERT_ALWAYS( property  && "Animation must specify a property name" );
301         Property prop = Property( targetHandle, propIndex );
302
303         KeyFrames keyframes = KeyFrames::New();
304
305         const TreeNode::ConstIterator endIter = (*keyFrameChild).CEnd();
306         for( TreeNode::ConstIterator iter = (*keyFrameChild).CBegin(); endIter != iter; ++iter )
307         {
308           const TreeNode::KeyNodePair& kfKeyChild = *iter;
309
310           OptionalFloat kfProgress = constant.IsFloat( IsChild(kfKeyChild.second, "progress" ) );
311           DALI_ASSERT_ALWAYS( kfProgress && "Key frame entry must have 'progress'" );
312
313           OptionalChild kfValue = IsChild( kfKeyChild.second, "value" );
314           DALI_ASSERT_ALWAYS( kfValue && "Key frame entry must have 'value'" );
315
316           try
317           {
318             propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *kfValue );
319           }
320           catch(...)
321           {
322             DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n",
323                               (*property).c_str(),
324                               PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)) );
325
326             throw;
327           }
328
329           AlphaFunction kfAlphaFunction( AlphaFunctions::Default );
330           if( OptionalString alphaFuncStr = constant.IsString( IsChild(pKeyChild.second, "alpha-function") ) )
331           {
332             kfAlphaFunction = GetAlphaFunction( *alphaFuncStr );
333           }
334
335           keyframes.Add( *kfProgress, propValue, kfAlphaFunction );
336         }
337
338         if( timeChild )
339         {
340           animation.AnimateBetween( prop, keyframes, alphaFunction, timePeriod );
341         }
342         else
343         {
344           animation.AnimateBetween( prop, keyframes, alphaFunction );
345         }
346       }
347       else if( OptionalString pathChild = IsString(pKeyChild.second, "path") )
348       {
349         //Get path
350         Path path = builder->GetPath(*pathChild);
351         if( path )
352         {
353           //Get forward vector if specified
354           Vector3 forward( 0.0f, 0.0f, 0.0f );
355           OptionalVector3 forwardProperty = constant.IsVector3( IsChild(pKeyChild.second, "forward" ) );
356           if( forwardProperty )
357           {
358             forward = *forwardProperty;
359           }
360
361           Actor actor = Actor::DownCast( targetHandle );
362           if( actor )
363           {
364             if( timeChild )
365             {
366               animation.Animate( actor, path, forward, alphaFunction, timePeriod );
367             }
368             else
369             {
370               animation.Animate( actor, path, forward, alphaFunction );
371             }
372
373           }
374         }
375         else
376         {
377           //Path not found
378           DALI_SCRIPT_WARNING( "Cannot find animation path '%s'\n", (*pathChild).c_str() );
379         }
380       }
381       else
382       {
383         DALI_ASSERT_ALWAYS( property  && "Animation must specify a property name" );
384
385         Property prop = Property( targetHandle, propIndex );
386         try
387         {
388           propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *IsChild(pKeyChild.second, "value") );
389         }
390         catch(...)
391         {
392           DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n", (*property).c_str(),
393                             PropertyTypes::GetName( prop.object.GetPropertyType(prop.propertyIndex) ) );
394
395           throw;
396         }
397
398         if( OptionalBoolean relative = constant.IsBoolean( IsChild(pKeyChild.second, "relative") ) )
399         {
400           if( timeChild )
401           {
402             animation.AnimateBy( prop, propValue, alphaFunction, timePeriod );
403           }
404           else
405           {
406             animation.AnimateBy( prop, propValue, alphaFunction );
407           }
408         }
409         else
410         {
411           if( timeChild )
412           {
413             animation.AnimateTo( prop, propValue, alphaFunction, timePeriod );
414           }
415           else
416           {
417             animation.AnimateTo( prop, propValue, alphaFunction );
418           }
419         }
420       }
421     }
422   }
423
424   if( !duration )
425   {
426     animation.SetDuration( durationSum );
427   }
428
429   return animation;
430 }
431
432 Animation CreateAnimation( const TreeNode& child, Builder* const builder )
433 {
434   Replacement replacement;
435   return CreateAnimation( child, replacement, Stage::GetCurrent().GetRootLayer(), builder );
436 }
437
438 } // namespace Internal
439
440 } // namespace Toolkit
441
442 } // namespace Dali
443