2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/animation/path-impl.h>
22 #include <cstring> // for strcmp
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>
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)
44 * These coefficient arise from the cubic polynomial equations for
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:
52 * f'(0) = 3*(cp0 - p0)
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};
57 const Dali::Matrix BezierBasis = Dali::Matrix(BezierBasisCoeff);
59 Dali::BaseHandle Create()
61 return Dali::Path::New();
64 TypeRegistration mType(typeid(Dali::Path), typeid(Dali::Handle), Create, PathDefaultProperties);
66 inline bool PathIsComplete(const Dali::Vector<Vector3>& point, const Dali::Vector<Vector3>& controlPoint)
68 return (point.Size() > 1 && controlPoint.Size() == (point.Size() - 1) * 2);
79 : Object(nullptr) // we don't have our own scene object
83 Path::~Path() = default;
85 Path* Path::Clone(const Path& path)
87 Path* clone = new Path();
88 clone->SetPoints(path.GetPoints());
89 clone->SetControlPoints(path.GetControlPoints());
94 Property::Value Path::GetDefaultProperty(Property::Index index) const
96 if(index == Dali::Path::Property::POINTS)
98 Property::Value value(Property::ARRAY);
99 Property::Array* array = value.GetArray();
100 Property::Array::SizeType pointCount = mPoint.Count();
104 array->Reserve(pointCount);
105 for(Property::Array::SizeType i = 0; i < pointCount; ++i)
107 array->PushBack(mPoint[i]);
112 else if(index == Dali::Path::Property::CONTROL_POINTS)
114 Property::Value value(Property::ARRAY);
115 Property::Array* array = value.GetArray();
116 Property::Array::SizeType controlpointCount = mControlPoint.Count();
120 array->Reserve(controlpointCount);
121 for(Property::Array::SizeType i = 0; i < controlpointCount; ++i)
123 array->PushBack(mControlPoint[i]);
129 return Property::Value();
132 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
134 const Property::Array* array = propertyValue.GetArray();
137 Property::Array::SizeType propertyArrayCount = array->Count();
138 if(index == Dali::Path::Property::POINTS)
140 mPoint.Reserve(propertyArrayCount);
141 for(Property::Array::SizeType i = 0; i < propertyArrayCount; ++i)
144 array->GetElementAt(i).Get(point);
145 mPoint.PushBack(point);
148 else if(index == Dali::Path::Property::CONTROL_POINTS)
150 mControlPoint.Reserve(propertyArrayCount);
151 for(Property::Array::SizeType i = 0; i < propertyArrayCount; ++i)
154 array->GetElementAt(i).Get(point);
155 mControlPoint.PushBack(point);
161 void Path::AddPoint(const Vector3& point)
163 mPoint.PushBack(point);
166 void Path::AddControlPoint(const Vector3& point)
168 mControlPoint.PushBack(point);
171 uint32_t Path::GetNumberOfSegments() const
173 return static_cast<uint32_t>((mPoint.Size() > 1) ? mPoint.Size() - 1 : 0);
176 void Path::GenerateControlPoints(float curvature)
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
181 mControlPoint.Resize(numSegments * 2);
183 //Generate two control points for each segment
184 for(uint32_t i(0); i < numSegments; ++i)
187 Vector3 p1 = mPoint[i];
188 Vector3 p2 = mPoint[i + 1];
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;
204 if(i == numSegments - 1)
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;
216 Vector3 p0p1 = p1 - p0;
217 Vector3 p1p2 = p2 - p1;
218 Vector3 p2p3 = p3 - p2;
220 float length = p1p2.Length();
222 Vector3 tangentOut = (p0p1 * length + p1p2 * p0p1.Length()) * 0.5f;
223 tangentOut.Normalize();
225 Vector3 tangentIn = (p1p2 * p2p3.Length() + p2p3 * length) * 0.5f;
226 tangentIn.Normalize();
228 //Use curvature to scale the tangents
230 mControlPoint[2 * i] = p1 + tangentOut * length;
231 mControlPoint[2 * i + 1] = p2 - tangentIn * length;
235 void Path::FindSegmentAndProgress(float t, uint32_t& segment, float& tLocal) const
237 //Find segment and local progress
238 uint32_t numSegs = GetNumberOfSegments();
240 if(t <= 0.0f || numSegs == 0)
247 segment = numSegs - 1;
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);
259 void Path::Sample(float t, Vector3& position, Vector3& tangent) const
261 if(!SampleAt(t, position, tangent))
263 DALI_ASSERT_ALWAYS(!"Spline not fully initialized");
267 bool Path::SampleAt(float t, Vector3& position, Vector3& tangent) const
271 if(PathIsComplete(mPoint, mControlPoint))
275 FindSegmentAndProgress(t, segment, tLocal);
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];
283 if(tLocal < Math::MACHINE_EPSILON_1)
286 tangent = (controlPoint0 - point0) * 3.0f;
289 else if((1.0 - tLocal) < Math::MACHINE_EPSILON_1)
292 tangent = (point1 - controlPoint1) * 3.0f;
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);
301 Vector4 cVect(point0.x, controlPoint0.x, controlPoint1.x, point1.x);
303 Vector4 A = BezierBasis * cVect;
304 position.x = sVect.Dot4(A);
305 tangent.x = sVectDerivative.Dot(Vector3(A));
309 cVect.y = controlPoint0.y;
310 cVect.z = controlPoint1.y;
313 A = BezierBasis * cVect;
314 position.y = sVect.Dot4(A);
315 tangent.y = sVectDerivative.Dot(Vector3(A));
319 cVect.y = controlPoint0.z;
320 cVect.z = controlPoint1.z;
323 A = BezierBasis * cVect;
324 position.z = sVect.Dot4(A);
325 tangent.z = sVectDerivative.Dot(Vector3(A));
336 bool Path::SamplePosition(float t, Vector3& position) const
340 if(PathIsComplete(mPoint, mControlPoint))
344 FindSegmentAndProgress(t, segment, tLocal);
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];
351 if(tLocal < Math::MACHINE_EPSILON_1)
355 else if((1.0 - tLocal) < Math::MACHINE_EPSILON_1)
361 const Vector4 sVect(tLocal * tLocal * tLocal, tLocal * tLocal, tLocal, 1.0f);
364 Vector4 cVect(point0.x, controlPoint0.x, controlPoint1.x, point1.x);
365 position.x = sVect.Dot4(BezierBasis * cVect);
369 cVect.y = controlPoint0.y;
370 cVect.z = controlPoint1.y;
372 position.y = sVect.Dot4(BezierBasis * cVect);
376 cVect.y = controlPoint0.z;
377 cVect.z = controlPoint1.z;
379 position.z = sVect.Dot4(BezierBasis * cVect);
388 bool Path::SampleTangent(float t, Vector3& tangent) const
392 if(PathIsComplete(mPoint, mControlPoint))
396 FindSegmentAndProgress(t, segment, tLocal);
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];
403 if(tLocal < Math::MACHINE_EPSILON_1)
405 tangent = (controlPoint0 - point0) * 3.0f;
407 else if((1.0f - tLocal) < Math::MACHINE_EPSILON_1)
409 tangent = (point1 - controlPoint1) * 3.0f;
413 const Vector3 sVectDerivative(3.0f * tLocal * tLocal, 2.0f * tLocal, 1.0f);
416 Vector4 cVect(point0.x, controlPoint0.x, controlPoint1.x, point1.x);
417 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
421 cVect.y = controlPoint0.y;
422 cVect.z = controlPoint1.y;
424 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
428 cVect.y = controlPoint0.z;
429 cVect.z = controlPoint1.z;
431 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
441 Vector3& Path::GetPoint(uint32_t index)
443 DALI_ASSERT_ALWAYS(index < mPoint.Size() && "Path: Point index out of bounds");
445 return mPoint[index];
448 Vector3& Path::GetControlPoint(uint32_t index)
450 DALI_ASSERT_ALWAYS(index < mControlPoint.Size() && "Path: Control Point index out of bounds");
452 return mControlPoint[index];
455 uint32_t Path::GetPointCount() const
457 return static_cast<uint32_t>(mPoint.Size());
460 void Path::ClearPoints()
465 void Path::ClearControlPoints()
467 mControlPoint.Clear();
470 } // namespace Internal