Merge "Clean up the code to build successfully on macOS" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / animation / path-impl.cpp
1 /*
2  * Copyright (c) 2018 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/public-api/object/property-array.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/internal/event/common/property-helper.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, PathDefaultProperties )
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 TypeRegistration mType( typeid(Dali::Path), typeid(Dali::Handle), Create, PathDefaultProperties );
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( nullptr ) // we don't have our own scene object
86 {
87 }
88
89 Path::~Path() = default;
90
91 Path* Path::Clone(const Path& path)
92 {
93   Path* clone = new Path();
94   clone->SetPoints( path.GetPoints() );
95   clone->SetControlPoints( path.GetControlPoints() );
96
97   return clone;
98 }
99
100 Property::Value Path::GetDefaultProperty( Property::Index index ) const
101 {
102   if( index == Dali::Path::Property::POINTS )
103   {
104     Property::Value value( Property::ARRAY );
105     Property::Array* array = value.GetArray();
106     Property::Array::SizeType pointCount = mPoint.Count();
107
108     if( array )
109     {
110       array->Reserve( pointCount );
111       for( Property::Array::SizeType i = 0; i < pointCount; ++i )
112       {
113         array->PushBack( mPoint[i] );
114       }
115     }
116     return value;
117   }
118   else if( index == Dali::Path::Property::CONTROL_POINTS )
119   {
120     Property::Value value( Property::ARRAY );
121     Property::Array* array = value.GetArray();
122     Property::Array::SizeType  controlpointCount = mControlPoint.Count();
123
124     if( array )
125     {
126       array->Reserve( controlpointCount );
127       for( Property::Array::SizeType i = 0; i < controlpointCount; ++i )
128       {
129         array->PushBack( mControlPoint[i] );
130       }
131     }
132     return value;
133   }
134
135   return Property::Value();
136 }
137
138 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
139 {
140   const Property::Array* array = propertyValue.GetArray();
141   if( array )
142   {
143     Property::Array::SizeType propertyArrayCount = array->Count();
144     if( index == Dali::Path::Property::POINTS )
145     {
146       mPoint.Reserve( propertyArrayCount );
147       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
148       {
149         Vector3 point;
150         array->GetElementAt( i ).Get( point );
151         mPoint.PushBack( point );
152       }
153     }
154     else if( index == Dali::Path::Property::CONTROL_POINTS )
155     {
156       mControlPoint.Reserve( propertyArrayCount );
157       for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
158       {
159         Vector3 point;
160         array->GetElementAt( i ).Get( point );
161         mControlPoint.PushBack( point );
162       }
163     }
164   }
165 }
166
167 void Path::AddPoint(const Vector3& point )
168 {
169   mPoint.PushBack( point );
170 }
171
172 void Path::AddControlPoint(const Vector3& point )
173 {
174   mControlPoint.PushBack( point );
175 }
176
177 uint32_t Path::GetNumberOfSegments() const
178 {
179   return static_cast<uint32_t>( (mPoint.Size()>1) ? mPoint.Size()-1 : 0 );
180 }
181
182 void Path::GenerateControlPoints( float curvature )
183 {
184   uint32_t numSegments = GetNumberOfSegments();
185   DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
186
187   mControlPoint.Resize( numSegments * 2);
188
189   //Generate two control points for each segment
190   for( uint32_t i(0); i<numSegments; ++i )
191   {
192     //Segment end-points
193     Vector3 p1 = mPoint[i];
194     Vector3 p2 = mPoint[i+1];
195
196     Vector3 p0;
197     if( i == 0 )
198     {
199       //There's no previous point. We chose a point in the line defined by the two end points  at
200       //a 1/8th of the distance between them.
201       p0 = p1 - (p2 - p1)/8.0f;
202     }
203     else
204     {
205       //Previous point
206       p0 = mPoint[i-1];
207     }
208
209     Vector3 p3;
210     if( i == numSegments - 1)
211     {
212       //There's no next point. We chose a point in the line defined by the two end points  at
213       //a 1/8th of the distance between them.
214       p3 = p2 - (p1 - p2)/8.0f;
215     }
216     else
217     {
218       //Next point
219       p3 = mPoint[i+2];
220     }
221
222     Vector3 p0p1 = p1 - p0;
223     Vector3 p1p2 = p2 - p1;
224     Vector3 p2p3 = p3 - p2;
225
226     float length = p1p2.Length();
227
228     Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
229     tangentOut.Normalize();
230
231     Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
232     tangentIn.Normalize();
233
234     //Use curvature to scale the tangents
235     length *= curvature;
236     mControlPoint[2*i] =   p1 + tangentOut*length;
237     mControlPoint[2*i+1] = p2 - tangentIn*length;
238   }
239 }
240
241 void Path::FindSegmentAndProgress( float t, uint32_t& segment, float& tLocal ) const
242 {
243   //Find segment and local progress
244   uint32_t numSegs = GetNumberOfSegments();
245
246   if( t <= 0.0f || numSegs == 0 )
247   {
248     segment = 0;
249     tLocal = 0.0f;
250   }
251   else if( t >= 1.0f )
252   {
253     segment = numSegs-1;
254     tLocal = 1.0f;
255   }
256   else
257   {
258     segment = static_cast<uint32_t>( t * static_cast<float>( numSegs ) );
259     float segLength = 1.0f / static_cast<float>( numSegs );
260     float segStart  = static_cast<float>( segment ) * segLength;
261     tLocal = (t - segStart) * static_cast<float>( numSegs );
262   }
263 }
264
265 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
266 {
267   if( !SampleAt(t, position, tangent) )
268   {
269     DALI_ASSERT_ALWAYS(!"Spline not fully initialized" );
270   }
271 }
272
273 bool Path::SampleAt( float t, Vector3& position, Vector3& tangent ) const
274 {
275   bool done = false;
276
277   if( PathIsComplete(mPoint, mControlPoint) )
278   {
279     uint32_t segment;
280     float tLocal;
281     FindSegmentAndProgress( t, segment, tLocal );
282
283     //Get points and control points in the segment
284     const Vector3& controlPoint0 = mControlPoint[2*segment];
285     const Vector3& controlPoint1 = mControlPoint[2*segment+1];
286     const Vector3& point0 = mPoint[segment];
287     const Vector3& point1 = mPoint[segment+1];
288
289     if(tLocal < Math::MACHINE_EPSILON_1)
290     {
291       position = point0;
292       tangent = ( controlPoint0 - point0 ) * 3.0f;
293       tangent.Normalize();
294     }
295     else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
296     {
297       position = point1;
298       tangent = ( point1 - controlPoint1 ) * 3.0f;
299       tangent.Normalize();
300     }
301     else
302     {
303       const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
304       const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
305
306       //X
307       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
308
309       Vector4 A = BezierBasis * cVect;
310       position.x = sVect.Dot4(A);
311       tangent.x  = sVectDerivative.Dot(Vector3(A));
312
313       //Y
314       cVect.x  = point0.y;
315       cVect.y  = controlPoint0.y;
316       cVect.z  = controlPoint1.y;
317       cVect.w  = point1.y;
318
319       A = BezierBasis * cVect;
320       position.y = sVect.Dot4(A);
321       tangent.y  = sVectDerivative.Dot(Vector3(A));
322
323       //Z
324       cVect.x  = point0.z;
325       cVect.y  = controlPoint0.z;
326       cVect.z  = controlPoint1.z;
327       cVect.w  = point1.z;
328
329       A = BezierBasis * cVect;
330       position.z = sVect.Dot4(A);
331       tangent.z  = sVectDerivative.Dot(Vector3(A));
332
333       tangent.Normalize();
334     }
335
336     done = true;
337   }
338
339   return done;
340 }
341
342 bool Path::SamplePosition( float t, Vector3& position ) const
343 {
344   bool done = false;
345
346   if( PathIsComplete(mPoint, mControlPoint) )
347   {
348     uint32_t segment;
349     float tLocal;
350     FindSegmentAndProgress( t, segment, tLocal );
351
352     const Vector3& controlPoint0 = mControlPoint[2*segment];
353     const Vector3& controlPoint1 = mControlPoint[2*segment+1];
354     const Vector3& point0 = mPoint[segment];
355     const Vector3& point1 = mPoint[segment+1];
356
357     if(tLocal < Math::MACHINE_EPSILON_1)
358     {
359       position = point0;
360     }
361     else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
362     {
363       position = point1;
364     }
365     else
366     {
367       const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
368
369       //X
370       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
371       position.x = sVect.Dot4(BezierBasis * cVect);
372
373       //Y
374       cVect.x  = point0.y;
375       cVect.y  = controlPoint0.y;
376       cVect.z  = controlPoint1.y;
377       cVect.w  = point1.y;
378       position.y = sVect.Dot4(BezierBasis * cVect);
379
380       //Z
381       cVect.x  = point0.z;
382       cVect.y  = controlPoint0.z;
383       cVect.z  = controlPoint1.z;
384       cVect.w  = point1.z;
385       position.z = sVect.Dot4(BezierBasis * cVect);
386     }
387
388     done = true;
389   }
390
391   return done;
392 }
393
394 bool Path::SampleTangent( float t, Vector3& tangent ) const
395 {
396   bool done = false;
397
398   if( PathIsComplete(mPoint, mControlPoint) )
399   {
400     uint32_t segment;
401     float tLocal;
402     FindSegmentAndProgress( t, segment, tLocal );
403
404     const Vector3& controlPoint0 = mControlPoint[2*segment];
405     const Vector3& controlPoint1 = mControlPoint[2*segment+1];
406     const Vector3& point0 = mPoint[segment];
407     const Vector3& point1 = mPoint[segment+1];
408
409     if(tLocal < Math::MACHINE_EPSILON_1)
410     {
411       tangent = ( controlPoint0 - point0 ) * 3.0f;
412     }
413     else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
414     {
415       tangent = ( point1 - controlPoint1 ) * 3.0f;
416     }
417     else
418     {
419       const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
420
421       //X
422       Vector4  cVect( point0.x, controlPoint0.x, controlPoint1.x,  point1.x);
423       tangent.x  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
424
425       //Y
426       cVect.x  = point0.y;
427       cVect.y  = controlPoint0.y;
428       cVect.z  = controlPoint1.y;
429       cVect.w  = point1.y;
430       tangent.y  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
431
432       //Z
433       cVect.x  = point0.z;
434       cVect.y  = controlPoint0.z;
435       cVect.z  = controlPoint1.z;
436       cVect.w  = point1.z;
437       tangent.z  = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
438     }
439
440     tangent.Normalize();
441     done = true;
442   }
443
444   return done;
445 }
446
447 Vector3& Path::GetPoint( uint32_t index )
448 {
449   DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
450
451   return mPoint[index];
452 }
453
454 Vector3& Path::GetControlPoint( uint32_t index )
455 {
456   DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
457
458   return mControlPoint[index];
459 }
460
461 uint32_t Path::GetPointCount() const
462 {
463   return static_cast<uint32_t>( mPoint.Size() );
464 }
465
466 void Path::ClearPoints()
467 {
468   mPoint.Clear();
469 }
470
471 void Path::ClearControlPoints()
472 {
473   mControlPoint.Clear();
474 }
475
476 } // Internal
477 } // Dali