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>
24 * These coefficient arise from the cubic polynomial equations for
27 * A bezier curve is defined by a cubic polynomial. Given two end points p0 and p1
28 * and two control points cp0 and cp1, the bezier curve will be defined by a polynomial in the form
29 * f(x) = a3*x^3 + a2*x^2 + a1*x + a0 with this restrictions:
32 * f'(0) = 3*(cp0 - p0)
35 const float BezierBasisCoeff[] = { -1.0f, 3.0f, -3.0f, 1.0f,
36 3.0f, -6.0f, 3.0f, 0.0f,
37 -3.0f, 3.0f, 0.0f, 0.0f,
38 1.0f, 0.0f, 0.0f, 0.0f };
40 const Dali::Matrix BezierBasis = Dali::Matrix( BezierBasisCoeff );
42 struct PropertyDetails
44 std::string name; ///< The name of the property.
45 Dali::Property::Type type; ///< The property type.
46 bool writable:1; ///< Whether the property is writable
47 bool animatable:1; ///< Whether the property is animatable.
48 bool constraintInput:1; ///< Whether the property can be used as an input to a constraint.
51 const PropertyDetails DEFAULT_PROPERTY_DETAILS[] = {{"points", Dali::Property::ARRAY, true, false, false },
52 {"control-points", Dali::Property::ARRAY, true, false, false },
55 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_DETAILS ) / sizeof( PropertyDetails );
61 const Property::Index Path::POINTS = 0;
62 const Property::Index Path::CONTROL_POINTS = 1;
81 Path* Path::Clone(const Path& path)
83 Path* clone = new Path();
84 clone->SetPoints( path.GetPoints() );
85 clone->SetControlPoints( path.GetControlPoints() );
90 unsigned int Path::GetDefaultPropertyCount() const
92 return DEFAULT_PROPERTY_COUNT;
95 void Path::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
97 indices.reserve( DEFAULT_PROPERTY_COUNT );
99 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
101 indices.push_back( i );
105 const std::string& Path::GetDefaultPropertyName(Property::Index index) const
107 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
109 return DEFAULT_PROPERTY_DETAILS[index].name;
113 // index out of range
114 static const std::string INVALID_PROPERTY_NAME;
115 return INVALID_PROPERTY_NAME;
119 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
121 Property::Index index = Property::INVALID_INDEX;
123 for( int i(0); i<DEFAULT_PROPERTY_COUNT; ++i )
125 if ( name == DEFAULT_PROPERTY_DETAILS[i].name )
134 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
136 if( index < DEFAULT_PROPERTY_COUNT )
138 return DEFAULT_PROPERTY_DETAILS[index].type;
142 // index out of range
143 return Property::NONE;
147 Property::Value Path::GetDefaultProperty( Property::Index index ) const
149 Property::Value value;
152 case Dali::Path::POINTS:
154 size_t pointCount( mPoint.Size() );
155 for( size_t i(0); i!=pointCount; ++i )
157 value.AppendItem( mPoint[i] );
161 case Dali::Path::CONTROL_POINTS:
163 size_t controlpointCount( mControlPoint.Size() );
164 for( size_t i(0); i!=controlpointCount; ++i )
166 value.AppendItem( mControlPoint[i] );
172 DALI_ASSERT_ALWAYS(false && "Path::Property is out of bounds");
180 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
184 case Dali::Path::POINTS:
186 Property::Array propertyArray;
187 propertyValue.Get(propertyArray);
189 size_t propertyArrayCount = propertyArray.size();
190 mPoint.Resize( propertyArrayCount );
191 for( size_t i(0); i!=propertyArrayCount; ++i )
193 propertyArray[i].Get( mPoint[i]);
197 case Dali::Path::CONTROL_POINTS:
199 Property::Array propertyArray;
200 propertyValue.Get(propertyArray);
202 size_t propertyArrayCount = propertyArray.size();
203 mControlPoint.Resize( propertyArrayCount );
204 for( size_t i(0); i!=propertyArrayCount; ++i )
206 propertyArray[i].Get( mControlPoint[i]);
212 DALI_ASSERT_ALWAYS(false && "Path::Property is out of bounds");
218 bool Path::IsDefaultPropertyWritable(Property::Index index) const
220 if( index < DEFAULT_PROPERTY_COUNT )
222 return DEFAULT_PROPERTY_DETAILS[index].writable;
230 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
232 if( index < DEFAULT_PROPERTY_COUNT )
234 return DEFAULT_PROPERTY_DETAILS[index].animatable;
242 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
244 if( index < DEFAULT_PROPERTY_COUNT )
246 return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
254 void Path::AddPoint(const Vector3& point )
256 mPoint.PushBack( point );
259 void Path::AddControlPoint(const Vector3& point )
261 mControlPoint.PushBack( point );
264 unsigned int Path::GetNumberOfSegments() const
266 return (mPoint.Size()>1)?mPoint.Size()-1:0;
269 void Path::GenerateControlPoints( float curvature )
271 unsigned int numSegments = GetNumberOfSegments();
272 DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
274 mControlPoint.Resize( numSegments * 2);
276 //Generate two control points for each segment
277 for( unsigned int i(0); i<numSegments; ++i )
280 Vector3 p1 = mPoint[i];
281 Vector3 p2 = mPoint[i+1];
286 //There's no previous point. We chose a point in the line defined by the two end points at
287 //a 1/8th of the distance between them.
288 p0 = p1 - (p2 - p1)/8.0f;
297 if( i == numSegments - 1)
299 //There's no next point. We chose a point in the line defined by the two end points at
300 //a 1/8th of the distance between them.
301 p3 = p2 - (p1 - p2)/8.0f;
309 Vector3 p0p1 = p1 - p0;
310 Vector3 p1p2 = p2 - p1;
311 Vector3 p2p3 = p3 - p2;
313 float length = p1p2.Length();
315 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
316 tangentOut.Normalize();
318 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
319 tangentIn.Normalize();
321 //Use curvature to scale the tangents
323 mControlPoint[2*i] = p1 + tangentOut*length;
324 mControlPoint[2*i+1] = p2 - tangentIn*length;
328 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
330 //Find segment and local progress
331 unsigned int numSegs = GetNumberOfSegments();
345 segment = t * numSegs;
346 float segLength = 1.0f / numSegs;
347 float segStart = (float)segment * segLength;
348 tLocal = (t - segStart) * numSegs;
352 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
354 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
356 unsigned int segment;
358 FindSegmentAndProgress( t, segment, tLocal );
360 //Get points and control points in the segment
361 const Vector3& controlPoint0 = mControlPoint[2*segment];
362 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
363 const Vector3& point0 = mPoint[segment];
364 const Vector3& point1 = mPoint[segment+1];
366 if(tLocal < Math::MACHINE_EPSILON_1)
369 tangent = ( controlPoint0 - point0 ) * 3.0f;
372 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
375 tangent = ( point1 - controlPoint1 ) * 3.0f;
380 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
381 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
384 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
386 Vector4 A = BezierBasis * cVect;
387 position.x = sVect.Dot4(A);
388 tangent.x = sVectDerivative.Dot(Vector3(A));
392 cVect.y = controlPoint0.y;
393 cVect.z = controlPoint1.y;
396 A = BezierBasis * cVect;
397 position.y = sVect.Dot4(A);
398 tangent.y = sVectDerivative.Dot(Vector3(A));
402 cVect.y = controlPoint0.z;
403 cVect.z = controlPoint1.z;
406 A = BezierBasis * cVect;
407 position.z = sVect.Dot4(A);
408 tangent.z = sVectDerivative.Dot(Vector3(A));
414 Vector3 Path::SamplePosition( float t ) const
416 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
418 unsigned int segment;
420 FindSegmentAndProgress( t, segment, tLocal );
422 const Vector3& controlPoint0 = mControlPoint[2*segment];
423 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
424 const Vector3& point0 = mPoint[segment];
425 const Vector3& point1 = mPoint[segment+1];
428 if(tLocal < Math::MACHINE_EPSILON_1)
432 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
438 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
441 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
442 position.x = sVect.Dot4(BezierBasis * cVect);
446 cVect.y = controlPoint0.y;
447 cVect.z = controlPoint1.y;
449 position.y = sVect.Dot4(BezierBasis * cVect);
453 cVect.y = controlPoint0.z;
454 cVect.z = controlPoint1.z;
456 position.z = sVect.Dot4(BezierBasis * cVect);
462 Vector3 Path::SampleTangent( float t ) const
464 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
466 unsigned int segment;
468 FindSegmentAndProgress( t, segment, tLocal );
470 const Vector3& controlPoint0 = mControlPoint[2*segment];
471 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
472 const Vector3& point0 = mPoint[segment];
473 const Vector3& point1 = mPoint[segment+1];
476 if(tLocal < Math::MACHINE_EPSILON_1)
478 tangent = ( controlPoint0 - point0 ) * 3.0f;
480 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
482 tangent = ( point1 - controlPoint1 ) * 3.0f;
486 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
489 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
490 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
494 cVect.y = controlPoint0.y;
495 cVect.z = controlPoint1.y;
497 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
501 cVect.y = controlPoint0.z;
502 cVect.z = controlPoint1.z;
504 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
511 Vector3& Path::GetPoint( size_t index )
513 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
515 return mPoint[index];
518 Vector3& Path::GetControlPoint( size_t index )
520 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
522 return mControlPoint[index];
525 size_t Path::GetPointCount() const
527 return mPoint.Size();