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