2 * Copyright (c) 2014 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>
20 #include <dali/internal/event/common/property-helper.h>
33 // Name Type writable animatable constraint-input enum for index-checking
34 DALI_PROPERTY_TABLE_BEGIN
35 DALI_PROPERTY( "points", ARRAY, true, false, false, Dali::Path::Property::Points )
36 DALI_PROPERTY( "control-points", ARRAY, true, false, false, Dali::Path::Property::ControlPoints )
37 DALI_PROPERTY_TABLE_END( DEFAULT_DERIVED_HANDLE_PROPERTY_START_INDEX )
40 * These coefficient arise from the cubic polynomial equations for
43 * A bezier curve is defined by a cubic polynomial. Given two end points p0 and p1
44 * and two control points cp0 and cp1, the bezier curve will be defined by a polynomial in the form
45 * f(x) = a3*x^3 + a2*x^2 + a1*x + a0 with this restrictions:
48 * f'(0) = 3*(cp0 - p0)
51 const float BezierBasisCoeff[] = { -1.0f, 3.0f, -3.0f, 1.0f,
52 3.0f, -6.0f, 3.0f, 0.0f,
53 -3.0f, 3.0f, 0.0f, 0.0f,
54 1.0f, 0.0f, 0.0f, 0.0f };
56 const Dali::Matrix BezierBasis = Dali::Matrix( BezierBasisCoeff );
74 Path* Path::Clone(const Path& path)
76 Path* clone = new Path();
77 clone->SetPoints( path.GetPoints() );
78 clone->SetControlPoints( path.GetControlPoints() );
83 unsigned int Path::GetDefaultPropertyCount() const
85 return DEFAULT_PROPERTY_COUNT;
88 void Path::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
90 indices.reserve( DEFAULT_PROPERTY_COUNT );
92 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
94 indices.push_back( i );
98 const char* Path::GetDefaultPropertyName(Property::Index index) const
100 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
102 return DEFAULT_PROPERTY_DETAILS[index].name;
105 // index out of range
109 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
111 Property::Index index = Property::INVALID_INDEX;
113 // Look for name in default properties
114 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
116 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
117 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
126 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
128 if( index < DEFAULT_PROPERTY_COUNT )
130 return DEFAULT_PROPERTY_DETAILS[index].type;
133 // index out of range
134 return Property::NONE;
137 Property::Value Path::GetDefaultProperty( Property::Index index ) const
139 Property::Value value;
140 if( index == Dali::Path::Property::Points )
142 size_t pointCount( mPoint.Size() );
143 for( size_t i( 0 ); i != pointCount; ++i )
145 value.AppendItem( mPoint[i] );
148 else if( index == Dali::Path::Property::ControlPoints )
150 size_t controlpointCount( mControlPoint.Size() );
151 for( size_t i( 0 ); i != controlpointCount; ++i )
153 value.AppendItem( mControlPoint[i] );
160 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
162 if( index == Dali::Path::Property::Points )
164 Property::Array propertyArray;
165 propertyValue.Get(propertyArray);
167 size_t propertyArrayCount = propertyArray.size();
168 mPoint.Resize( propertyArrayCount );
169 for( size_t i(0); i!=propertyArrayCount; ++i )
171 propertyArray[i].Get( mPoint[i]);
174 else if( index == Dali::Path::Property::ControlPoints )
176 Property::Array propertyArray;
177 propertyValue.Get(propertyArray);
179 size_t propertyArrayCount = propertyArray.size();
180 mControlPoint.Resize( propertyArrayCount );
181 for( size_t i(0); i!=propertyArrayCount; ++i )
183 propertyArray[i].Get( mControlPoint[i]);
188 bool Path::IsDefaultPropertyWritable(Property::Index index) const
190 if( index < DEFAULT_PROPERTY_COUNT )
192 return DEFAULT_PROPERTY_DETAILS[index].writable;
198 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
200 if( index < DEFAULT_PROPERTY_COUNT )
202 return DEFAULT_PROPERTY_DETAILS[index].animatable;
208 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
210 if( index < DEFAULT_PROPERTY_COUNT )
212 return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
218 void Path::AddPoint(const Vector3& point )
220 mPoint.PushBack( point );
223 void Path::AddControlPoint(const Vector3& point )
225 mControlPoint.PushBack( point );
228 unsigned int Path::GetNumberOfSegments() const
230 return (mPoint.Size()>1)?mPoint.Size()-1:0;
233 void Path::GenerateControlPoints( float curvature )
235 unsigned int numSegments = GetNumberOfSegments();
236 DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
238 mControlPoint.Resize( numSegments * 2);
240 //Generate two control points for each segment
241 for( unsigned int i(0); i<numSegments; ++i )
244 Vector3 p1 = mPoint[i];
245 Vector3 p2 = mPoint[i+1];
250 //There's no previous point. We chose a point in the line defined by the two end points at
251 //a 1/8th of the distance between them.
252 p0 = p1 - (p2 - p1)/8.0f;
261 if( i == numSegments - 1)
263 //There's no next point. We chose a point in the line defined by the two end points at
264 //a 1/8th of the distance between them.
265 p3 = p2 - (p1 - p2)/8.0f;
273 Vector3 p0p1 = p1 - p0;
274 Vector3 p1p2 = p2 - p1;
275 Vector3 p2p3 = p3 - p2;
277 float length = p1p2.Length();
279 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
280 tangentOut.Normalize();
282 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
283 tangentIn.Normalize();
285 //Use curvature to scale the tangents
287 mControlPoint[2*i] = p1 + tangentOut*length;
288 mControlPoint[2*i+1] = p2 - tangentIn*length;
292 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
294 //Find segment and local progress
295 unsigned int numSegs = GetNumberOfSegments();
309 segment = t * numSegs;
310 float segLength = 1.0f / numSegs;
311 float segStart = (float)segment * segLength;
312 tLocal = (t - segStart) * numSegs;
316 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
318 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
320 unsigned int segment;
322 FindSegmentAndProgress( t, segment, tLocal );
324 //Get points and control points in the segment
325 const Vector3& controlPoint0 = mControlPoint[2*segment];
326 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
327 const Vector3& point0 = mPoint[segment];
328 const Vector3& point1 = mPoint[segment+1];
330 if(tLocal < Math::MACHINE_EPSILON_1)
333 tangent = ( controlPoint0 - point0 ) * 3.0f;
336 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
339 tangent = ( point1 - controlPoint1 ) * 3.0f;
344 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
345 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
348 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
350 Vector4 A = BezierBasis * cVect;
351 position.x = sVect.Dot4(A);
352 tangent.x = sVectDerivative.Dot(Vector3(A));
356 cVect.y = controlPoint0.y;
357 cVect.z = controlPoint1.y;
360 A = BezierBasis * cVect;
361 position.y = sVect.Dot4(A);
362 tangent.y = sVectDerivative.Dot(Vector3(A));
366 cVect.y = controlPoint0.z;
367 cVect.z = controlPoint1.z;
370 A = BezierBasis * cVect;
371 position.z = sVect.Dot4(A);
372 tangent.z = sVectDerivative.Dot(Vector3(A));
378 Vector3 Path::SamplePosition( float t ) const
380 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
382 unsigned int segment;
384 FindSegmentAndProgress( t, segment, tLocal );
386 const Vector3& controlPoint0 = mControlPoint[2*segment];
387 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
388 const Vector3& point0 = mPoint[segment];
389 const Vector3& point1 = mPoint[segment+1];
392 if(tLocal < Math::MACHINE_EPSILON_1)
396 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
402 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
405 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
406 position.x = sVect.Dot4(BezierBasis * cVect);
410 cVect.y = controlPoint0.y;
411 cVect.z = controlPoint1.y;
413 position.y = sVect.Dot4(BezierBasis * cVect);
417 cVect.y = controlPoint0.z;
418 cVect.z = controlPoint1.z;
420 position.z = sVect.Dot4(BezierBasis * cVect);
426 Vector3 Path::SampleTangent( float t ) const
428 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
430 unsigned int segment;
432 FindSegmentAndProgress( t, segment, tLocal );
434 const Vector3& controlPoint0 = mControlPoint[2*segment];
435 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
436 const Vector3& point0 = mPoint[segment];
437 const Vector3& point1 = mPoint[segment+1];
440 if(tLocal < Math::MACHINE_EPSILON_1)
442 tangent = ( controlPoint0 - point0 ) * 3.0f;
444 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
446 tangent = ( point1 - controlPoint1 ) * 3.0f;
450 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
453 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
454 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
458 cVect.y = controlPoint0.y;
459 cVect.z = controlPoint1.y;
461 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
465 cVect.y = controlPoint0.z;
466 cVect.z = controlPoint1.z;
468 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
475 Vector3& Path::GetPoint( size_t index )
477 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
479 return mPoint[index];
482 Vector3& Path::GetControlPoint( size_t index )
484 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
486 return mControlPoint[index];
489 size_t Path::GetPointCount() const
491 return mPoint.Size();