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