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 const Dali::Internal::PropertyDetails DEFAULT_PROPERTY_DETAILS[] =
44 { "points", Dali::Property::ARRAY, true, false, false },
45 { "control-points", Dali::Property::ARRAY, true, false, false },
48 const int DEFAULT_PROPERTY_COUNT = sizeof( DEFAULT_PROPERTY_DETAILS ) / sizeof( DEFAULT_PROPERTY_DETAILS[0] );
54 const Property::Index Path::POINTS = 0;
55 const Property::Index Path::CONTROL_POINTS = 1;
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;
106 // index out of range
111 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
113 Property::Index index = Property::INVALID_INDEX;
115 // Look for name in default properties
116 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
118 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
119 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
128 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
130 if( index < DEFAULT_PROPERTY_COUNT )
132 return DEFAULT_PROPERTY_DETAILS[index].type;
136 // index out of range
137 return Property::NONE;
141 Property::Value Path::GetDefaultProperty( Property::Index index ) const
143 Property::Value value;
146 case Dali::Path::POINTS:
148 size_t pointCount( mPoint.Size() );
149 for( size_t i(0); i!=pointCount; ++i )
151 value.AppendItem( mPoint[i] );
155 case Dali::Path::CONTROL_POINTS:
157 size_t controlpointCount( mControlPoint.Size() );
158 for( size_t i(0); i!=controlpointCount; ++i )
160 value.AppendItem( mControlPoint[i] );
166 DALI_ASSERT_ALWAYS(false && "Path::Property is out of bounds");
174 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
178 case Dali::Path::POINTS:
180 Property::Array propertyArray;
181 propertyValue.Get(propertyArray);
183 size_t propertyArrayCount = propertyArray.size();
184 mPoint.Resize( propertyArrayCount );
185 for( size_t i(0); i!=propertyArrayCount; ++i )
187 propertyArray[i].Get( mPoint[i]);
191 case Dali::Path::CONTROL_POINTS:
193 Property::Array propertyArray;
194 propertyValue.Get(propertyArray);
196 size_t propertyArrayCount = propertyArray.size();
197 mControlPoint.Resize( propertyArrayCount );
198 for( size_t i(0); i!=propertyArrayCount; ++i )
200 propertyArray[i].Get( mControlPoint[i]);
206 // no read only properties
212 bool Path::IsDefaultPropertyWritable(Property::Index index) const
214 if( index < DEFAULT_PROPERTY_COUNT )
216 return DEFAULT_PROPERTY_DETAILS[index].writable;
224 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
226 if( index < DEFAULT_PROPERTY_COUNT )
228 return DEFAULT_PROPERTY_DETAILS[index].animatable;
236 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
238 if( index < DEFAULT_PROPERTY_COUNT )
240 return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
248 void Path::AddPoint(const Vector3& point )
250 mPoint.PushBack( point );
253 void Path::AddControlPoint(const Vector3& point )
255 mControlPoint.PushBack( point );
258 unsigned int Path::GetNumberOfSegments() const
260 return (mPoint.Size()>1)?mPoint.Size()-1:0;
263 void Path::GenerateControlPoints( float curvature )
265 unsigned int numSegments = GetNumberOfSegments();
266 DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
268 mControlPoint.Resize( numSegments * 2);
270 //Generate two control points for each segment
271 for( unsigned int i(0); i<numSegments; ++i )
274 Vector3 p1 = mPoint[i];
275 Vector3 p2 = mPoint[i+1];
280 //There's no previous point. We chose a point in the line defined by the two end points at
281 //a 1/8th of the distance between them.
282 p0 = p1 - (p2 - p1)/8.0f;
291 if( i == numSegments - 1)
293 //There's no next point. We chose a point in the line defined by the two end points at
294 //a 1/8th of the distance between them.
295 p3 = p2 - (p1 - p2)/8.0f;
303 Vector3 p0p1 = p1 - p0;
304 Vector3 p1p2 = p2 - p1;
305 Vector3 p2p3 = p3 - p2;
307 float length = p1p2.Length();
309 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
310 tangentOut.Normalize();
312 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
313 tangentIn.Normalize();
315 //Use curvature to scale the tangents
317 mControlPoint[2*i] = p1 + tangentOut*length;
318 mControlPoint[2*i+1] = p2 - tangentIn*length;
322 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
324 //Find segment and local progress
325 unsigned int numSegs = GetNumberOfSegments();
339 segment = t * numSegs;
340 float segLength = 1.0f / numSegs;
341 float segStart = (float)segment * segLength;
342 tLocal = (t - segStart) * numSegs;
346 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
348 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
350 unsigned int segment;
352 FindSegmentAndProgress( t, segment, tLocal );
354 //Get points and control points in the segment
355 const Vector3& controlPoint0 = mControlPoint[2*segment];
356 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
357 const Vector3& point0 = mPoint[segment];
358 const Vector3& point1 = mPoint[segment+1];
360 if(tLocal < Math::MACHINE_EPSILON_1)
363 tangent = ( controlPoint0 - point0 ) * 3.0f;
366 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
369 tangent = ( point1 - controlPoint1 ) * 3.0f;
374 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
375 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
378 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
380 Vector4 A = BezierBasis * cVect;
381 position.x = sVect.Dot4(A);
382 tangent.x = sVectDerivative.Dot(Vector3(A));
386 cVect.y = controlPoint0.y;
387 cVect.z = controlPoint1.y;
390 A = BezierBasis * cVect;
391 position.y = sVect.Dot4(A);
392 tangent.y = sVectDerivative.Dot(Vector3(A));
396 cVect.y = controlPoint0.z;
397 cVect.z = controlPoint1.z;
400 A = BezierBasis * cVect;
401 position.z = sVect.Dot4(A);
402 tangent.z = sVectDerivative.Dot(Vector3(A));
408 Vector3 Path::SamplePosition( float t ) const
410 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
412 unsigned int segment;
414 FindSegmentAndProgress( t, segment, tLocal );
416 const Vector3& controlPoint0 = mControlPoint[2*segment];
417 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
418 const Vector3& point0 = mPoint[segment];
419 const Vector3& point1 = mPoint[segment+1];
422 if(tLocal < Math::MACHINE_EPSILON_1)
426 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
432 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
435 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
436 position.x = sVect.Dot4(BezierBasis * cVect);
440 cVect.y = controlPoint0.y;
441 cVect.z = controlPoint1.y;
443 position.y = sVect.Dot4(BezierBasis * cVect);
447 cVect.y = controlPoint0.z;
448 cVect.z = controlPoint1.z;
450 position.z = sVect.Dot4(BezierBasis * cVect);
456 Vector3 Path::SampleTangent( float t ) const
458 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
460 unsigned int segment;
462 FindSegmentAndProgress( t, segment, tLocal );
464 const Vector3& controlPoint0 = mControlPoint[2*segment];
465 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
466 const Vector3& point0 = mPoint[segment];
467 const Vector3& point1 = mPoint[segment+1];
470 if(tLocal < Math::MACHINE_EPSILON_1)
472 tangent = ( controlPoint0 - point0 ) * 3.0f;
474 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
476 tangent = ( point1 - controlPoint1 ) * 3.0f;
480 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
483 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
484 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
488 cVect.y = controlPoint0.y;
489 cVect.z = controlPoint1.y;
491 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
495 cVect.y = controlPoint0.z;
496 cVect.z = controlPoint1.z;
498 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
505 Vector3& Path::GetPoint( size_t index )
507 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
509 return mPoint[index];
512 Vector3& Path::GetControlPoint( size_t index )
514 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
516 return mControlPoint[index];
519 size_t Path::GetPointCount() const
521 return mPoint.Size();