# 贝塞尔曲线

## 二阶贝塞尔曲线

$=\left(1-t{\right)}^{2}{P}_{0}+t\left(1-t\right){P}_{1}+t\left(1-t\right){P}_{1}+{t}^{2}{P}_{2}$

$=\left(1-t\right)\left[\left(1-t\right){P}_{0}+t{P}_{1}\right]+t\left[\left(1-t\right){P}_{1}+t{P}_{2}\right]$

$=\left[\begin{array}{ccc}\left(1-t{\right)}^{2}& 2\left(1-t\right)t& {t}^{2}\end{array}\right]\left[\begin{array}{c}{P}_{0}\\ {P}_{1}\\ {P}_{2}\end{array}\right]$

$=\left[\begin{array}{ccc}1& t& {t}^{2}\end{array}\right]\left[\begin{array}{ccc}1& 0& 0\\ -2& 2& 0\\ 1& -2& 1\end{array}\right]\left[\begin{array}{c}{P}_{0}\\ {P}_{1}\\ {P}_{2}\end{array}\right]$

$B\left(t\right)=\left(1-t\right)\left[\left(1-t\right){P}_{0}+t{P}_{1}\right]+t\left[\left(1-t\right){P}_{1}+t{P}_{2}\right]$

• 首先计算出 ${P}_{0}$$P_{0}$ ${P}_{1}$$P_{1}$两个控制点之间的插值点 ${P}_{01}=\left(1-t\right){P}_{0}+t{P}_{1}$$P_{01} = (1-t) P_{0}+ t P_{1}$,
• 然后计算出 ${P}_{1}$$P_{1}$ ${P}_{2}$$P_{2}$两个控制点之间的插值点 ${P}_{12}=\left(1-t\right){P}_{1}+t{P}_{2}$$P_{12} = (1-t) P_{1}+ t P_{2}$,
• 最后再取 ${P}_{01}$$P_{01}$ ${P}_{02}$$P_{02}$两点之间的插值点 $P=\left(1-t\right){P}_{01}+t{P}_{12}$$P = (1-t) P_{01} + t P_{12}$, 点 $P$$P$ 即二阶贝塞尔曲线上的点。

$t=0.5$$t=0.5$时， 计算过程如下图所示:

${B}^{\prime }\left(t\right)=2\left(1-t\right)\left({P}_{1}-{P}_{0}\right)+2t\left({P}_{2}-{P}_{1}\right)$

${B}^{″}\left(t\right)=2\left({P}_{2}-2{P}_{1}+{P}_{0}\right)$

# Code

//
//   Author: Chunfeng Yang
//   Version: 0.2.0
//
// Parameters:
//    controlPoints  -- control points array of the Bezier curve
//             It contains the coordinates of control points
//             data type: Array
//
//    t     -- parameter t of the Biezier curve
//             data type: float
//
//    start -- the index of start control point in the strP array
//             data type: int
//
//    end   -- the index of end point in the strP array
//             data type: int
//
function BezierCurve( controlPoints, t, start, end )
{

if( undefined === controlPoints )
{
console.error('ERROR: undefined point array ');
return;
}

if( Object.prototype.toString.call( controlPoints ) !== '[object Array]' )
{
console.error('ERROR: invalided point array ');
return;
}

if( undefined === t )
{
console.log('Warning: t is undefined, using default value: t = 0.0 ');
t = 0.0;
}
if( undefined === start )
{
console.log('Warning: start is undefined, using default value: start = 0');
start = 0;
}
if( undefined === end )
{
console.log('Warning: end is undefined, using default value: end = controlPoints.length - 1');
end = controlPoints.length - 1;
}

if( parseInt(start) > parseInt(end) - 1 )
{
console.error('ERROR: start > end - 1 ');
return;
}

var len = controlPoints.length;
if( 0 === parseInt(len) )
{
console.error('ERROR: point array length is ZERO');
return;
}

if( parseInt(start) < 0 )
{
console.log('Warning: start is invalided, using default value: start = 0');
start = 0;
}
if( parseInt(end) < 1 )
{
console.log('Warning: end is invalided, using default value: end = controlPoints.length - 1');
end = controlPoints.length - 1;
}
if( parseInt(start) > parseInt(len) - 1 )
{
console.log('Warning: start is invalided, using default value: start = 0');
start = 0;
}
if( parseInt(end) > parseInt(len) - 1 )
{
console.log('Warning: end is invalided, using default value: end = controlPoints.length - 1');
end = controlPoints.length - 1;
}

if( 1 == ( parseInt(end) - parseInt(start) ) )
{
var p1 = controlPoints[start];
var p2 = controlPoints[end];

var result = [];
for( var item in p1 )
{
var delta =  parseFloat( p2[item] ) -  parseFloat( p1[item] )
result.push( parseFloat( p1[item] ) + t * parseFloat( delta ) );
}
return result;

}else {
var p1 = BezierCurve( controlPoints, t, start, parseInt(end) -1 ) ;
var p2 = BezierCurve( controlPoints, t, parseInt(start) + 1, end  );
var result = [];
for( var item in p1 )
{
result.push(( 1-parseFloat(t)) * parseFloat( p1[item] ) + t * parseFloat(p2[item]));
}
return result;
}

return;
}

