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