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