Making DALi core internals typesafe using guaranteed types; uint8_t, uint32_t
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / path-impl.cpp
1 /*
2  * Copyright (c) 2017 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 ( Property::Index 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( Property::Index 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 Property::Value Path::GetDefaultPropertyCurrentValue( Property::Index index ) const
195 {
196   return GetDefaultProperty( index ); // Event-side only properties
197 }
198
199 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
200 {
201   const Property::Array* array = propertyValue.GetArray();
202   if( array )
203   {
204     Property::Array::SizeType propertyArrayCount = array->Count();
205     if( index == Dali::Path::Property::POINTS )
206     {
207       mPoint.Reserve( propertyArrayCount );
208       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
209       {
210         Vector3 point;
211         array->GetElementAt( i ).Get( point );
212         mPoint.PushBack( point );
213       }
214     }
215     else if( index == Dali::Path::Property::CONTROL_POINTS )
216     {
217       mControlPoint.Reserve( propertyArrayCount );
218       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
219       {
220         Vector3 point;
221         array->GetElementAt( i ).Get( point );
222         mControlPoint.PushBack( point );
223       }
224     }
225   }
226 }
227
228 bool Path::IsDefaultPropertyWritable(Property::Index index) const
229 {
230   if( index < DEFAULT_PROPERTY_COUNT )
231   {
232     return DEFAULT_PROPERTY_DETAILS[index].writable;
233   }
234
235   return false;
236 }
237
238 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
239 {
240   if( index < DEFAULT_PROPERTY_COUNT )
241   {
242     return DEFAULT_PROPERTY_DETAILS[index].animatable;
243   }
244
245   return false;
246 }
247
248 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
249 {
250   if( index < DEFAULT_PROPERTY_COUNT )
251   {
252     return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
253   }
254
255   return false;
256 }
257
258 void Path::AddPoint(const Vector3& point )
259 {
260   mPoint.PushBack( point );
261 }
262
263 void Path::AddControlPoint(const Vector3& point )
264 {
265   mControlPoint.PushBack( point );
266 }
267
268 unsigned int Path::GetNumberOfSegments() const
269 {
270   return static_cast<unsigned int>( (mPoint.Size()>1) ? mPoint.Size()-1 : 0 );
271 }
272
273 void Path::GenerateControlPoints( float curvature )
274 {
275   unsigned int numSegments = GetNumberOfSegments();
276   DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
277
278   mControlPoint.Resize( numSegments * 2);
279
280   //Generate two control points for each segment
281   for( unsigned int i(0); i<numSegments; ++i )
282   {
283     //Segment end-points
284     Vector3 p1 = mPoint[i];
285     Vector3 p2 = mPoint[i+1];
286
287     Vector3 p0;
288     if( i == 0 )
289     {
290       //There's no previous point. We chose a point in the line defined by the two end points  at
291       //a 1/8th of the distance between them.
292       p0 = p1 - (p2 - p1)/8.0f;
293     }
294     else
295     {
296       //Previous point
297       p0 = mPoint[i-1];
298     }
299
300     Vector3 p3;
301     if( i == numSegments - 1)
302     {
303       //There's no next point. We chose a point in the line defined by the two end points  at
304       //a 1/8th of the distance between them.
305       p3 = p2 - (p1 - p2)/8.0f;
306     }
307     else
308     {
309       //Next point
310       p3 = mPoint[i+2];
311     }
312
313     Vector3 p0p1 = p1 - p0;
314     Vector3 p1p2 = p2 - p1;
315     Vector3 p2p3 = p3 - p2;
316
317     float length = p1p2.Length();
318
319     Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
320     tangentOut.Normalize();
321
322     Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
323     tangentIn.Normalize();
324
325     //Use curvature to scale the tangents
326     length *= curvature;
327     mControlPoint[2*i] =   p1 + tangentOut*length;
328     mControlPoint[2*i+1] = p2 - tangentIn*length;
329   }
330 }
331
332 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
333 {
334   //Find segment and local progress
335   unsigned int numSegs = GetNumberOfSegments();
336
337   if( t <= 0.0f || numSegs == 0 )
338   {
339     segment = 0;
340     tLocal = 0.0f;
341   }
342   else if( t >= 1.0f )
343   {
344     segment = numSegs-1;
345     tLocal = 1.0f;
346   }
347   else
348   {
349     segment = static_cast<unsigned int>( t * static_cast<float>( numSegs ) );
350     float segLength = 1.0f / static_cast<float>( numSegs );
351     float segStart  = static_cast<float>( segment ) * segLength;
352     tLocal = (t - segStart) * static_cast<float>( numSegs );
353   }
354 }
355
356 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
357 {
358   if( !SampleAt(t, position, tangent) )
359   {
360     DALI_ASSERT_ALWAYS(!"Spline not fully initialized" );
361   }
362 }
363
364 bool Path::SampleAt( float t, Vector3& position, Vector3& tangent ) const
365 {
366   bool done = false;
367
368   if( PathIsComplete(mPoint, mControlPoint) )
369   {
370     unsigned int segment;
371     float tLocal;
372     FindSegmentAndProgress( t, segment, tLocal );
373
374     //Get points and control points in the segment
375     const Vector3& controlPoint0 = mControlPoint[2*segment];
376     const Vector3& controlPoint1 = mControlPoint[2*segment+1];
377     const Vector3& point0 = mPoint[segment];
378     const Vector3& point1 = mPoint[segment+1];
379
380     if(tLocal < Math::MACHINE_EPSILON_1)
381     {
382       position = point0;
383       tangent = ( controlPoint0 - point0 ) * 3.0f;
384       tangent.Normalize();
385     }
386     else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
387     {
388       position = point1;
389       tangent = ( point1 - controlPoint1 ) * 3.0f;
390       tangent.Normalize();
391     }
392     else
393     {
394       const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
395       const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
396
397       //X
398       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
399
400       Vector4 A = BezierBasis * cVect;
401       position.x = sVect.Dot4(A);
402       tangent.x  = sVectDerivative.Dot(Vector3(A));
403
404       //Y
405       cVect.x  = point0.y;
406       cVect.y  = controlPoint0.y;
407       cVect.z  = controlPoint1.y;
408       cVect.w  = point1.y;
409
410       A = BezierBasis * cVect;
411       position.y = sVect.Dot4(A);
412       tangent.y  = sVectDerivative.Dot(Vector3(A));
413
414       //Z
415       cVect.x  = point0.z;
416       cVect.y  = controlPoint0.z;
417       cVect.z  = controlPoint1.z;
418       cVect.w  = point1.z;
419
420       A = BezierBasis * cVect;
421       position.z = sVect.Dot4(A);
422       tangent.z  = sVectDerivative.Dot(Vector3(A));
423
424       tangent.Normalize();
425     }
426
427     done = true;
428   }
429
430   return done;
431 }
432
433 bool Path::SamplePosition( float t, Vector3& position ) const
434 {
435   bool done = false;
436
437   if( PathIsComplete(mPoint, mControlPoint) )
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     if(tLocal < Math::MACHINE_EPSILON_1)
449     {
450       position = point0;
451     }
452     else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
453     {
454       position = point1;
455     }
456     else
457     {
458       const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
459
460       //X
461       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
462       position.x = sVect.Dot4(BezierBasis * cVect);
463
464       //Y
465       cVect.x  = point0.y;
466       cVect.y  = controlPoint0.y;
467       cVect.z  = controlPoint1.y;
468       cVect.w  = point1.y;
469       position.y = sVect.Dot4(BezierBasis * cVect);
470
471       //Z
472       cVect.x  = point0.z;
473       cVect.y  = controlPoint0.z;
474       cVect.z  = controlPoint1.z;
475       cVect.w  = point1.z;
476       position.z = sVect.Dot4(BezierBasis * cVect);
477     }
478
479     done = true;
480   }
481
482   return done;
483 }
484
485 bool Path::SampleTangent( float t, Vector3& tangent ) const
486 {
487   bool done = false;
488
489   if( PathIsComplete(mPoint, mControlPoint) )
490   {
491     unsigned int segment;
492     float tLocal;
493     FindSegmentAndProgress( t, segment, tLocal );
494
495     const Vector3& controlPoint0 = mControlPoint[2*segment];
496     const Vector3& controlPoint1 = mControlPoint[2*segment+1];
497     const Vector3& point0 = mPoint[segment];
498     const Vector3& point1 = mPoint[segment+1];
499
500     if(tLocal < Math::MACHINE_EPSILON_1)
501     {
502       tangent = ( controlPoint0 - point0 ) * 3.0f;
503     }
504     else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
505     {
506       tangent = ( point1 - controlPoint1 ) * 3.0f;
507     }
508     else
509     {
510       const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
511
512       //X
513       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
514       tangent.x  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
515
516       //Y
517       cVect.x  = point0.y;
518       cVect.y  = controlPoint0.y;
519       cVect.z  = controlPoint1.y;
520       cVect.w  = point1.y;
521       tangent.y  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
522
523       //Z
524       cVect.x  = point0.z;
525       cVect.y  = controlPoint0.z;
526       cVect.z  = controlPoint1.z;
527       cVect.w  = point1.z;
528       tangent.z  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
529     }
530
531     tangent.Normalize();
532     done = true;
533   }
534
535   return done;
536 }
537
538 Vector3& Path::GetPoint( size_t index )
539 {
540   DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
541
542   return mPoint[index];
543 }
544
545 Vector3& Path::GetControlPoint( size_t index )
546 {
547   DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
548
549   return mControlPoint[index];
550 }
551
552 size_t Path::GetPointCount() const
553 {
554   return mPoint.Size();
555 }
556
557 void Path::ClearPoints()
558 {
559   mPoint.Clear();
560 }
561
562 void Path::ClearControlPoints()
563 {
564   mControlPoint.Clear();
565 }
566
567 } // Internal
568 } // Dali