Testing Scenario

var f;
var result = 0;

//
// Test 1
//
var a = "Hello world"
result = BezierCurve( a, 0.5, 0, 2 )
if( undefined == result )
{
console.log( "Test 1: controlPoints type is String test -- OK" );
}

//
// Test 2
//
var a = 3.2
result = BezierCurve( a, 0.5, 0, 2 )
if( undefined == result )
{
console.log( "Test 2: controlPoints type is Number test -- OK" );
}

//
// Test 3
//
var a = {"Helloworld":3}
result = BezierCurve( a, 0.5, 0, 2 )
if( undefined == result )
{
console.log( "Test 3: controlPoints type is JSON test -- OK" );
}

//
// Test 4
//
var a = {"Helloworld":3}
result = BezierCurve( f, 0.5, 0, 2 )
if( undefined == result )
{
console.log( "Test 4: undefined controlPoints test -- OK" );
}

//
// Test 5
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, f, 0, 2 )
if( undefined !== result )
{
if( ( 10 == parseInt(result[0]) ) & ( 40 == parseInt(result[1]) ) )
{
console.log( "Test 5: undefined t test -- OK" );
}
}

//
// Test 6
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, 0, f, 2 )
if( undefined !== result )
{
if( ( 10 == parseInt(result[0]) ) & ( 40 == parseInt(result[1]) ) )
{
console.log( "Test 6: undefined start test -- OK" );
}
}

//
// Test 7
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, 0, 0, f )
if( undefined !== result )
{
if( ( 10 == parseInt(result[0]) ) & ( 40 == parseInt(result[1]) ) )
{
console.log( "Test 7: undefined end test -- OK" );
}
}

//
// Test 8
//
var a = [ ];
result = BezierCurve( a, 0, 0, 2 )
if( undefined == result )
{
console.log( "Test 8: point array length is ZERO test -- OK" );
}

//
// Test 9
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, 0, 2, 2 )
if( undefined == result )
{
console.log( "Test 9: start > end - 1 test -- OK" );
}

//
// Test 10
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, 0, 9, 10 )
if( undefined !== result )
{
if( ( 10 == parseInt(result[0]) ) & ( 40 == parseInt(result[1]) ) )
{
console.log( "Test 10: invalided end test -- OK" );
}
}

//
// Test 11
//
var a = [[10, 40 ], [20, 30 ], [30, 40], [40, 30]];
result = BezierCurve( a, 0.5, 0, 2 )
if( undefined !== result )
{
if( ( 20 == parseInt(result[0]) ) & ( 35 == parseInt(result[1]) ) )
{
console.log( "Test 11:  calculation test -- OK" );
}
}
console.log(result);

Results:

ERROR: invalided point array
Test 1: controlPoints type is String test -- OK
ERROR: invalided point array
Test 2: controlPoints type is Number test -- OK
ERROR: invalided point array
Test 3: controlPoints type is JSON test -- OK
ERROR: undefined point array
Test 4: undefined controlPoints test -- OK
Warning: t is undefined, using default value: t = 0.0
Test 5: undefined t test -- OK
Warning: start is undefined, using default value: start = 0
Test 6: undefined start test -- OK
Warning: end is undefined, using default value: end = controlPoints.length - 1
Test 7: undefined end test -- OK
ERROR: point array length is ZERO
Test 8: point array length is ZERO test -- OK
ERROR: start > end - 1
Test 9: start > end - 1 test -- OK
Warning: start is invalided, using default value: start = 0
Warning: end is invalided, using default value: end = controlPoints.length - 1
Test 10: invalided end test -- OK
Test 11:  calculation test -- OK
[ 20, 35 ]