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