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