Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

757 lines
28 KiB

  1. //====== Copyright � 1996-2009, Valve Corporation, All rights reserved. =======
  2. //
  3. // A template implementation of a bezier curve class and associated helper
  4. // functions.
  5. //
  6. //=============================================================================
  7. #ifndef BEZIERCURVE_H
  8. #define BEZIERCURVE_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. const float ONE_THIRD = 1.0f / 3.0f;
  13. const float TWO_THIRDS = 2.0f / 3.0f;
  14. //-----------------------------------------------------------------------------
  15. // Generic order N Bezier curve evaluation. Evaluates the bezier curve at the
  16. // specified 0 to 1 parameter and returns the result.
  17. //-----------------------------------------------------------------------------
  18. template< class POINT_TYPE, int ORDER >
  19. struct BezierEvaluateImpl
  20. {
  21. // This generic implementation performs an iterative set of lerps in order
  22. // to compute the bezier evaluation for any order curve, it is not efficiently
  23. // and here primarily to maintain generality. All order curve that are used
  24. // with any frequency should have their own specialized implementations.
  25. static void BezierEvaluate( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  26. {
  27. // This array is initialized with the control points and is then
  28. // used to hold the intermediate results of each lerp in order
  29. // to preserve the original set of control points.
  30. POINT_TYPE points[ ORDER + 1 ];
  31. for ( int i = 0; i <= ORDER; ++i )
  32. {
  33. points[ i ] = pControlPoints[ i ];
  34. }
  35. for ( int i = 1; i <= ORDER; ++i )
  36. {
  37. for ( int j = 0; j <= ( ORDER - i ); ++j )
  38. {
  39. points[ j ] = ( ( 1.0f - t ) * points[ j ] ) + ( t * points[ j + 1 ] );
  40. }
  41. }
  42. result = points[ 0 ];
  43. }
  44. };
  45. //-----------------------------------------------------------------------------
  46. // Partial specialization for linear evaluation.
  47. //-----------------------------------------------------------------------------
  48. template< class POINT_TYPE >
  49. struct BezierEvaluateImpl< POINT_TYPE, 1 >
  50. {
  51. static void BezierEvaluate( const POINT_TYPE *points, float t, POINT_TYPE &result )
  52. {
  53. float u = 1.0f - t;
  54. result = ( u * points[ 0 ] ) + ( t * points[ 1 ] );
  55. }
  56. };
  57. //-----------------------------------------------------------------------------
  58. // Partial specialization for quadratic bezier curve evaluation
  59. //-----------------------------------------------------------------------------
  60. template< class POINT_TYPE >
  61. struct BezierEvaluateImpl< POINT_TYPE, 2 >
  62. {
  63. static void BezierEvaluate( const POINT_TYPE *points, float t, POINT_TYPE &result )
  64. {
  65. float u = 1.0f - t;
  66. float t2 = t * t;
  67. float u2 = u * u;
  68. result = ( u2 * points[ 0 ] ) + ( 2.0f * u * t * points[ 1 ] ) + ( t2 * points[ 2 ] );
  69. }
  70. };
  71. //-----------------------------------------------------------------------------
  72. // Partial specialization for cubic Bezier curve evaluation.
  73. //-----------------------------------------------------------------------------
  74. template< class POINT_TYPE >
  75. struct BezierEvaluateImpl< POINT_TYPE, 3 >
  76. {
  77. static void BezierEvaluate( const POINT_TYPE *points, float t, POINT_TYPE &result )
  78. {
  79. float u = 1.0f - t;
  80. float t2 = t * t;
  81. float t3 = t * t * t;
  82. float u2 = u * u;
  83. float u3 = u * u * u;
  84. result = ( u3 * points[ 0 ] ) + ( 3.0f * u2 * t * points[ 1 ] ) + ( 3.0f * u * t2 * points[ 2 ] ) + ( t3 * points[ 3 ] );
  85. }
  86. };
  87. //-----------------------------------------------------------------------------
  88. // Evaluate the bezier curve of the specified order given a set of control
  89. // points for the curve. Uses the BezierEvaluateImpl in order to allow
  90. // template partial specialization for specific order curves.
  91. //-----------------------------------------------------------------------------
  92. template< class POINT_TYPE, int ORDER >
  93. void BezierEvaluate( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  94. {
  95. BezierEvaluateImpl< POINT_TYPE, ORDER >::BezierEvaluate( pControlPoints, t, result );
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Generic order N Bezier curve tangent evaluation. Evaluates the derivative of
  99. // the Bezier curve at the specified 0 to 1 parameter and returns the result.
  100. //-----------------------------------------------------------------------------
  101. template< class POINT_TYPE, int ORDER >
  102. struct BezierTangentImpl
  103. {
  104. static void BezierTangent( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  105. {
  106. POINT_TYPE derPoints[ ORDER ];
  107. for ( int i = 0; i < ORDER; ++i )
  108. {
  109. derPoints[ i ] = ( pControlPoints[ i + 1 ] - pControlPoints[ i ] ) * (float)ORDER;
  110. }
  111. BezierEvaluate< POINT_TYPE, ORDER - 1 >( derPoints, t, result );
  112. }
  113. };
  114. //-----------------------------------------------------------------------------
  115. // Partial specialization for linear Bezier curve tangent evaluation
  116. //-----------------------------------------------------------------------------
  117. template< class POINT_TYPE >
  118. struct BezierTangentImpl< POINT_TYPE, 1 >
  119. {
  120. static void BezierTangent( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  121. {
  122. POINT_TYPE derPoint;
  123. result = ( pControlPoints[ 1 ] - pControlPoints[ 0 ] );
  124. }
  125. };
  126. //-----------------------------------------------------------------------------
  127. // Partial specialization for quadratic Bezier curve tangent evaluation
  128. //-----------------------------------------------------------------------------
  129. template< class POINT_TYPE >
  130. struct BezierTangentImpl< POINT_TYPE, 2 >
  131. {
  132. static void BezierTangent( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  133. {
  134. POINT_TYPE a = pControlPoints[ 0 ] + ( -2.0f * pControlPoints[ 1 ] ) + pControlPoints[ 2 ];
  135. POINT_TYPE b = ( -2.0f * pControlPoints[ 0 ] ) + ( 2.0f * pControlPoints[ 1 ] );
  136. result = ( 2.0f * a * t ) + b;
  137. }
  138. };
  139. //-----------------------------------------------------------------------------
  140. // Partial specialization for cubic Bezier curve tangent evaluation
  141. //-----------------------------------------------------------------------------
  142. template< class POINT_TYPE >
  143. struct BezierTangentImpl< POINT_TYPE, 3 >
  144. {
  145. static void BezierTangent( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  146. {
  147. POINT_TYPE a = (-1.0f * pControlPoints[ 0 ] ) + ( 3.0f * pControlPoints[ 1 ] ) + (-3.0f * pControlPoints[ 2 ] ) + pControlPoints[ 3 ];
  148. POINT_TYPE b = ( 3.0f * pControlPoints[ 0 ] ) + (-6.0f * pControlPoints[ 1 ] ) + ( 3.0f * pControlPoints[ 2 ] );
  149. POINT_TYPE c = (-3.0f * pControlPoints[ 0 ] ) + ( 3.0f * pControlPoints[ 1 ] );
  150. result = ( 3.0f * a * t * t ) + ( 2.0f * b * t ) + c;
  151. }
  152. };
  153. //-----------------------------------------------------------------------------
  154. // Evaluate the derivative of the bezier curve in order to compute the tangent
  155. // of the curve the the specified parameter. Uses BezierTangentImpl in order
  156. // to allow template partial specialization for specific order curves.
  157. //-----------------------------------------------------------------------------
  158. template< class POINT_TYPE, int ORDER >
  159. void BezierTangent( const POINT_TYPE *pControlPoints, float t, POINT_TYPE &result )
  160. {
  161. BezierTangentImpl< POINT_TYPE, ORDER >::BezierTangent( pControlPoints, t, result );
  162. }
  163. //-----------------------------------------------------------------------------
  164. // The CBezierCurve represents an a order N bezier curve defined by control
  165. // points of an arbitrary dimension. The class has template parameters for both
  166. // the order ( ORDER ) and the point type ( POINT_TYPE ). In general the point
  167. // type is expected to be vector, vector2d, or vector4d, but may work with other
  168. // types if the appropriate operators are provided.
  169. //
  170. //-----------------------------------------------------------------------------
  171. template < class POINT_TYPE, int ORDER >
  172. class CBezierCurve
  173. {
  174. protected:
  175. static const int NUM_POINTS = ORDER + 1;
  176. public:
  177. // Default constructor, performs no initialization
  178. CBezierCurve() {}
  179. // Copy constructor
  180. CBezierCurve( const CBezierCurve &source );
  181. // Array constructor, initialize the bezier from an array of control points
  182. explicit CBezierCurve( const POINT_TYPE controlPoints[ NUM_POINTS ] );
  183. // Set all of the control points of the curve
  184. void SetControlPoints( const POINT_TYPE controlPoints[ NUM_POINTS ] );
  185. // Evaluate the curve at specified 0 to 1 parameter, returning the point on the curve
  186. void Evaluate( float flParam, POINT_TYPE &point ) const;
  187. // Compute the tangent vector at the specified parameter of the curve
  188. void ComputeTangent( float flParam, POINT_TYPE &tangent ) const;
  189. // Get the specified control point
  190. const POINT_TYPE &ControlPoint( int index ) const { return m_ControlPoints[ index ]; }
  191. protected:
  192. POINT_TYPE m_ControlPoints[ NUM_POINTS ];
  193. };
  194. //-----------------------------------------------------------------------------
  195. // Copy constructor
  196. //-----------------------------------------------------------------------------
  197. template< class POINT_TYPE, int ORDER >
  198. CBezierCurve< POINT_TYPE, ORDER >::CBezierCurve( const CBezierCurve &source )
  199. {
  200. m_ControlPoints = source.m_ControlPoints;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Array constructor, initialize the bezier from an array of control points
  204. //-----------------------------------------------------------------------------
  205. template< class POINT_TYPE, int ORDER >
  206. CBezierCurve< POINT_TYPE, ORDER >::CBezierCurve( const POINT_TYPE controlPoints[ NUM_POINTS ] )
  207. {
  208. SetControlPoints( controlPoints );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Set all of the control points of the curve
  212. //-----------------------------------------------------------------------------
  213. template< class POINT_TYPE, int ORDER >
  214. void CBezierCurve< POINT_TYPE, ORDER >::SetControlPoints( const POINT_TYPE controlPoints[ NUM_POINTS ] )
  215. {
  216. for ( int i = 0; i < NUM_POINTS; ++i )
  217. {
  218. m_ControlPoints[ i ] = controlPoints[ i ];
  219. }
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Evaluate the bezier curve of the specified order given a set of control
  223. // points for the curve
  224. //-----------------------------------------------------------------------------
  225. template< class POINT_TYPE, int ORDER >
  226. void CBezierCurve< POINT_TYPE, ORDER >::Evaluate( float t, POINT_TYPE &result ) const
  227. {
  228. BezierEvaluate< POINT_TYPE, ORDER >( m_ControlPoints, t, result );
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Compute the tangent vector at the specified parameter of the curve
  232. //-----------------------------------------------------------------------------
  233. template< class POINT_TYPE, int ORDER >
  234. void CBezierCurve< POINT_TYPE, ORDER>::ComputeTangent( float t, POINT_TYPE &tangent ) const
  235. {
  236. BezierTangent< POINT_TYPE, ORDER >( m_ControlPoints, t, tangent );
  237. }
  238. //-----------------------------------------------------------------------------
  239. // The CCubicBezierCurve class represents a third order specialization of the
  240. // generic CBezierCurve class and provided additional functionality which is
  241. // implemented specifically for the cubic form of the bezier curve.
  242. //-----------------------------------------------------------------------------
  243. template < class POINT_TYPE >
  244. class CCubicBezierCurve : public CBezierCurve< POINT_TYPE, 3 >
  245. {
  246. public:
  247. // Default constructor, performs no initialization
  248. CCubicBezierCurve() {}
  249. // Array constructor, initialize the bezier from an array of control points
  250. explicit CCubicBezierCurve( const POINT_TYPE controlPoints[ 4 ] );
  251. // Compute the specified number of points along the curve
  252. void ComputePoints( POINT_TYPE *pPoints, int numPoints ) const;
  253. // Fit the curve to a set of data points
  254. bool FitToPoints( const POINT_TYPE *pPoints, float *pParams, int nPoints, int nMaxSteps, float flMaxError );
  255. private:
  256. // Compute the bezier factor values used for evaluation that are independent of the parameter value
  257. void ComputeFactors( POINT_TYPE &a, POINT_TYPE &b, POINT_TYPE &c, POINT_TYPE &d ) const;
  258. // Perform a single step of the iterative point fitting process
  259. bool FitToPointsStep( const POINT_TYPE *pPoints, float *pParams, int nPoints, bool bReparaterize );
  260. // Calculate the control points of the curve that best fit the sample points with the specified parameters
  261. void ComputeControlPointsForSamples( const POINT_TYPE *pPoints, float *pParams, int nNumPoints );
  262. // Re-parameterize the provided set of points, finding parameter values which provide
  263. // points on the curve closer to the sample points than the current parameter values.
  264. void ReparameterizePoints( const POINT_TYPE *pPoints, float *pParams, int nNumPoints ) const;
  265. // Compute the maximum squared distance between the specified points and the curve
  266. float ComputeMaxError( const POINT_TYPE *pPoints, const float *pParams, int nNumPoints ) const;
  267. // Compute the sum of the squared distance between the specified points and the curve
  268. float ComputeErrorSum( const POINT_TYPE *pPoints, const float *pParams, int nNumPoints ) const;
  269. };
  270. //-----------------------------------------------------------------------------
  271. // Array constructor, initialize the bezier from an array of control points
  272. //-----------------------------------------------------------------------------
  273. template< class POINT_TYPE >
  274. CCubicBezierCurve< POINT_TYPE >::CCubicBezierCurve( const POINT_TYPE controlPoints[ 4 ] )
  275. {
  276. this->m_ControlPoints[ 0 ] = controlPoints[ 0 ];
  277. this->m_ControlPoints[ 1 ] = controlPoints[ 1 ];
  278. this->m_ControlPoints[ 2 ] = controlPoints[ 2 ];
  279. this->m_ControlPoints[ 3 ] = controlPoints[ 3 ];
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Compute the specified number of points along the curve
  283. //-----------------------------------------------------------------------------
  284. template< class POINT_TYPE >
  285. void CCubicBezierCurve< POINT_TYPE >::ComputePoints( POINT_TYPE *pPoints, int numPoints ) const
  286. {
  287. // Must evaluate at least two points.
  288. if ( numPoints <= 1 )
  289. return;
  290. // Calculate the parameter increment for each step
  291. const float flStep = 1.0f / ( numPoints - 1 );
  292. // Compute the basis values that can be re-used for all of the point calculations
  293. POINT_TYPE a, b, c, d;
  294. ComputeFactors( a, b, c, d );
  295. float t = 0;
  296. for ( int i = 0; i < numPoints; ++i )
  297. {
  298. pPoints[ i ] = ( a * t * t * t ) + ( b * t * t ) + ( c * t ) + d;
  299. t = t + flStep;
  300. }
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Fit the curve to a set of data points
  304. //-----------------------------------------------------------------------------
  305. template < class POINT_TYPE >
  306. bool CCubicBezierCurve< POINT_TYPE >::FitToPoints( const POINT_TYPE *pPoints, float *pParams, int nPoints, int nMaxSteps, float flMaxError )
  307. {
  308. if ( ( pPoints == NULL ) || ( pParams == NULL ) || ( nPoints < 2 ) )
  309. return false;
  310. // Compute the max error distance, the provided max error is assumed
  311. // to be a percentage of based on the length of the curve.
  312. float flLengthSQ = pPoints[ 0 ].DistToSqr( pPoints[ nPoints - 1 ] );
  313. // Do one initial step using chord length parameterization.
  314. FitToPointsStep( pPoints, pParams, nPoints, false );
  315. float flError = ComputeMaxError( pPoints, pParams, nPoints );
  316. // Check to see if the error is reasonable enough to be solved by iteration.
  317. float flMaxIterationError = flLengthSQ * 0.1f;
  318. if ( flError > flMaxIterationError )
  319. {
  320. POINT_TYPE vSegment = pPoints[ nPoints - 1 ] - pPoints[ 0 ];
  321. this->m_ControlPoints[ 0 ] = pPoints[ 0 ];
  322. this->m_ControlPoints[ 1 ] = pPoints[ 0 ] + ( vSegment * ONE_THIRD );
  323. this->m_ControlPoints[ 2 ] = pPoints[ 0 ] + ( vSegment * TWO_THIRDS );
  324. this->m_ControlPoints[ 3 ] = pPoints[ nPoints - 1 ];
  325. return false;
  326. }
  327. // Iteratively improve the solution by re-evaluating the parameter values to make them match
  328. // the sample points more closely and then re-fitting the curve using the least squares method.
  329. int iStep = 0;
  330. float flMaxErrorDist = flLengthSQ * ( flMaxError * flMaxError );
  331. while ( ( flError > flMaxErrorDist ) && ( iStep < nMaxSteps ) )
  332. {
  333. FitToPointsStep( pPoints, pParams, nPoints, true );
  334. flError = ComputeMaxError( pPoints, pParams, nPoints );
  335. ++iStep;
  336. }
  337. return ( flError <= flMaxErrorDist );
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Compute the bezier factor values used for evaluation that are independent of
  341. // the parameter value
  342. //-----------------------------------------------------------------------------
  343. template < class POINT_TYPE >
  344. void CCubicBezierCurve< POINT_TYPE >::ComputeFactors( POINT_TYPE &a, POINT_TYPE &b, POINT_TYPE &c, POINT_TYPE &d ) const
  345. {
  346. const POINT_TYPE *pControlPoints = this->m_ControlPoints;
  347. a = (-1.0f * pControlPoints[ 0 ] ) + ( 3.0f * pControlPoints[ 1 ] ) + (-3.0f * pControlPoints[ 2 ] ) + pControlPoints[ 3 ];
  348. b = ( 3.0f * pControlPoints[ 0 ] ) + (-6.0f * pControlPoints[ 1 ] ) + ( 3.0f * pControlPoints[ 2 ] );
  349. c = (-3.0f * pControlPoints[ 0 ] ) + ( 3.0f * pControlPoints[ 1 ] );
  350. d = ( 1.0f * pControlPoints[ 0 ] );
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Perform a single step of the iterative point fitting process
  354. //-----------------------------------------------------------------------------
  355. template < class POINT_TYPE >
  356. bool CCubicBezierCurve< POINT_TYPE >::FitToPointsStep( const POINT_TYPE *pPoints, float *pParams, int nNumPoints, bool bReparameterize )
  357. {
  358. POINT_TYPE *pControlPoints = this->m_ControlPoints;
  359. if ( ( pPoints == NULL ) || ( pParams == NULL ) )
  360. return false;
  361. if ( nNumPoints < 2 )
  362. return false;
  363. if ( nNumPoints == 2)
  364. {
  365. pControlPoints[ 0 ] = pPoints[ 0 ];
  366. pControlPoints[ 1 ] = pPoints[ 0 ] + ( pPoints[ 1 ] - pPoints[ 0 ] ) * ONE_THIRD;
  367. pControlPoints[ 2 ] = pPoints[ 0 ] + ( pPoints[ 1 ] - pPoints[ 0 ] ) * TWO_THIRDS;
  368. pControlPoints[ 3 ] = pPoints[ 1 ];
  369. pParams[ 0 ] = 0.0f;
  370. pParams[ 1 ] = 1.0f;
  371. return true;
  372. }
  373. if ( bReparameterize )
  374. {
  375. ReparameterizePoints( pPoints, pParams, nNumPoints );
  376. }
  377. else
  378. {
  379. // Chord length parameterization
  380. float length = 0;
  381. pParams[ 0 ] = 0;
  382. for ( int i = 1; i < nNumPoints; ++i )
  383. {
  384. float distance = pPoints[ i - 1 ].DistTo( pPoints[ i ] );
  385. length += distance;
  386. pParams[ i ] = length;
  387. }
  388. for ( int i = 0; i < nNumPoints; ++i )
  389. {
  390. pParams[ i ] = pParams[ i ] / length;
  391. }
  392. }
  393. ComputeControlPointsForSamples( pPoints, pParams, nNumPoints );
  394. return true;
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Calculate the control points of the curve that best fit the sample points
  398. // with the specified parameters.
  399. //-----------------------------------------------------------------------------
  400. template < class POINT_TYPE >
  401. void CCubicBezierCurve< POINT_TYPE >::ComputeControlPointsForSamples( const POINT_TYPE *pPoints, float *pParams, int nPoints )
  402. {
  403. POINT_TYPE *pControlPoints = this->m_ControlPoints;
  404. // Set end control points to the first and last sample points
  405. pControlPoints[ 0 ] = pPoints[ 0 ];
  406. pControlPoints[ 3 ] = pPoints[ nPoints - 1 ];
  407. // Use the least squares method to calculate new controls points
  408. float a1 = 0;
  409. float a2 = 0;
  410. float a12 = 0;
  411. POINT_TYPE c1;
  412. POINT_TYPE c2;
  413. c1.Init();
  414. c2.Init();
  415. Assert( c1.IsZero() );
  416. Assert( c2.IsZero() );
  417. for ( int i = 0; i < nPoints; ++i )
  418. {
  419. const POINT_TYPE &p = pPoints[ i ];
  420. float t = pParams[ i ];
  421. float t2 = t * t;
  422. float t3 = t * t2;
  423. float t4 = t * t3;
  424. float u = 1 - t;
  425. float u2 = u * u;
  426. float u3 = u * u2;
  427. float u4 = u * u3;
  428. a1 += t2 * u4;
  429. a2 += t4 * u2;
  430. a12 += t3 * u3;
  431. POINT_TYPE vP = p - (u3 * pControlPoints[ 0 ]) - ( t3 * pControlPoints[ 3 ] );
  432. c1 += ( ( 3 * t * u2 ) * vP );
  433. c2 += ( ( 3 * t2 * u ) * vP );
  434. }
  435. a1 = 9.0f * a1;
  436. a2 = 9.0f * a2;
  437. a12 = 9.0f * a12;
  438. const float flFactorTolerance = 0.000001f;
  439. float flFactor = ( a1 * a2 - a12 * a12 );
  440. if ( fabs( flFactor ) < flFactorTolerance )
  441. {
  442. POINT_TYPE vSegment = pControlPoints[ 3 ] - pControlPoints[ 0 ];
  443. pControlPoints[ 1 ] = pControlPoints[ 0 ] + vSegment * ONE_THIRD;
  444. pControlPoints[ 2 ] = pControlPoints[ 0 ] + vSegment * TWO_THIRDS;
  445. }
  446. else
  447. {
  448. pControlPoints[ 1 ] = ( a2 * c1 - a12 * c2 ) / flFactor;
  449. pControlPoints[ 2 ] = ( a1 * c2 - a12 * c1 ) / flFactor;
  450. }
  451. // The subsequent re-parameterization relies on NewtonRaphson root finding which will
  452. // fail if the tangents have an x delta of 0 or less, so ensure this does not happen.
  453. float flMinStep = ( pControlPoints[ 3 ].x - pControlPoints[ 0 ].x ) * 0.0001f;
  454. float flMinX = pControlPoints[ 0 ].x + flMinStep;
  455. float flMaxX = pControlPoints[ 3 ].x - flMinStep;
  456. pControlPoints[ 1 ].x = MAX( flMinX, MIN( flMaxX, pControlPoints[ 1 ].x ) );
  457. pControlPoints[ 2 ].x = MAX( flMinX, MIN( flMaxX, pControlPoints[ 2 ].x ) );
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Find a better parameter for the specified point using the NewtonRaphson
  461. // method, or with simple iteration if the NewtonRaphson method fails.
  462. //-----------------------------------------------------------------------------
  463. template < class POINT_TYPE >
  464. void CCubicBezierCurve< POINT_TYPE >::ReparameterizePoints( const POINT_TYPE *pPoints, float *pParams, int nNumPoints ) const
  465. {
  466. const POINT_TYPE *pControlPoints = this->m_ControlPoints;
  467. const float flTolerance = 0.0001f;
  468. POINT_TYPE der1[ 3 ];
  469. der1[ 0 ] = ( pControlPoints[ 1 ] - pControlPoints[ 0 ] ) * 3.0f;
  470. der1[ 1 ] = ( pControlPoints[ 2 ] - pControlPoints[ 1 ] ) * 3.0f;
  471. der1[ 2 ] = ( pControlPoints[ 3 ] - pControlPoints[ 2 ] ) * 3.0f;
  472. POINT_TYPE der2[ 2 ];
  473. der2[ 0 ] = ( der1[ 1 ] - der1[ 0 ] ) * 2.0f;
  474. der2[ 1 ] = ( der1[ 2 ] - der1[ 1 ] ) * 2.0f;
  475. // Compute the basis values that can be re-used for all of the point calculations
  476. POINT_TYPE b3a, b3b, b3c, b3d;
  477. ComputeFactors( b3a, b3b, b3c, b3d );
  478. POINT_TYPE b2a = ( 1.0f * der1[ 0 ] ) + ( -2.0f * der1[ 1 ] ) + ( 1.0f * der1[ 2 ] );
  479. POINT_TYPE b2b = ( -2.0f * der1[ 0 ] ) + ( 2.0f * der1[ 1 ] );
  480. POINT_TYPE b2c = ( 1.0f * der1[ 0 ] );
  481. POINT_TYPE b1a = der2[ 1 ] - der2[ 0 ];
  482. POINT_TYPE b1b = der2[ 0 ];
  483. float flPrevParam = 0;
  484. for ( int iPoint = 0; iPoint < nNumPoints; ++iPoint )
  485. {
  486. float t = pParams[ iPoint ];
  487. const POINT_TYPE &point = pPoints[ iPoint ];
  488. POINT_TYPE curvePoint = ( b3a * t * t * t ) + ( b3b * t * t ) + ( b3c * t ) + b3d;
  489. POINT_TYPE der1Point = ( b2a * t * t ) + ( b2b * t ) + b2c;
  490. POINT_TYPE der2Point = ( b1a * t ) + b1b;
  491. // Attempt to find a better parameter for the point
  492. // using the NewtonRaphson root finding method.
  493. POINT_TYPE vDelta = curvePoint - point;
  494. float flNumerator = vDelta.Dot( der1Point );
  495. float flDenominator = vDelta.Dot( der2Point ) + der1Point.Dot( der1Point );
  496. float flRootParam = ( flDenominator == 0.0f ) ? t : t - ( flNumerator / flDenominator );
  497. // We are not interested in any solutions outside the 0 to 1 range, so
  498. // clamp the result. This may give a result that is farther than the
  499. // original parameter, in which case the original parameter will be used.
  500. flRootParam = MAX( 0.0f, MIN( 1.0f, flRootParam ) );
  501. // Evaluate the parameter returned by the root finding, to
  502. // determine if it is actually better parameter for the point.
  503. float rp = flRootParam;
  504. POINT_TYPE rootCurvePoint = ( b3a * rp * rp * rp ) + ( b3b * rp * rp ) + ( b3c * rp ) + b3d;
  505. float flDist = point.DistToSqr( curvePoint );
  506. float flDistRoot = point.DistToSqr( rootCurvePoint );
  507. // If the parameter returned by the root finding method gives a point on the
  508. // curve that is closer to the sample point than the current parameter make
  509. // the new parameter the value found by the root finding method.
  510. float flNewParam = t;
  511. if ( flDistRoot <= flDist )
  512. {
  513. flNewParam = flRootParam;
  514. }
  515. else if ( flDist > flTolerance )
  516. {
  517. // If the root finding method failed, try to find a better parameter iteratively. This is
  518. // basically a brute force method, but with a couple of observations which actually make it
  519. // reasonable. First the direction to iterate from the current parameter can be deduced
  520. // from the dot product of the vector from the point and the tangent of the curve. Second
  521. // the range of iteration can be restricted such that values before the last parameter
  522. // are not considered.
  523. POINT_TYPE stepPoint;
  524. float flStepParam = t;
  525. float flStepDist = 0;
  526. float flBestStepParam = t;
  527. float flBestStepDist = flDist;
  528. const int nMaxSteps = 10;
  529. const float flBaseStepSize = MAX( t - flPrevParam, 0.001f ) / ( float )nMaxSteps;
  530. float flStepSize = 0;
  531. int nStep = 0;
  532. // The numerator of the root finding method is the dot product between the vector from the
  533. // sample point to the point on the curve and the tangent of the curve. The tangent of the
  534. // curve tells us which way the curve is going and we want to move the parameter along the
  535. // curve in the way which is moving closer to the point, so if the dot product of the
  536. // tangent and the vector from the point on the curve to the sample point is positive then
  537. // moving in a positive direction along the curve will bring us closer to the sample point.
  538. // However, the numerator value used the vector from the sample point to the curve point,
  539. // so negative value implies a positive movement.
  540. if ( flNumerator < 0 )
  541. {
  542. flStepSize = flBaseStepSize;
  543. }
  544. else
  545. {
  546. flStepSize = -flBaseStepSize;
  547. }
  548. // Starting with the current parameter, move the parameter by the calculated step interval
  549. // and evaluate the result. Continue as long as the result is closer than the previous
  550. // best result and the specified maximum number of steps has not been reached.
  551. while ( nStep < nMaxSteps )
  552. {
  553. flStepParam = MAX( 0.0f, MIN( 1.0f, flStepParam + flStepSize ) );
  554. float sp = flStepParam;
  555. stepPoint = ( b3a * sp * sp * sp ) + ( b3b * sp * sp ) + ( b3c * sp ) + b3d;
  556. flStepDist = point.DistToSqr( stepPoint );
  557. if ( flStepDist >= flBestStepDist )
  558. break;
  559. flBestStepParam = flStepParam;
  560. flBestStepDist = flStepDist;
  561. ++nStep;
  562. }
  563. flNewParam = flBestStepParam;
  564. }
  565. // Update the parameter to the new value which provides
  566. // a closer point on the curve to the sample point.
  567. Assert( flNewParam >= 0.0f );
  568. Assert( flNewParam <= 1.0f );
  569. pParams[ iPoint ] = flNewParam;
  570. // Save the old parameter so it may be used by the next point
  571. // to determine the iteration range if the root finding fails
  572. flPrevParam = t;
  573. }
  574. }
  575. //-----------------------------------------------------------------------------
  576. // Compute the maximum squared distance between the specified points and the
  577. // curve
  578. //-----------------------------------------------------------------------------
  579. template < class POINT_TYPE >
  580. float CCubicBezierCurve< POINT_TYPE >::ComputeMaxError( const POINT_TYPE *pPoints, const float *pParams, int nNumPoints ) const
  581. {
  582. float flMaxError = 0.0f;
  583. POINT_TYPE a, b, c, d;
  584. ComputeFactors( a, b, c, d );
  585. for ( int iPoint = 0; iPoint < nNumPoints; ++iPoint )
  586. {
  587. const POINT_TYPE &samplePoint = pPoints[ iPoint ];
  588. float t = pParams[ iPoint ];
  589. POINT_TYPE curvePoint = ( a * t * t * t ) + ( b * t * t ) + ( c * t ) + d;
  590. float flDistSQ = samplePoint.DistToSqr( curvePoint );
  591. flMaxError = MAX( flDistSQ, flMaxError );
  592. }
  593. return flMaxError;
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Compute the sum of the squared distance between the specified points and the
  597. // curve
  598. //-----------------------------------------------------------------------------
  599. template < class POINT_TYPE >
  600. float CCubicBezierCurve< POINT_TYPE >::ComputeErrorSum( const POINT_TYPE *pPoints, const float *pParams, int nNumPoints ) const
  601. {
  602. float flErrorSum = 0.0f;
  603. POINT_TYPE a, b, c, d;
  604. ComputeFactors( a, b, c, d );
  605. for ( int iPoint = 0; iPoint < nNumPoints; ++iPoint )
  606. {
  607. const POINT_TYPE &samplePoint = pPoints[ iPoint ];
  608. float t = pParams[ iPoint ];
  609. POINT_TYPE curvePoint = ( a * t * t * t ) + ( b * t * t ) + ( c * t ) + d;
  610. float flDistSQ = samplePoint.DistToSqr( curvePoint );
  611. flErrorSum += flDistSQ;
  612. }
  613. return flErrorSum;
  614. }
  615. #endif