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