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>
39 // Name Type writable animatable constraint-input enum for index-checking
40 DALI_PROPERTY_TABLE_BEGIN
41 DALI_PROPERTY( "points", ARRAY, true, false, false, Dali::Path::Property::POINTS )
42 DALI_PROPERTY( "control-points", ARRAY, true, false, false, Dali::Path::Property::CONTROL_POINTS )
43 DALI_PROPERTY_TABLE_END( DEFAULT_OBJECT_PROPERTY_START_INDEX )
46 * These coefficient arise from the cubic polynomial equations for
49 * A bezier curve is defined by a cubic polynomial. Given two end points p0 and p1
50 * and two control points cp0 and cp1, the bezier curve will be defined by a polynomial in the form
51 * f(x) = a3*x^3 + a2*x^2 + a1*x + a0 with this restrictions:
54 * f'(0) = 3*(cp0 - p0)
57 const float BezierBasisCoeff[] = { -1.0f, 3.0f, -3.0f, 1.0f,
58 3.0f, -6.0f, 3.0f, 0.0f,
59 -3.0f, 3.0f, 0.0f, 0.0f,
60 1.0f, 0.0f, 0.0f, 0.0f };
62 const Dali::Matrix BezierBasis = Dali::Matrix( BezierBasisCoeff );
80 Path* Path::Clone(const Path& path)
82 Path* clone = new Path();
83 clone->SetPoints( path.GetPoints() );
84 clone->SetControlPoints( path.GetControlPoints() );
89 unsigned int Path::GetDefaultPropertyCount() const
91 return DEFAULT_PROPERTY_COUNT;
94 void Path::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
96 indices.Reserve( DEFAULT_PROPERTY_COUNT );
98 for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
100 indices.PushBack( i );
104 const char* Path::GetDefaultPropertyName(Property::Index index) const
106 if ( ( index >= 0 ) && ( index < DEFAULT_PROPERTY_COUNT ) )
108 return DEFAULT_PROPERTY_DETAILS[index].name;
111 // index out of range
115 Property::Index Path::GetDefaultPropertyIndex(const std::string& name) const
117 Property::Index index = Property::INVALID_INDEX;
119 // Look for name in default properties
120 for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
122 const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
123 if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
132 Property::Type Path::GetDefaultPropertyType(Property::Index index) const
134 if( index < DEFAULT_PROPERTY_COUNT )
136 return DEFAULT_PROPERTY_DETAILS[index].type;
139 // index out of range
140 return Property::NONE;
143 Property::Value Path::GetDefaultProperty( Property::Index index ) const
145 Property::Value value;
146 if( index == Dali::Path::Property::POINTS )
148 Property::Array propertyArray;
149 value = Property::Value(propertyArray);
150 size_t pointCount( mPoint.Size() );
151 for( size_t i( 0 ); i != pointCount; ++i )
153 value.AppendItem( mPoint[i] );
156 else if( index == Dali::Path::Property::CONTROL_POINTS )
158 Property::Array propertyArray;
159 value = Property::Value(propertyArray);
160 size_t controlpointCount( mControlPoint.Size() );
161 for( size_t i( 0 ); i != controlpointCount; ++i )
163 value.AppendItem( mControlPoint[i] );
170 void Path::SetDefaultProperty(Property::Index index, const Property::Value& propertyValue)
172 if( index == Dali::Path::Property::POINTS )
174 Property::Array propertyArray;
175 propertyValue.Get(propertyArray);
177 size_t propertyArrayCount = propertyArray.Size();
178 mPoint.Resize( propertyArrayCount );
179 for( size_t i(0); i!=propertyArrayCount; ++i )
181 propertyArray[i].Get( mPoint[i]);
184 else if( index == Dali::Path::Property::CONTROL_POINTS )
186 Property::Array propertyArray;
187 propertyValue.Get(propertyArray);
189 size_t propertyArrayCount = propertyArray.Size();
190 mControlPoint.Resize( propertyArrayCount );
191 for( size_t i(0); i!=propertyArrayCount; ++i )
193 propertyArray[i].Get( mControlPoint[i]);
198 bool Path::IsDefaultPropertyWritable(Property::Index index) const
200 if( index < DEFAULT_PROPERTY_COUNT )
202 return DEFAULT_PROPERTY_DETAILS[index].writable;
208 bool Path::IsDefaultPropertyAnimatable(Property::Index index) const
210 if( index < DEFAULT_PROPERTY_COUNT )
212 return DEFAULT_PROPERTY_DETAILS[index].animatable;
218 bool Path::IsDefaultPropertyAConstraintInput( Property::Index index ) const
220 if( index < DEFAULT_PROPERTY_COUNT )
222 return DEFAULT_PROPERTY_DETAILS[index].constraintInput;
228 void Path::AddPoint(const Vector3& point )
230 mPoint.PushBack( point );
233 void Path::AddControlPoint(const Vector3& point )
235 mControlPoint.PushBack( point );
238 unsigned int Path::GetNumberOfSegments() const
240 return (mPoint.Size()>1)?mPoint.Size()-1:0;
243 void Path::GenerateControlPoints( float curvature )
245 unsigned int numSegments = GetNumberOfSegments();
246 DALI_ASSERT_ALWAYS( numSegments > 0 && "Need at least 1 segment to generate control points" ); // need at least 1 segment
248 mControlPoint.Resize( numSegments * 2);
250 //Generate two control points for each segment
251 for( unsigned int i(0); i<numSegments; ++i )
254 Vector3 p1 = mPoint[i];
255 Vector3 p2 = mPoint[i+1];
260 //There's no previous point. We chose a point in the line defined by the two end points at
261 //a 1/8th of the distance between them.
262 p0 = p1 - (p2 - p1)/8.0f;
271 if( i == numSegments - 1)
273 //There's no next point. We chose a point in the line defined by the two end points at
274 //a 1/8th of the distance between them.
275 p3 = p2 - (p1 - p2)/8.0f;
283 Vector3 p0p1 = p1 - p0;
284 Vector3 p1p2 = p2 - p1;
285 Vector3 p2p3 = p3 - p2;
287 float length = p1p2.Length();
289 Vector3 tangentOut = ( p0p1*length + p1p2*p0p1.Length() ) * 0.5f;
290 tangentOut.Normalize();
292 Vector3 tangentIn = ( p1p2*p2p3.Length() + p2p3*length ) * 0.5f;
293 tangentIn.Normalize();
295 //Use curvature to scale the tangents
297 mControlPoint[2*i] = p1 + tangentOut*length;
298 mControlPoint[2*i+1] = p2 - tangentIn*length;
302 void Path::FindSegmentAndProgress( float t, unsigned int& segment, float& tLocal ) const
304 //Find segment and local progress
305 unsigned int numSegs = GetNumberOfSegments();
319 segment = t * numSegs;
320 float segLength = 1.0f / numSegs;
321 float segStart = (float)segment * segLength;
322 tLocal = (t - segStart) * numSegs;
326 void Path::Sample( float t, Vector3& position, Vector3& tangent ) const
328 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
330 unsigned int segment;
332 FindSegmentAndProgress( t, segment, tLocal );
334 //Get points and control points in the segment
335 const Vector3& controlPoint0 = mControlPoint[2*segment];
336 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
337 const Vector3& point0 = mPoint[segment];
338 const Vector3& point1 = mPoint[segment+1];
340 if(tLocal < Math::MACHINE_EPSILON_1)
343 tangent = ( controlPoint0 - point0 ) * 3.0f;
346 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
349 tangent = ( point1 - controlPoint1 ) * 3.0f;
354 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
355 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
358 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
360 Vector4 A = BezierBasis * cVect;
361 position.x = sVect.Dot4(A);
362 tangent.x = sVectDerivative.Dot(Vector3(A));
366 cVect.y = controlPoint0.y;
367 cVect.z = controlPoint1.y;
370 A = BezierBasis * cVect;
371 position.y = sVect.Dot4(A);
372 tangent.y = sVectDerivative.Dot(Vector3(A));
376 cVect.y = controlPoint0.z;
377 cVect.z = controlPoint1.z;
380 A = BezierBasis * cVect;
381 position.z = sVect.Dot4(A);
382 tangent.z = sVectDerivative.Dot(Vector3(A));
388 Vector3 Path::SamplePosition( float t ) const
390 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
392 unsigned int segment;
394 FindSegmentAndProgress( t, segment, tLocal );
396 const Vector3& controlPoint0 = mControlPoint[2*segment];
397 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
398 const Vector3& point0 = mPoint[segment];
399 const Vector3& point1 = mPoint[segment+1];
402 if(tLocal < Math::MACHINE_EPSILON_1)
406 else if( (1.0 - tLocal) < Math::MACHINE_EPSILON_1)
412 const Vector4 sVect(tLocal*tLocal*tLocal, tLocal*tLocal, tLocal, 1.0f );
415 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
416 position.x = sVect.Dot4(BezierBasis * cVect);
420 cVect.y = controlPoint0.y;
421 cVect.z = controlPoint1.y;
423 position.y = sVect.Dot4(BezierBasis * cVect);
427 cVect.y = controlPoint0.z;
428 cVect.z = controlPoint1.z;
430 position.z = sVect.Dot4(BezierBasis * cVect);
436 Vector3 Path::SampleTangent( float t ) const
438 DALI_ASSERT_ALWAYS(mPoint.Size() > 1 && mControlPoint.Size() == (mPoint.Size()-1)*2 && "Spline not fully initialized" );
440 unsigned int segment;
442 FindSegmentAndProgress( t, segment, tLocal );
444 const Vector3& controlPoint0 = mControlPoint[2*segment];
445 const Vector3& controlPoint1 = mControlPoint[2*segment+1];
446 const Vector3& point0 = mPoint[segment];
447 const Vector3& point1 = mPoint[segment+1];
450 if(tLocal < Math::MACHINE_EPSILON_1)
452 tangent = ( controlPoint0 - point0 ) * 3.0f;
454 else if( (1.0f - tLocal) < Math::MACHINE_EPSILON_1)
456 tangent = ( point1 - controlPoint1 ) * 3.0f;
460 const Vector3 sVectDerivative(3.0f*tLocal*tLocal, 2.0f*tLocal, 1.0f );
463 Vector4 cVect( point0.x, controlPoint0.x, controlPoint1.x, point1.x);
464 tangent.x = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
468 cVect.y = controlPoint0.y;
469 cVect.z = controlPoint1.y;
471 tangent.y = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
475 cVect.y = controlPoint0.z;
476 cVect.z = controlPoint1.z;
478 tangent.z = sVectDerivative.Dot(Vector3(BezierBasis * cVect));
485 Vector3& Path::GetPoint( size_t index )
487 DALI_ASSERT_ALWAYS( index < mPoint.Size() && "Path: Point index out of bounds" );
489 return mPoint[index];
492 Vector3& Path::GetControlPoint( size_t index )
494 DALI_ASSERT_ALWAYS( index < mControlPoint.Size() && "Path: Control Point index out of bounds" );
496 return mControlPoint[index];
499 size_t Path::GetPointCount() const
501 return mPoint.Size();