Remove dali-any from Property::Value
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / path-impl.cpp
1 /*
2  * Copyright (c) 2015 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 // CLASS HEADER
19 #include <dali/internal/event/animation/path-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/internal/event/common/property-helper.h>
26 #include <dali/public-api/object/property-array.h>
27
28 namespace Dali
29 {
30
31 namespace Internal
32 {
33
34 namespace
35 {
36
37 // Properties
38
39 //              Name             Type   writable animatable constraint-input  enum for index-checking
40 DALI_PROPERTY_TABLE_BEGIN
41 DALI_PROPERTY( "points",         ARRAY, true, false, false,   Dali::Path::Property::POINTS         )
42 DALI_PROPERTY( "control-points", ARRAY, true, false, false,   Dali::Path::Property::CONTROL_POINTS )
43 DALI_PROPERTY_TABLE_END( DEFAULT_OBJECT_PROPERTY_START_INDEX )
44
45 /**
46  * These coefficient arise from the cubic polynomial equations for
47  * a bezier curve.
48  *
49  * A bezier curve is defined by a cubic polynomial. Given two end points p0 and p1
50  * and two control points cp0 and cp1, the bezier curve will be defined by a polynomial in the form
51  * f(x) = a3*x^3 + a2*x^2 + a1*x + a0 with this restrictions:
52  * f(0) = p0
53  * f(1) = p1
54  * f'(0) = 3*(cp0 - p0)
55  * f'(1) = 3*(p1-cp1)
56  */
57 const float BezierBasisCoeff[] = {  -1.0f,  3.0f, -3.0f, 1.0f,
58                                      3.0f, -6.0f,  3.0f, 0.0f,
59                                     -3.0f,  3.0f,  0.0f, 0.0f,
60                                      1.0f,  0.0f,  0.0f, 0.0f  };
61
62 const Dali::Matrix BezierBasis = Dali::Matrix( BezierBasisCoeff );
63
64 } //Unnamed namespace
65
66 Path* Path::New()
67 {
68   return new Path();
69 }
70
71 Path::Path()
72 : Object()
73 {
74 }
75
76 Path::~Path()
77 {
78 }
79
80 Path* Path::Clone(const Path& path)
81 {
82   Path* clone = new Path();
83   clone->SetPoints( path.GetPoints() );
84   clone->SetControlPoints( path.GetControlPoints() );
85
86   return clone;
87 }
88
89 unsigned int Path::GetDefaultPropertyCount() const
90 {
91   return DEFAULT_PROPERTY_COUNT;
92 }
93
94 void Path::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
95 {
96   indices.Reserve( DEFAULT_PROPERTY_COUNT );
97
98   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
99   {
100     indices.PushBack( i );
101   }
102 }
103
104 const char* Path::GetDefaultPropertyName(Property::Index index) const
105 {
106   if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
107   {
108     return DEFAULT_PROPERTY_DETAILS[index].name;
109   }
110
111   // index out of range
112   return NULL;
113 }
114
115 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
116 {
117   Property::Index index = Property::INVALID_INDEX;
118
119   // Look for name in default properties
120   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
121   {
122     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
123     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
124     {
125       index = i;
126       break;
127     }
128   }
129   return index;
130 }
131
132 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
133 {
134   if( index < DEFAULT_PROPERTY_COUNT )
135   {
136     return DEFAULT_PROPERTY_DETAILS[index].type;
137   }
138
139   // index out of range
140   return Property::NONE;
141 }
142
143 Property::Value Path::GetDefaultProperty( Property::Index index ) const
144 {
145   if( index == Dali::Path::Property::POINTS )
146   {
147     Property::Value value( Property::ARRAY );
148     Property::Array* array = value.GetArray();
149     Property::Array::SizeType pointCount = mPoint.Count();
150     array->Reserve( pointCount );
151     for( Property::Array::SizeType i = 0; i < pointCount; ++i )
152     {
153       array->PushBack( mPoint[i] );
154     }
155     return value;
156   }
157   else if( index == Dali::Path::Property::CONTROL_POINTS )
158   {
159     Property::Value value( Property::ARRAY );
160     Property::Array* array = value.GetArray();
161     Property::Array::SizeType  controlpointCount = mControlPoint.Count();
162     array->Reserve( controlpointCount );
163     for( Property::Array::SizeType i = 0; i < controlpointCount; ++i )
164     {
165       array->PushBack( mControlPoint[i] );
166     }
167     return value;
168   }
169
170   return Property::Value();
171 }
172
173 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
174 {
175   const Property::Array* array = propertyValue.GetArray();
176   if( array )
177   {
178     Property::Array::SizeType propertyArrayCount = array->Count();
179     if( index == Dali::Path::Property::POINTS )
180     {
181       mPoint.Reserve( propertyArrayCount );
182       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
183       {
184         Vector3 point;
185         array->GetElementAt( i ).Get( point );
186         mPoint.PushBack( point );
187       }
188     }
189     else if( index == Dali::Path::Property::CONTROL_POINTS )
190     {
191       mControlPoint.Reserve( propertyArrayCount );
192       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
193       {
194         Vector3 point;
195         array->GetElementAt( i ).Get( point );
196         mControlPoint.PushBack( point );
197       }
198     }
199   }
200 }
201
202 bool Path::IsDefaultPropertyWritable(Property::Index index) const
203 {
204   if( index < DEFAULT_PROPERTY_COUNT )
205   {
206     return DEFAULT_PROPERTY_DETAILS[index].writable;
207   }
208
209   return false;
210 }
211
212 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
213 {
214   if( index < DEFAULT_PROPERTY_COUNT )
215   {
216     return DEFAULT_PROPERTY_DETAILS[index].animatable;
217   }
218
219   return false;
220 }
221
222 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
223 {
224   if( index < DEFAULT_PROPERTY_COUNT )
225   {
226     return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
227   }
228
229   return false;
230 }
231
232 void Path::AddPoint(const Vector3& point )
233 {
234   mPoint.PushBack( point );
235 }
236
237 void Path::AddControlPoint(const Vector3& point )
238 {
239   mControlPoint.PushBack( point );
240 }
241
242 unsigned int Path::GetNumberOfSegments() const
243 {
244   return (mPoint.Size()>1)?mPoint.Size()-1:0;
245 }
246
247 void Path::GenerateControlPoints( float curvature )
248 {
249   unsigned int numSegments = GetNumberOfSegments();
250   DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
251
252   mControlPoint.Resize( numSegments * 2);
253
254   //Generate two control points for each segment
255   for( unsigned int i(0); i<numSegments; ++i )
256   {
257     //Segment end-points
258     Vector3 p1 = mPoint[i];
259     Vector3 p2 = mPoint[i+1];
260
261     Vector3 p0;
262     if( i == 0 )
263     {
264       //There's no previous point. We chose a point in the line defined by the two end points  at
265       //a 1/8th of the distance between them.
266       p0 = p1 - (p2 - p1)/8.0f;
267     }
268     else
269     {
270       //Previous point
271       p0 = mPoint[i-1];
272     }
273
274     Vector3 p3;
275     if( i == numSegments - 1)
276     {
277       //There's no next point. We chose a point in the line defined by the two end points  at
278       //a 1/8th of the distance between them.
279       p3 = p2 - (p1 - p2)/8.0f;
280     }
281     else
282     {
283       //Next point
284       p3 = mPoint[i+2];
285     }
286
287     Vector3 p0p1 = p1 - p0;
288     Vector3 p1p2 = p2 - p1;
289     Vector3 p2p3 = p3 - p2;
290
291     float length = p1p2.Length();
292
293     Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
294     tangentOut.Normalize();
295
296     Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
297     tangentIn.Normalize();
298
299     //Use curvature to scale the tangents
300     length *= curvature;
301     mControlPoint[2*i] =   p1 + tangentOut*length;
302     mControlPoint[2*i+1] = p2 - tangentIn*length;
303   }
304 }
305
306 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
307 {
308   //Find segment and local progress
309   unsigned int numSegs = GetNumberOfSegments();
310
311   if( t <= 0.0f )
312   {
313     segment = 0;
314     tLocal = 0.0f;
315   }
316   else if( t >= 1.0f )
317   {
318     segment = numSegs-1;
319     tLocal = 1.0f;
320   }
321   else
322   {
323     segment = t * numSegs;
324     float segLength = 1.0f / numSegs;
325     float segStart  = (float)segment * segLength;
326     tLocal = (t - segStart) * numSegs;
327   }
328 }
329
330 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
331 {
332   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
333
334   unsigned int segment;
335   float tLocal;
336   FindSegmentAndProgress( t, segment, tLocal );
337
338   //Get points and control points in the segment
339   const Vector3& controlPoint0 = mControlPoint[2*segment];
340   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
341   const Vector3& point0 = mPoint[segment];
342   const Vector3& point1 = mPoint[segment+1];
343
344   if(tLocal < Math::MACHINE_EPSILON_1)
345   {
346     position = point0;
347     tangent = ( controlPoint0 - point0 ) * 3.0f;
348     tangent.Normalize();
349   }
350   else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
351   {
352     position = point1;
353     tangent = ( point1 - controlPoint1 ) * 3.0f;
354     tangent.Normalize();
355   }
356   else
357   {
358     const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
359     const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
360
361     //X
362     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
363
364     Vector4 A = BezierBasis * cVect;
365     position.x = sVect.Dot4(A);
366     tangent.x  = sVectDerivative.Dot(Vector3(A));
367
368     //Y
369     cVect.x  = point0.y;
370     cVect.y  = controlPoint0.y;
371     cVect.z  = controlPoint1.y;
372     cVect.w  = point1.y;
373
374     A = BezierBasis * cVect;
375     position.y = sVect.Dot4(A);
376     tangent.y  = sVectDerivative.Dot(Vector3(A));
377
378     //Z
379     cVect.x  = point0.z;
380     cVect.y  = controlPoint0.z;
381     cVect.z  = controlPoint1.z;
382     cVect.w  = point1.z;
383
384     A = BezierBasis * cVect;
385     position.z = sVect.Dot4(A);
386     tangent.z  = sVectDerivative.Dot(Vector3(A));
387
388     tangent.Normalize();
389   }
390 }
391
392 Vector3 Path::SamplePosition( float t ) const
393 {
394   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
395
396   unsigned int segment;
397   float tLocal;
398   FindSegmentAndProgress( t, segment, tLocal );
399
400   const Vector3& controlPoint0 = mControlPoint[2*segment];
401   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
402   const Vector3& point0 = mPoint[segment];
403   const Vector3& point1 = mPoint[segment+1];
404
405   Vector3 position;
406   if(tLocal < Math::MACHINE_EPSILON_1)
407   {
408     position = point0;
409   }
410   else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
411   {
412     position = point1;
413   }
414   else
415   {
416     const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
417
418     //X
419     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
420     position.x = sVect.Dot4(BezierBasis * cVect);
421
422     //Y
423     cVect.x  = point0.y;
424     cVect.y  = controlPoint0.y;
425     cVect.z  = controlPoint1.y;
426     cVect.w  = point1.y;
427     position.y = sVect.Dot4(BezierBasis * cVect);
428
429     //Z
430     cVect.x  = point0.z;
431     cVect.y  = controlPoint0.z;
432     cVect.z  = controlPoint1.z;
433     cVect.w  = point1.z;
434     position.z = sVect.Dot4(BezierBasis * cVect);
435   }
436
437   return position;
438 }
439
440 Vector3 Path::SampleTangent( float t ) const
441 {
442   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
443
444   unsigned int segment;
445   float tLocal;
446   FindSegmentAndProgress( t, segment, tLocal );
447
448   const Vector3& controlPoint0 = mControlPoint[2*segment];
449   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
450   const Vector3& point0 = mPoint[segment];
451   const Vector3& point1 = mPoint[segment+1];
452
453   Vector3 tangent;
454   if(tLocal < Math::MACHINE_EPSILON_1)
455   {
456     tangent = ( controlPoint0 - point0 ) * 3.0f;
457   }
458   else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
459   {
460     tangent = ( point1 - controlPoint1 ) * 3.0f;
461   }
462   else
463   {
464     const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
465
466     //X
467     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
468     tangent.x  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
469
470     //Y
471     cVect.x  = point0.y;
472     cVect.y  = controlPoint0.y;
473     cVect.z  = controlPoint1.y;
474     cVect.w  = point1.y;
475     tangent.y  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
476
477     //Z
478     cVect.x  = point0.z;
479     cVect.y  = controlPoint0.z;
480     cVect.z  = controlPoint1.z;
481     cVect.w  = point1.z;
482     tangent.z  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
483   }
484
485   tangent.Normalize();
486   return tangent;
487 }
488
489 Vector3& Path::GetPoint( size_t index )
490 {
491   DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
492
493   return mPoint[index];
494 }
495
496 Vector3& Path::GetControlPoint( size_t index )
497 {
498   DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
499
500   return mControlPoint[index];
501 }
502
503 size_t Path::GetPointCount() const
504 {
505   return mPoint.Size();
506 }
507
508 void Path::ClearPoints()
509 {
510   mPoint.Clear();
511 }
512
513 void Path::ClearControlPoints()
514 {
515   mControlPoint.Clear();
516 }
517
518 } // Internal
519 } // Dali