2 * Copyright (c) 2018 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/public-api/object/property-array.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/internal/event/common/property-helper.h>
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 )
47 * These coefficient arise from the cubic polynomial equations for
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:
55 * f'(0) = 3*(cp0 - p0)
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 };
63 const Dali::Matrix BezierBasis = Dali::Matrix( BezierBasisCoeff );
65 Dali::BaseHandle Create()
67 return Dali::Path::New();
70 TypeRegistration mType( typeid(Dali::Path), typeid(Dali::Handle), Create, PathDefaultProperties );
72 inline bool PathIsComplete(const Dali::Vector<Vector3>& point, const Dali::Vector<Vector3>& controlPoint)
74 return ( point.Size() > 1 && controlPoint.Size() == (point.Size()-1)*2 );
85 : Object( nullptr ) // we don't have our own scene object
89 Path::~Path() = default;
91 Path* Path::Clone(const Path& path)
93 Path* clone = new Path();
94 clone->SetPoints( path.GetPoints() );
95 clone->SetControlPoints( path.GetControlPoints() );
100 Property::Value Path::GetDefaultProperty( Property::Index index ) const
102 if( index == Dali::Path::Property::POINTS )
104 Property::Value value( Property::ARRAY );
105 Property::Array* array = value.GetArray();
106 Property::Array::SizeType pointCount = mPoint.Count();
110 array->Reserve( pointCount );
111 for( Property::Array::SizeType i = 0; i < pointCount; ++i )
113 array->PushBack( mPoint[i] );
118 else if( index == Dali::Path::Property::CONTROL_POINTS )
120 Property::Value value( Property::ARRAY );
121 Property::Array* array = value.GetArray();
122 Property::Array::SizeType controlpointCount = mControlPoint.Count();
126 array->Reserve( controlpointCount );
127 for( Property::Array::SizeType i = 0; i < controlpointCount; ++i )
129 array->PushBack( mControlPoint[i] );
135 return Property::Value();
138 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
140 const Property::Array* array = propertyValue.GetArray();
143 Property::Array::SizeType propertyArrayCount = array->Count();
144 if( index == Dali::Path::Property::POINTS )
146 mPoint.Reserve( propertyArrayCount );
147 for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
150 array->GetElementAt( i ).Get( point );
151 mPoint.PushBack( point );
154 else if( index == Dali::Path::Property::CONTROL_POINTS )
156 mControlPoint.Reserve( propertyArrayCount );
157 for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
160 array->GetElementAt( i ).Get( point );
161 mControlPoint.PushBack( point );
167 void Path::AddPoint(const Vector3& point )
169 mPoint.PushBack( point );
172 void Path::AddControlPoint(const Vector3& point )
174 mControlPoint.PushBack( point );
177 uint32_t Path::GetNumberOfSegments() const
179 return static_cast<uint32_t>( (mPoint.Size()>1) ? mPoint.Size()-1 : 0 );
182 void Path::GenerateControlPoints( float curvature )
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
187 mControlPoint.Resize( numSegments * 2);
189 //Generate two control points for each segment
190 for( uint32_t i(0); i<numSegments; ++i )
193 Vector3 p1 = mPoint[i];
194 Vector3 p2 = mPoint[i+1];
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;
210 if( i == numSegments - 1)
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;
222 Vector3 p0p1 = p1 - p0;
223 Vector3 p1p2 = p2 - p1;
224 Vector3 p2p3 = p3 - p2;
226 float length = p1p2.Length();
228 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
229 tangentOut.Normalize();
231 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
232 tangentIn.Normalize();
234 //Use curvature to scale the tangents
236 mControlPoint[2*i] = p1 + tangentOut*length;
237 mControlPoint[2*i+1] = p2 - tangentIn*length;
241 void Path::FindSegmentAndProgress( float t, uint32_t& segment, float& tLocal ) const
243 //Find segment and local progress
244 uint32_t numSegs = GetNumberOfSegments();
246 if( t <= 0.0f || numSegs == 0 )
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 );
265 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
267 if( !SampleAt(t, position, tangent) )
269 DALI_ASSERT_ALWAYS(!"Spline not fully initialized" );
273 bool Path::SampleAt( float t, Vector3& position, Vector3& tangent ) const
277 if( PathIsComplete(mPoint, mControlPoint) )
281 FindSegmentAndProgress( t, segment, tLocal );
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];
289 if(tLocal < Math::MACHINE_EPSILON_1)
292 tangent = ( controlPoint0 - point0 ) * 3.0f;
295 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
298 tangent = ( point1 - controlPoint1 ) * 3.0f;
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 );
307 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
309 Vector4 A = BezierBasis * cVect;
310 position.x = sVect.Dot4(A);
311 tangent.x = sVectDerivative.Dot(Vector3(A));
315 cVect.y = controlPoint0.y;
316 cVect.z = controlPoint1.y;
319 A = BezierBasis * cVect;
320 position.y = sVect.Dot4(A);
321 tangent.y = sVectDerivative.Dot(Vector3(A));
325 cVect.y = controlPoint0.z;
326 cVect.z = controlPoint1.z;
329 A = BezierBasis * cVect;
330 position.z = sVect.Dot4(A);
331 tangent.z = sVectDerivative.Dot(Vector3(A));
342 bool Path::SamplePosition( float t, Vector3& position ) const
346 if( PathIsComplete(mPoint, mControlPoint) )
350 FindSegmentAndProgress( t, segment, tLocal );
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];
357 if(tLocal < Math::MACHINE_EPSILON_1)
361 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
367 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
370 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
371 position.x = sVect.Dot4(BezierBasis * cVect);
375 cVect.y = controlPoint0.y;
376 cVect.z = controlPoint1.y;
378 position.y = sVect.Dot4(BezierBasis * cVect);
382 cVect.y = controlPoint0.z;
383 cVect.z = controlPoint1.z;
385 position.z = sVect.Dot4(BezierBasis * cVect);
394 bool Path::SampleTangent( float t, Vector3& tangent ) const
398 if( PathIsComplete(mPoint, mControlPoint) )
402 FindSegmentAndProgress( t, segment, tLocal );
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];
409 if(tLocal < Math::MACHINE_EPSILON_1)
411 tangent = ( controlPoint0 - point0 ) * 3.0f;
413 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
415 tangent = ( point1 - controlPoint1 ) * 3.0f;
419 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
422 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
423 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
427 cVect.y = controlPoint0.y;
428 cVect.z = controlPoint1.y;
430 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
434 cVect.y = controlPoint0.z;
435 cVect.z = controlPoint1.z;
437 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
447 Vector3& Path::GetPoint( uint32_t index )
449 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
451 return mPoint[index];
454 Vector3& Path::GetControlPoint( uint32_t index )
456 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
458 return mControlPoint[index];
461 uint32_t Path::GetPointCount() const
463 return static_cast<uint32_t>( mPoint.Size() );
466 void Path::ClearPoints()
471 void Path::ClearControlPoints()
473 mControlPoint.Clear();