[dali_2.3.19] Merge branch 'devel/master'
[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/devel-api/common/stage.h>
20 #include <dali/integration-api/debug.h>
21 #include <dali/public-api/actors/layer.h>
22
23 // INTERNAL INCLUDES
24 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
25 #include <dali-toolkit/internal/builder/builder-impl.h>
26 #include <dali-toolkit/internal/builder/replacement.h>
27
28 namespace // unnamed namespace
29 {
30 using namespace Dali;
31
32 TimePeriod GetTimePeriod(const TreeNode& child, const Toolkit::Internal::Replacement& constant)
33 {
34   OptionalFloat delay    = constant.IsFloat(IsChild(child, "delay"));
35   OptionalFloat duration = constant.IsFloat(IsChild(child, "duration"));
36   DALI_ASSERT_ALWAYS(duration && "Time period must have at least a duration");
37
38   if(delay)
39   {
40     return TimePeriod(*delay, *duration);
41   }
42   else
43   {
44     return TimePeriod(*duration);
45   }
46 }
47
48 Property::Value GetPropertyValue(const Property::Type& propType, const TreeNode& child)
49 {
50   switch(propType)
51   {
52     case Property::BOOLEAN:
53     {
54       return Property::Value(GetBoolean(child));
55     }
56
57     case Property::FLOAT:
58     {
59       return Property::Value(GetFloat(child));
60     }
61
62     case Property::VECTOR2:
63     {
64       return Property::Value(GetVector2(child));
65     }
66
67     case Property::VECTOR3:
68     {
69       return Property::Value(GetVector3(child));
70     }
71
72     case Property::VECTOR4:
73     {
74       return Property::Value(GetVector4(child));
75     }
76
77     case Property::ROTATION:
78     {
79       if(4 == child.Size())
80       {
81         Vector4 v(GetVector4(child));
82         // angle, axis as per spec
83         return Property::Value(Quaternion(Radian(Degree(v[3])),
84                                           Vector3(v[0], v[1], v[2])));
85       }
86       else
87       {
88         // degrees as per spec
89         Vector3 rotation = GetVector3(child);
90         return Property::Value(Quaternion(Radian(Degree(rotation.x)),
91                                           Radian(Degree(rotation.y)),
92                                           Radian(Degree(rotation.z))));
93       }
94     }
95
96     case Property::NONE: // fall
97     default:
98     {
99       DALI_ASSERT_ALWAYS(!"Property type incorrect");
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"]          = AlphaFunction(AlphaFunction::DEFAULT);
113     alphaFunctionLut["LINEAR"]           = AlphaFunction(AlphaFunction::LINEAR);
114     alphaFunctionLut["REVERSE"]          = AlphaFunction(AlphaFunction::REVERSE);
115     alphaFunctionLut["EASE_IN_SQUARE"]   = AlphaFunction(AlphaFunction::EASE_IN_SQUARE);
116     alphaFunctionLut["EASE_OUT_SQUARE"]  = AlphaFunction(AlphaFunction::EASE_OUT_SQUARE);
117     alphaFunctionLut["EASE_IN"]          = AlphaFunction(AlphaFunction::EASE_IN);
118     alphaFunctionLut["EASE_OUT"]         = AlphaFunction(AlphaFunction::EASE_OUT);
119     alphaFunctionLut["EASE_IN_OUT"]      = AlphaFunction(AlphaFunction::EASE_IN_OUT);
120     alphaFunctionLut["EASE_IN_SINE"]     = AlphaFunction(AlphaFunction::EASE_IN_SINE);
121     alphaFunctionLut["EASE_OUT_SINE"]    = AlphaFunction(AlphaFunction::EASE_OUT_SINE);
122     alphaFunctionLut["EASE_IN_OUT_SINE"] = AlphaFunction(AlphaFunction::EASE_IN_OUT_SINE);
123     alphaFunctionLut["BOUNCE"]           = AlphaFunction(AlphaFunction::BOUNCE);
124     alphaFunctionLut["SIN"]              = AlphaFunction(AlphaFunction::SIN);
125     alphaFunctionLut["EASE_OUT_BACK"]    = AlphaFunction(AlphaFunction::EASE_OUT_BACK);
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 Alpha Constant");
137     return Dali::AlphaFunction::DEFAULT;
138   }
139 }
140
141 } // unnamed namespace
142
143 namespace Dali
144 {
145 namespace Toolkit
146 {
147 namespace Internal
148 {
149 Animation CreateAnimation(const TreeNode& child, const Replacement& constant, Dali::Actor searchRoot, Builder* const builder)
150 {
151   float durationSum = 0.f;
152
153   Dali::Actor searchActor = searchRoot ? searchRoot : Dali::Stage::GetCurrent().GetRootLayer();
154
155   Animation animation(Animation::New(0.f));
156
157   // duration needs to be set before AnimateTo calls for correct operation when AnimateTo has no "timePeriod".
158   OptionalFloat duration = constant.IsFloat(IsChild(child, "duration"));
159
160   if(duration)
161   {
162     animation.SetDuration(*duration);
163   }
164
165   if(OptionalBoolean looping = constant.IsBoolean(IsChild(child, "loop")))
166   {
167     animation.SetLooping(*looping);
168   }
169
170   if(OptionalString endAction = constant.IsString(IsChild(child, "endAction")))
171   {
172     if("BAKE" == *endAction)
173     {
174       animation.SetEndAction(Animation::BAKE);
175     }
176     else if("DISCARD" == *endAction)
177     {
178       animation.SetEndAction(Animation::DISCARD);
179     }
180     else if("BAKE_FINAL" == *endAction)
181     {
182       animation.SetEndAction(Animation::BAKE_FINAL);
183     }
184   }
185
186   if(OptionalString endAction = constant.IsString(IsChild(child, "disconnectAction")))
187   {
188     if("BAKE" == *endAction)
189     {
190       animation.SetDisconnectAction(Animation::BAKE);
191     }
192     else if("DISCARD" == *endAction)
193     {
194       animation.SetDisconnectAction(Animation::DISCARD);
195     }
196     else if("BAKE_FINAL" == *endAction)
197     {
198       animation.SetDisconnectAction(Animation::BAKE_FINAL);
199     }
200   }
201
202   OptionalChild propertiesNode = IsChild(child, "properties");
203   if(propertiesNode)
204   {
205     const TreeNode::ConstIterator endIter = (*propertiesNode).CEnd();
206     for(TreeNode::ConstIterator iter = (*propertiesNode).CBegin(); endIter != iter; ++iter)
207     {
208       const TreeNode::KeyNodePair& pKeyChild = *iter;
209
210       OptionalString actorName(constant.IsString(IsChild(pKeyChild.second, "actor")));
211       OptionalString property(constant.IsString(IsChild(pKeyChild.second, "property")));
212       DALI_ASSERT_ALWAYS(actorName && "Animation must specify actor name");
213
214       Handle targetHandle = searchActor.FindChildByName(*actorName);
215       DALI_ASSERT_ALWAYS(targetHandle && "Actor must exist for property");
216
217       Property::Value propValue;
218       Property::Index propIndex = Property::INVALID_INDEX;
219       if(property)
220       {
221         propIndex = targetHandle.GetPropertyIndex(*property);
222
223         // if the property is not found from the (actor) handle, try to downcast it to renderable actor
224         // to allow animating shader uniforms
225         if(propIndex == Property::INVALID_INDEX)
226         {
227           DALI_SCRIPT_WARNING("Cannot find property on object\n");
228           continue;
229         }
230       }
231
232       // these are the defaults
233       AlphaFunction alphaFunction(AlphaFunction::DEFAULT);
234       TimePeriod    timePeriod(0.f);
235
236       OptionalChild timeChild = IsChild(pKeyChild.second, "timePeriod");
237
238       if(timeChild)
239       {
240         timePeriod = GetTimePeriod(*timeChild, constant);
241       }
242
243       durationSum = std::max(durationSum, timePeriod.delaySeconds + timePeriod.durationSeconds);
244
245       if(OptionalString alphaChild = constant.IsString(IsChild(pKeyChild.second, "alphaFunction")))
246       {
247         alphaFunction = GetAlphaFunction(*alphaChild);
248       }
249
250       if(OptionalChild keyFrameChild = IsChild(pKeyChild.second, "keyFrames"))
251       {
252         DALI_ASSERT_ALWAYS(property && "Animation must specify a property name");
253         Property prop = Property(targetHandle, propIndex);
254
255         KeyFrames keyframes = KeyFrames::New();
256
257         const TreeNode::ConstIterator endIter = (*keyFrameChild).CEnd();
258         for(TreeNode::ConstIterator iter = (*keyFrameChild).CBegin(); endIter != iter; ++iter)
259         {
260           const TreeNode::KeyNodePair& kfKeyChild = *iter;
261
262           OptionalFloat kfProgress = constant.IsFloat(IsChild(kfKeyChild.second, "progress"));
263           DALI_ASSERT_ALWAYS(kfProgress && "Key frame entry must have 'progress'");
264
265           OptionalChild kfValue = IsChild(kfKeyChild.second, "value");
266           DALI_ASSERT_ALWAYS(kfValue && "Key frame entry must have 'value'");
267
268           try
269           {
270             propValue = GetPropertyValue(prop.object.GetPropertyType(prop.propertyIndex), *kfValue);
271           }
272           catch(...)
273           {
274             DALI_LOG_WARNING("Property:'%s' type does not match value type '%s'\n",
275                              (*property).c_str(),
276                              PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)));
277
278             throw;
279           }
280
281           AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
282           if(OptionalString alphaFuncStr = constant.IsString(IsChild(pKeyChild.second, "alphaFunction")))
283           {
284             kfAlphaFunction = GetAlphaFunction(*alphaFuncStr);
285           }
286
287           keyframes.Add(*kfProgress, propValue, kfAlphaFunction);
288         }
289
290         if(timeChild)
291         {
292           animation.AnimateBetween(prop, keyframes, alphaFunction, timePeriod);
293         }
294         else
295         {
296           animation.AnimateBetween(prop, keyframes, alphaFunction);
297         }
298       }
299       else if(OptionalString pathChild = IsString(pKeyChild.second, "path"))
300       {
301         //Get path
302         Path path = builder->GetPath(*pathChild);
303         if(path)
304         {
305           //Get forward vector if specified
306           Vector3         forward(0.0f, 0.0f, 0.0f);
307           OptionalVector3 forwardProperty = constant.IsVector3(IsChild(pKeyChild.second, "forward"));
308           if(forwardProperty)
309           {
310             forward = *forwardProperty;
311           }
312
313           Actor actor = Actor::DownCast(targetHandle);
314           if(actor)
315           {
316             if(timeChild)
317             {
318               animation.Animate(actor, path, forward, alphaFunction, timePeriod);
319             }
320             else
321             {
322               animation.Animate(actor, path, forward, alphaFunction);
323             }
324           }
325         }
326         else
327         {
328           //Path not found
329           DALI_SCRIPT_WARNING("Cannot find animation path '%s'\n", (*pathChild).c_str());
330         }
331       }
332       else
333       {
334         DALI_ASSERT_ALWAYS(property && "Animation must specify a property name");
335
336         Property prop = Property(targetHandle, propIndex);
337         try
338         {
339           propValue = GetPropertyValue(prop.object.GetPropertyType(prop.propertyIndex), *IsChild(pKeyChild.second, "value"));
340         }
341         catch(...)
342         {
343           DALI_LOG_WARNING("Property:'%s' type does not match value type '%s'\n", (*property).c_str(), PropertyTypes::GetName(prop.object.GetPropertyType(prop.propertyIndex)));
344
345           throw;
346         }
347
348         if(OptionalBoolean relative = constant.IsBoolean(IsChild(pKeyChild.second, "relative")))
349         {
350           if(timeChild)
351           {
352             animation.AnimateBy(prop, propValue, alphaFunction, timePeriod);
353           }
354           else
355           {
356             animation.AnimateBy(prop, propValue, alphaFunction);
357           }
358         }
359         else
360         {
361           if(timeChild)
362           {
363             animation.AnimateTo(prop, propValue, alphaFunction, timePeriod);
364           }
365           else
366           {
367             animation.AnimateTo(prop, propValue, alphaFunction);
368           }
369         }
370       }
371     }
372   }
373
374   if(!duration)
375   {
376     animation.SetDuration(durationSum);
377   }
378
379   return animation;
380 }
381
382 Animation CreateAnimation(const TreeNode& child, Builder* const builder)
383 {
384   Replacement replacement;
385   return CreateAnimation(child, replacement, Stage::GetCurrent().GetRootLayer(), builder);
386 }
387
388 } // namespace Internal
389
390 } // namespace Toolkit
391
392 } // namespace Dali