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