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