2 * Copyright (c) 2015 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>
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( "control-points", ARRAY, true, false, false, Dali::Path::Property::CONTROL_POINTS )
44 DALI_PROPERTY_TABLE_END( DEFAULT_OBJECT_PROPERTY_START_INDEX )
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 );
66 Dali::BaseHandle Create()
68 return Dali::Path::New();
71 Dali::TypeRegistration mType( typeid(Dali::Path), typeid(Dali::Handle), Create );
89 Path* Path::Clone(const Path& path)
91 Path* clone = new Path();
92 clone->SetPoints( path.GetPoints() );
93 clone->SetControlPoints( path.GetControlPoints() );
98 unsigned int Path::GetDefaultPropertyCount() const
100 return DEFAULT_PROPERTY_COUNT;
103 void Path::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
105 indices.Reserve( DEFAULT_PROPERTY_COUNT );
107 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
109 indices.PushBack( i );
113 const char* Path::GetDefaultPropertyName(Property::Index index) const
115 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
117 return DEFAULT_PROPERTY_DETAILS[index].name;
120 // index out of range
124 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
126 Property::Index index = Property::INVALID_INDEX;
128 // Look for name in default properties
129 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
131 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
132 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
141 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
143 if( index < DEFAULT_PROPERTY_COUNT )
145 return DEFAULT_PROPERTY_DETAILS[index].type;
148 // index out of range
149 return Property::NONE;
152 Property::Value Path::GetDefaultProperty( Property::Index index ) const
154 if( index == Dali::Path::Property::POINTS )
156 Property::Value value( Property::ARRAY );
157 Property::Array* array = value.GetArray();
158 Property::Array::SizeType pointCount = mPoint.Count();
162 array->Reserve( pointCount );
163 for( Property::Array::SizeType i = 0; i < pointCount; ++i )
165 array->PushBack( mPoint[i] );
170 else if( index == Dali::Path::Property::CONTROL_POINTS )
172 Property::Value value( Property::ARRAY );
173 Property::Array* array = value.GetArray();
174 Property::Array::SizeType controlpointCount = mControlPoint.Count();
178 array->Reserve( controlpointCount );
179 for( Property::Array::SizeType i = 0; i < controlpointCount; ++i )
181 array->PushBack( mControlPoint[i] );
187 return Property::Value();
190 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
192 const Property::Array* array = propertyValue.GetArray();
195 Property::Array::SizeType propertyArrayCount = array->Count();
196 if( index == Dali::Path::Property::POINTS )
198 mPoint.Reserve( propertyArrayCount );
199 for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
202 array->GetElementAt( i ).Get( point );
203 mPoint.PushBack( point );
206 else if( index == Dali::Path::Property::CONTROL_POINTS )
208 mControlPoint.Reserve( propertyArrayCount );
209 for( Property::Array::SizeType i = 0; i < propertyArrayCount; ++i )
212 array->GetElementAt( i ).Get( point );
213 mControlPoint.PushBack( point );
219 bool Path::IsDefaultPropertyWritable(Property::Index index) const
221 if( index < DEFAULT_PROPERTY_COUNT )
223 return DEFAULT_PROPERTY_DETAILS[index].writable;
229 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
231 if( index < DEFAULT_PROPERTY_COUNT )
233 return DEFAULT_PROPERTY_DETAILS[index].animatable;
239 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
241 if( index < DEFAULT_PROPERTY_COUNT )
243 return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
249 void Path::AddPoint(const Vector3& point )
251 mPoint.PushBack( point );
254 void Path::AddControlPoint(const Vector3& point )
256 mControlPoint.PushBack( point );
259 unsigned int Path::GetNumberOfSegments() const
261 return (mPoint.Size()>1)?mPoint.Size()-1:0;
264 void Path::GenerateControlPoints( float curvature )
266 unsigned int numSegments = GetNumberOfSegments();
267 DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
269 mControlPoint.Resize( numSegments * 2);
271 //Generate two control points for each segment
272 for( unsigned int i(0); i<numSegments; ++i )
275 Vector3 p1 = mPoint[i];
276 Vector3 p2 = mPoint[i+1];
281 //There's no previous point. We chose a point in the line defined by the two end points at
282 //a 1/8th of the distance between them.
283 p0 = p1 - (p2 - p1)/8.0f;
292 if( i == numSegments - 1)
294 //There's no next point. We chose a point in the line defined by the two end points at
295 //a 1/8th of the distance between them.
296 p3 = p2 - (p1 - p2)/8.0f;
304 Vector3 p0p1 = p1 - p0;
305 Vector3 p1p2 = p2 - p1;
306 Vector3 p2p3 = p3 - p2;
308 float length = p1p2.Length();
310 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
311 tangentOut.Normalize();
313 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
314 tangentIn.Normalize();
316 //Use curvature to scale the tangents
318 mControlPoint[2*i] = p1 + tangentOut*length;
319 mControlPoint[2*i+1] = p2 - tangentIn*length;
323 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
325 //Find segment and local progress
326 unsigned int numSegs = GetNumberOfSegments();
328 if( t <= 0.0f || numSegs == 0 )
340 segment = t * numSegs;
341 float segLength = 1.0f / numSegs;
342 float segStart = (float)segment * segLength;
343 tLocal = (t - segStart) * numSegs;
347 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
349 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
351 unsigned int segment;
353 FindSegmentAndProgress( t, segment, tLocal );
355 //Get points and control points in the segment
356 const Vector3& controlPoint0 = mControlPoint[2*segment];
357 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
358 const Vector3& point0 = mPoint[segment];
359 const Vector3& point1 = mPoint[segment+1];
361 if(tLocal < Math::MACHINE_EPSILON_1)
364 tangent = ( controlPoint0 - point0 ) * 3.0f;
367 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
370 tangent = ( point1 - controlPoint1 ) * 3.0f;
375 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
376 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
379 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
381 Vector4 A = BezierBasis * cVect;
382 position.x = sVect.Dot4(A);
383 tangent.x = sVectDerivative.Dot(Vector3(A));
387 cVect.y = controlPoint0.y;
388 cVect.z = controlPoint1.y;
391 A = BezierBasis * cVect;
392 position.y = sVect.Dot4(A);
393 tangent.y = sVectDerivative.Dot(Vector3(A));
397 cVect.y = controlPoint0.z;
398 cVect.z = controlPoint1.z;
401 A = BezierBasis * cVect;
402 position.z = sVect.Dot4(A);
403 tangent.z = sVectDerivative.Dot(Vector3(A));
409 Vector3 Path::SamplePosition( float t ) const
411 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
413 unsigned int segment;
415 FindSegmentAndProgress( t, segment, tLocal );
417 const Vector3& controlPoint0 = mControlPoint[2*segment];
418 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
419 const Vector3& point0 = mPoint[segment];
420 const Vector3& point1 = mPoint[segment+1];
423 if(tLocal < Math::MACHINE_EPSILON_1)
427 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
433 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
436 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
437 position.x = sVect.Dot4(BezierBasis * cVect);
441 cVect.y = controlPoint0.y;
442 cVect.z = controlPoint1.y;
444 position.y = sVect.Dot4(BezierBasis * cVect);
448 cVect.y = controlPoint0.z;
449 cVect.z = controlPoint1.z;
451 position.z = sVect.Dot4(BezierBasis * cVect);
457 Vector3 Path::SampleTangent( float t ) const
459 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
461 unsigned int segment;
463 FindSegmentAndProgress( t, segment, tLocal );
465 const Vector3& controlPoint0 = mControlPoint[2*segment];
466 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
467 const Vector3& point0 = mPoint[segment];
468 const Vector3& point1 = mPoint[segment+1];
471 if(tLocal < Math::MACHINE_EPSILON_1)
473 tangent = ( controlPoint0 - point0 ) * 3.0f;
475 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
477 tangent = ( point1 - controlPoint1 ) * 3.0f;
481 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
484 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
485 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
489 cVect.y = controlPoint0.y;
490 cVect.z = controlPoint1.y;
492 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
496 cVect.y = controlPoint0.z;
497 cVect.z = controlPoint1.z;
499 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
506 Vector3& Path::GetPoint( size_t index )
508 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
510 return mPoint[index];
513 Vector3& Path::GetControlPoint( size_t index )
515 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
517 return mControlPoint[index];
520 size_t Path::GetPointCount() const
522 return mPoint.Size();
525 void Path::ClearPoints()
530 void Path::ClearControlPoints()
532 mControlPoint.Clear();