1572caeeee26a56cc11f267f48e0dd6035d2cd33
[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   Property::Value value;
146   if( index == Dali::Path::Property::POINTS )
147   {
148     Property::Array propertyArray;
149     value = Property::Value(propertyArray);
150     size_t pointCount( mPoint.Size() );
151     for( size_t i( 0 ); i != pointCount; ++i )
152     {
153       value.AppendItem( mPoint[i] );
154     }
155   }
156   else if( index == Dali::Path::Property::CONTROL_POINTS )
157   {
158     Property::Array propertyArray;
159     value = Property::Value(propertyArray);
160     size_t controlpointCount( mControlPoint.Size() );
161     for( size_t i( 0 ); i != controlpointCount; ++i )
162     {
163       value.AppendItem( mControlPoint[i] );
164     }
165   }
166
167   return value;
168 }
169
170 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
171 {
172   if( index == Dali::Path::Property::POINTS )
173   {
174     Property::Array propertyArray;
175     propertyValue.Get(propertyArray);
176
177     size_t propertyArrayCount = propertyArray.Size();
178     mPoint.Resize( propertyArrayCount );
179     for( size_t i(0); i!=propertyArrayCount; ++i )
180     {
181       propertyArray[i].Get( mPoint[i]);
182     }
183   }
184   else if( index == Dali::Path::Property::CONTROL_POINTS )
185   {
186     Property::Array propertyArray;
187     propertyValue.Get(propertyArray);
188
189     size_t propertyArrayCount = propertyArray.Size();
190     mControlPoint.Resize( propertyArrayCount );
191     for( size_t i(0); i!=propertyArrayCount; ++i )
192     {
193       propertyArray[i].Get( mControlPoint[i]);
194     }
195   }
196 }
197
198 bool Path::IsDefaultPropertyWritable(Property::Index index) const
199 {
200   if( index < DEFAULT_PROPERTY_COUNT )
201   {
202     return DEFAULT_PROPERTY_DETAILS[index].writable;
203   }
204
205   return false;
206 }
207
208 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
209 {
210   if( index < DEFAULT_PROPERTY_COUNT )
211   {
212     return DEFAULT_PROPERTY_DETAILS[index].animatable;
213   }
214
215   return false;
216 }
217
218 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
219 {
220   if( index < DEFAULT_PROPERTY_COUNT )
221   {
222     return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
223   }
224
225   return false;
226 }
227
228 void Path::AddPoint(const Vector3& point )
229 {
230   mPoint.PushBack( point );
231 }
232
233 void Path::AddControlPoint(const Vector3& point )
234 {
235   mControlPoint.PushBack( point );
236 }
237
238 unsigned int Path::GetNumberOfSegments() const
239 {
240   return (mPoint.Size()>1)?mPoint.Size()-1:0;
241 }
242
243 void Path::GenerateControlPoints( float curvature )
244 {
245   unsigned int numSegments = GetNumberOfSegments();
246   DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
247
248   mControlPoint.Resize( numSegments * 2);
249
250   //Generate two control points for each segment
251   for( unsigned int i(0); i<numSegments; ++i )
252   {
253     //Segment end-points
254     Vector3 p1 = mPoint[i];
255     Vector3 p2 = mPoint[i+1];
256
257     Vector3 p0;
258     if( i == 0 )
259     {
260       //There's no previous point. We chose a point in the line defined by the two end points  at
261       //a 1/8th of the distance between them.
262       p0 = p1 - (p2 - p1)/8.0f;
263     }
264     else
265     {
266       //Previous point
267       p0 = mPoint[i-1];
268     }
269
270     Vector3 p3;
271     if( i == numSegments - 1)
272     {
273       //There's no next point. We chose a point in the line defined by the two end points  at
274       //a 1/8th of the distance between them.
275       p3 = p2 - (p1 - p2)/8.0f;
276     }
277     else
278     {
279       //Next point
280       p3 = mPoint[i+2];
281     }
282
283     Vector3 p0p1 = p1 - p0;
284     Vector3 p1p2 = p2 - p1;
285     Vector3 p2p3 = p3 - p2;
286
287     float length = p1p2.Length();
288
289     Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
290     tangentOut.Normalize();
291
292     Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
293     tangentIn.Normalize();
294
295     //Use curvature to scale the tangents
296     length *= curvature;
297     mControlPoint[2*i] =   p1 + tangentOut*length;
298     mControlPoint[2*i+1] = p2 - tangentIn*length;
299   }
300 }
301
302 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
303 {
304   //Find segment and local progress
305   unsigned int numSegs = GetNumberOfSegments();
306
307   if( t <= 0.0f )
308   {
309     segment = 0;
310     tLocal = 0.0f;
311   }
312   else if( t >= 1.0f )
313   {
314     segment = numSegs-1;
315     tLocal = 1.0f;
316   }
317   else
318   {
319     segment = t * numSegs;
320     float segLength = 1.0f / numSegs;
321     float segStart  = (float)segment * segLength;
322     tLocal = (t - segStart) * numSegs;
323   }
324 }
325
326 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
327 {
328   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
329
330   unsigned int segment;
331   float tLocal;
332   FindSegmentAndProgress( t, segment, tLocal );
333
334   //Get points and control points in the segment
335   const Vector3& controlPoint0 = mControlPoint[2*segment];
336   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
337   const Vector3& point0 = mPoint[segment];
338   const Vector3& point1 = mPoint[segment+1];
339
340   if(tLocal < Math::MACHINE_EPSILON_1)
341   {
342     position = point0;
343     tangent = ( controlPoint0 - point0 ) * 3.0f;
344     tangent.Normalize();
345   }
346   else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
347   {
348     position = point1;
349     tangent = ( point1 - controlPoint1 ) * 3.0f;
350     tangent.Normalize();
351   }
352   else
353   {
354     const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
355     const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
356
357     //X
358     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
359
360     Vector4 A = BezierBasis * cVect;
361     position.x = sVect.Dot4(A);
362     tangent.x  = sVectDerivative.Dot(Vector3(A));
363
364     //Y
365     cVect.x  = point0.y;
366     cVect.y  = controlPoint0.y;
367     cVect.z  = controlPoint1.y;
368     cVect.w  = point1.y;
369
370     A = BezierBasis * cVect;
371     position.y = sVect.Dot4(A);
372     tangent.y  = sVectDerivative.Dot(Vector3(A));
373
374     //Z
375     cVect.x  = point0.z;
376     cVect.y  = controlPoint0.z;
377     cVect.z  = controlPoint1.z;
378     cVect.w  = point1.z;
379
380     A = BezierBasis * cVect;
381     position.z = sVect.Dot4(A);
382     tangent.z  = sVectDerivative.Dot(Vector3(A));
383
384     tangent.Normalize();
385   }
386 }
387
388 Vector3 Path::SamplePosition( float t ) const
389 {
390   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
391
392   unsigned int segment;
393   float tLocal;
394   FindSegmentAndProgress( t, segment, tLocal );
395
396   const Vector3& controlPoint0 = mControlPoint[2*segment];
397   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
398   const Vector3& point0 = mPoint[segment];
399   const Vector3& point1 = mPoint[segment+1];
400
401   Vector3 position;
402   if(tLocal < Math::MACHINE_EPSILON_1)
403   {
404     position = point0;
405   }
406   else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
407   {
408     position = point1;
409   }
410   else
411   {
412     const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
413
414     //X
415     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
416     position.x = sVect.Dot4(BezierBasis * cVect);
417
418     //Y
419     cVect.x  = point0.y;
420     cVect.y  = controlPoint0.y;
421     cVect.z  = controlPoint1.y;
422     cVect.w  = point1.y;
423     position.y = sVect.Dot4(BezierBasis * cVect);
424
425     //Z
426     cVect.x  = point0.z;
427     cVect.y  = controlPoint0.z;
428     cVect.z  = controlPoint1.z;
429     cVect.w  = point1.z;
430     position.z = sVect.Dot4(BezierBasis * cVect);
431   }
432
433   return position;
434 }
435
436 Vector3 Path::SampleTangent( float t ) const
437 {
438   DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
439
440   unsigned int segment;
441   float tLocal;
442   FindSegmentAndProgress( t, segment, tLocal );
443
444   const Vector3& controlPoint0 = mControlPoint[2*segment];
445   const Vector3& controlPoint1 = mControlPoint[2*segment+1];
446   const Vector3& point0 = mPoint[segment];
447   const Vector3& point1 = mPoint[segment+1];
448
449   Vector3 tangent;
450   if(tLocal < Math::MACHINE_EPSILON_1)
451   {
452     tangent = ( controlPoint0 - point0 ) * 3.0f;
453   }
454   else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
455   {
456     tangent = ( point1 - controlPoint1 ) * 3.0f;
457   }
458   else
459   {
460     const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
461
462     //X
463     Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
464     tangent.x  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
465
466     //Y
467     cVect.x  = point0.y;
468     cVect.y  = controlPoint0.y;
469     cVect.z  = controlPoint1.y;
470     cVect.w  = point1.y;
471     tangent.y  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
472
473     //Z
474     cVect.x  = point0.z;
475     cVect.y  = controlPoint0.z;
476     cVect.z  = controlPoint1.z;
477     cVect.w  = point1.z;
478     tangent.z  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
479   }
480
481   tangent.Normalize();
482   return tangent;
483 }
484
485 Vector3& Path::GetPoint( size_t index )
486 {
487   DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
488
489   return mPoint[index];
490 }
491
492 Vector3& Path::GetControlPoint( size_t index )
493 {
494   DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
495
496   return mControlPoint[index];
497 }
498
499 size_t Path::GetPointCount() const
500 {
501   return mPoint.Size();
502 }
503
504 } // Internal
505 } // Dali