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