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