贝塞尔三次方的公式,涉及到4个点。如p1,p2,p3,p4,其中p1是起点,p4是重点, p2,和p3是控制点。公式如下:


B(t) = p1 * (1 - t) * (1 - t) * (1 - t) +
       P2 * 3t * (1 - t) * (1 - t)      +
       p3 * 3t * t * (1 - t) *          +
       p3 * t * t * t;



那个指数不好打字,所以我把平方和立方都乘开了。t 在 [0 , 1]的范围内表示的是时间,B函数是在 t 时间的时刻,贝塞尔三次方曲线上的一个点的值。在一段连续的时间内,利用这个公式,我们就能计算出一系列的点,连接起来就是贝塞尔三次方曲线。P代表的点(x, y),所以我带入方程的时候,需要分别令p为x和y分别计算。

为了优化,我们现在要展开这个公式,就是展开每个括号。 以后变成以 t 为系数的方程。如下

B(t) = t  * t * t * (-p1 + 3p2 - 3p3 + p4) +
       3t * t * (p1 - 2p2 + p3) +
       3t * (-p1 + p2) +
       p1;



我们可以看到,p1234点都是已知的值,t 是变量, t 的变化就能计算出点的位置。如果我们假设贝塞尔上次曲线有10个点构成,那么每个点的时间就是 t = 1 / 10, 并且这10个点的间隔是相等的0.1, 0.2, 0.3 … 这样。 于是我们只要代入 t 的值,我们就能计算出10个点的所有值。



=======================================================

我们观察到,时间 t 是倍数递增的。比如上面就是0 .1, 0.2, 0.3 直到 1.0, 步长无论取值多少都是 1 / step 倍数递增。还可以看到点p1234都是定值,只有 t 是变化的。所以我们,令


D  =  (-p1 + 3p2 - 3p3 + p4)
c  = 3 * (p1 - 2p2 + p3) 
B  = 3 * (-p1 + p2) 
A  = p1
 
B(t) = A + tB + ttC + tttD




这里有一种算法,把乘法转换成加法。首先我们看一个函数如下:


f(t) =  tx;
 
f(1) = x;
f(2) = 2x;
f(3) = 3x;
f(4) = 4x;
 
// 我们可以看到这个f(t) = tx函数就可以用加法来计算;
// 有几个t就循环几次迭代出 f(t) 的值
while(t--) {
   x++;
}
 
// 那么 f(t) = t * t * x 这个函数如何计算:
 
f(1) = x;
            3x;
f(2) = 4x;       2x;
            5x;
f(3) = 9x;       2x;
            7X;
f(4) = 16x;
 
// 中间的数值是相邻的差值, 我们看到 2x 是一个定值,于是有:
        fx             dfx                 ddfx
        0              x                   2x
f(1)    x              3x
f(2)    4x             5x
f(3)    9x             7x
f(4)    16x             
 
// 以上规律不言自明, fx 存储了最终f(x) = t * t * x 的值
// 我们只要设定初始的 dfx, ddfx 即可
 
fx   = 0;
dfx  = x;
ddfx = 2x;
 
while(t--) {
   fx  += dfx;
   dfx += ddfx; 
}



// 我们再来看看  f(t) = t * t * t * x 这个函数如何转化,原理和上面一样
 
f(1) = x;
               7x;
f(2) = 8x;             12x;
               19x;           6x;
f(3) = 27x;            18x;
               37x;           6x;
f(4) = 64x;            24x;
               61x;
f(5) = 125x;
 
// 中间的数值是相邻的差值, 我们看到 2x 是一个定值,于是有:
      fx          dfx          ddfx          dddfx        
      0           x            6x            6x
f(1)  x           7x           12x
f(2)  8x          19x          18x
f(3)  27x         37x          24x
 
// fx 存储了最后 f(t) = t * t * t * x的数值,代码如下:
fx    = 0;
dfx   = x;
ddfx  = 6x;
dddfx = 6x;
 
while(t--) {
   fx   += dfx;
   dfx  += ddfx;
   ddfx += dddfx;
}



有了以上的算法,我们就可以B(t) = A + tB + ttC + tttD这个函数看成是 B(t) = A + f(B) + f(C) + f(D) 的新函数, 我们就可以应用把乘法转换成加法的运算了。

=======================================================

 

C语言实现如下:

// 定义: p1(x1, y1) p2(x2, y2) p3(x3, y3) p4(x4, y4)
 
// 曲线点的个数
int   pointNum = 10;
float t        = 1 / pointNum;
float t2       = t * t;
float t3       = t * t2;
 
// 预计算
float pre_3t   = 3 * t;
float pre_3t2  = 3 * t2;
float pre_6t2  = 6 * t2;
float pre_6t3  = 6 * t3;
 
// (p1 - 2p2 + p3)
float tmp1x = x1 - x2 * 2.0 + x3;
float tmp1y = y1 - y2 * 2.0 + y3;
 
// (-p1 + 3p2 - 3p3 + p4)
float tmp2x = (x2 - x3) * 3.0 - x1 + x4;
float tmp2y = (y2 - y3) * 3.0 - y1 + y4;
 
// p1
float fx = x1;
float fy = y1;
 
// (p2 - p1) * 3t + 
// (p1 - 2p2 + p3) * 3t2  + 
// (-p1 + 3p2 - 3p3 + p4) * t3
float dfx = (x2 - x1) * pre_3t + tmp1x * pre_3t2 + tmp2x * t3;
float dfy = (y2 - y1) * pre_3t + tmp1y * pre_3t2 + tmp2y * t3;
 
// (p1 - 2p2 + p3) * 6t2  +
// (-p1 + 3p2 - 3p3 + p4) * 6t3
float ddfx = tmp1x * pre_6t2 + tmp2x * pre_6t3;
float ddfy = tmp1y * pre_6t2 + tmp2y * pre_6t3;
 
// (-p1 + 3p2 - 3p3 + p4) * 6t3
float dddfx = tmp2x * pre_6t3;
float dddfy = tmp2y * pre_6t3;
 
// fx, fy 就是t值对应计算出的曲线值
while(pointNum--) {
     fx   += dfx;
     fy   += dfy;
     dfx  += ddfx;
     dfy  += ddfy;
     ddfx += dddfx;
     ddfy += dddfy;
 }