Scripting: Update constant replacement in JSON files
[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
168   if( OptionalBoolean looping = constant.IsBoolean(  IsChild(child, "loop" ) ) )
169   {
170     animation.SetLooping( *looping );
171   }
172
173   if( OptionalString endAction = constant.IsString(  IsChild(child, "end-action" ) ) )
174   {
175     if("BAKE" == *endAction)
176     {
177       animation.SetEndAction( Animation::Bake );
178     }
179     else if("DISCARD" == *endAction)
180     {
181       animation.SetEndAction( Animation::Discard );
182     }
183   }
184
185   if( OptionalString endAction = constant.IsString(  IsChild(child, "destroy-action" ) ) )
186   {
187     if("BAKE" == *endAction)
188     {
189       animation.SetDestroyAction( Animation::Bake );
190     }
191     else if("DISCARD" == *endAction)
192     {
193       animation.SetDestroyAction( Animation::Discard );
194     }
195   }
196
197   OptionalChild propertiesNode = IsChild(child, "properties" );
198   if(propertiesNode)
199   {
200     const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
201     for( TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter )
202     {
203       const TreeNode::KeyNodePair& pKeyChild = *iter;
204
205       OptionalString actorName( constant.IsString( IsChild(pKeyChild.second, "actor" ) ) );
206       OptionalString property(  constant.IsString( IsChild(pKeyChild.second, "property" ) ) );
207       DALI_ASSERT_ALWAYS( actorName && "Animation must specify actor name" );
208       DALI_ASSERT_ALWAYS( property  && "Animation must specify a property name" );
209
210       Actor targetActor = searchActor.FindChildByName( *actorName );
211       DALI_ASSERT_ALWAYS( targetActor && "Actor must exist for property" );
212
213       Property::Index idx( targetActor.GetPropertyIndex( *property ) );
214
215       // A limitation here is that its possible that between binding to the signal and
216       // the signal call that the ShaderEffect of the targetActor has been changed.
217       // However this is a unlikely use case especially when using scripts.
218       if( idx == Property::INVALID_INDEX )
219       {
220         if( ShaderEffect effect = targetActor.GetShaderEffect() )
221         {
222           idx = effect.GetPropertyIndex( *property );
223           if(idx != Property::INVALID_INDEX)
224           {
225             targetActor = effect;
226           }
227           else
228           {
229             DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
230             continue;
231           }
232         }
233         else
234         {
235           DALI_SCRIPT_WARNING( "Cannot find property on object or ShaderEffect\n" );
236           continue;
237         }
238       }
239
240       if( idx == Property::INVALID_INDEX)
241       {
242         DALI_ASSERT_ALWAYS( idx != Property::INVALID_INDEX && "Animation must specify a valid property" );
243         continue;
244       }
245
246       Property prop( Property( targetActor, idx ) );
247       Property::Value propValue;
248
249       // these are the defaults
250       AlphaFunction alphaFunction( AlphaFunctions::Default );
251       TimePeriod timePeriod( 0.f );
252
253       OptionalChild timeChild = IsChild( pKeyChild.second, "time-period" );
254
255       if( timeChild )
256       {
257         timePeriod = GetTimePeriod( *timeChild, constant );
258       }
259
260       durationSum = std::max( durationSum, timePeriod.delaySeconds + timePeriod.durationSeconds );
261
262       if( OptionalString alphaChild = constant.IsString( IsChild(pKeyChild.second, "alpha-function" ) ) )
263       {
264         alphaFunction = GetAlphaFunction( *alphaChild );
265       }
266
267       if( OptionalChild keyFrameChild = IsChild(pKeyChild.second, "key-frames") )
268       {
269         KeyFrames keyframes = KeyFrames::New();
270
271         const TreeNode::ConstIterator endIter = (*keyFrameChild).CEnd();
272         for( TreeNode::ConstIterator iter = (*keyFrameChild).CBegin(); endIter != iter; ++iter )
273         {
274           const TreeNode::KeyNodePair& kfKeyChild = *iter;
275
276           OptionalFloat kfProgress = constant.IsFloat( IsChild(kfKeyChild.second, "progress" ) );
277           DALI_ASSERT_ALWAYS( kfProgress && "Key frame entry must have 'progress'" );
278
279           OptionalChild kfValue = IsChild( kfKeyChild.second, "value" );
280           DALI_ASSERT_ALWAYS( kfValue && "Key frame entry must have 'value'" );
281
282           try
283           {
284             propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *kfValue );
285           }
286           catch(...)
287           {
288             DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n",
289                               (*property).c_str(),
290                               PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)) );
291
292             throw;
293           }
294
295           AlphaFunction kfAlphaFunction( AlphaFunctions::Default );
296           if( OptionalString alphaFuncStr = constant.IsString( IsChild(pKeyChild.second, "alpha-function") ) )
297           {
298             kfAlphaFunction = GetAlphaFunction( *alphaFuncStr );
299           }
300
301           keyframes.Add( *kfProgress, propValue, kfAlphaFunction );
302         }
303
304         if( timeChild )
305         {
306           animation.AnimateBetween( prop, keyframes, alphaFunction, timePeriod );
307         }
308         else
309         {
310           animation.AnimateBetween( prop, keyframes, alphaFunction );
311         }
312       }
313       else
314       {
315         try
316         {
317           propValue = GetPropertyValue( prop.object.GetPropertyType(prop.propertyIndex), *IsChild(pKeyChild.second, "value") );
318         }
319         catch(...)
320         {
321           DALI_LOG_WARNING( "Property:'%s' type does not match value type '%s'\n", (*property).c_str(),
322                             PropertyTypes::GetName( prop.object.GetPropertyType(prop.propertyIndex) ) );
323
324           throw;
325         }
326
327         if( OptionalBoolean relative = constant.IsBoolean( IsChild(pKeyChild.second, "relative") ) )
328         {
329           if( timeChild )
330           {
331             animation.AnimateBy( prop, propValue, alphaFunction, timePeriod );
332           }
333           else
334           {
335             animation.AnimateBy( prop, propValue, alphaFunction );
336           }
337         }
338         else
339         {
340           if( timeChild )
341           {
342             animation.AnimateTo( prop, propValue, alphaFunction, timePeriod );
343           }
344           else
345           {
346             animation.AnimateTo( prop, propValue, alphaFunction );
347           }
348         }
349       }
350     }
351   }
352
353   if( !duration )
354   {
355     animation.SetDuration( durationSum );
356   }
357
358   return animation;
359 }
360
361 Animation CreateAnimation( const TreeNode& child )
362 {
363   Replacement replacement;
364   return CreateAnimation( child, replacement, Stage::GetCurrent().GetRootLayer() );
365 }
366
367 } // namespace Internal
368
369 } // namespace Toolkit
370
371 } // namespace Dali
372