贝塞尔曲线


    对给定的 n+1 个点,可以作出 n 阶的贝塞尔曲线。其中最前和最后这两个点在曲线上,其余 n-1 个中间点是控制点,主要用于控制曲线的形状,不一定在曲线上。
    假如给定的 n+1 个点是 (x0,y0),...,(xn,yn), 以 t 作为参数,则生成的曲线上的所有点可以表示如下:
       n
  x =  ∑ C(n,i) *t^i *(1-t)^(n-i) *xi
      i=0

       n
  y =  ∑ C(n,i) *t^i *(1-t)^(n-i) *yi
      i=0
    其中 x,y 分别是生成的曲线上的点的 横坐标 和 纵坐标。
    t 是参数,其有效范围是 [0,1]。
    C(n,i)就是组合数,比如 C(3,1) = 3。


实用 3 阶贝塞尔曲线:
    实际应用中,常用 3 阶的贝塞尔曲线,也就是要对 4 个点进行计算。
设 已知 的4点为 p0(x0,y0),p1(x1,y1),p2(x2,y2),p3(x3,y3),其中 0 和 3 是端点, 1,2是控制点,则 该曲线上的所有点表示为:
  x = (1-t)^3 *x0 + 3*t*(1-t)^2 *x1 + 3*t^2*(1-t) *x2 + t^3 *x3
  y = (1-t)^3 *y0 + 3*t*(1-t)^2 *y1 + 3*t^2*(1-t) *y2 + t^3 *y3
  其中 0 <= t <= 1.


直接的简单实现:
    按照公式,对每个t,代入式子就可以得到曲线上对应点的坐标了。当然具体实现时,可根据适当的间隔取  t 值,然后用直线将这些值对应的点连起来就是了。
    类似于多项式的计算,在计算机上计算的时候,可以用 德卡斯特里奥算法 来求出,而不必直接计算高次幂。
设四个控制顶点是p0,p1,p2,p3,对每个具体的 t,可以做出下面的表:
p00
p10  p11
p20  p21  p22
p30  p31  p32  p33

其中:
p00 = p0
p10 = p1
p20 = p2
p30 = p3

p11 = p00*(1-t) + p10*t
p21 = p10*(1-t) + p20*t
p31 = p20*(1-t) + p30*t

p22 = p11*(1-t) + p21*t
p32 = p21*(1-t) + p31*t

p33 = p22*(1-t) + p32*t
通过验证可以知道,p33就是要求的值。

代码略。


简单实现遇到的问题:
    上面说的简单实现,t 的间距不好确定。因为曲线的形状可能千奇百怪,间隔太大则误差太大,太小的话则效率太低,难以确定一个对所有情况都合适的间隔值。而在实际应用中,则是用 递归 的方法来解决这个问题的。


实际中的递归处理:
    回头看上面求值的那个算法,当取 t=1/2 时, t = 1-t = 1/2,计算过程可以简化如下:
p00
p10  p11
p20  p21  p22
p30  p31  p32  p33
其中:
pi0跟上面一样,分别就是那4个输入点p0,p1,p2,p3
pij = [ p(i-1)(j-1) + p(i-1)j ] / 2    (简化的就是这个计算过程)

显然,p33就是 t=1/2 时所对应曲线上的点。
而且,
    如果以p00,p11,p22,p33为4个新的输入点,生成新的3阶贝塞尔曲线,则该曲线跟原来p0~p3生成的曲线在t∈[0,1/2]的部分“完全重合”。
    如果以p33,p32,p31,p30为4个新的输入点,生成新的3阶贝塞尔曲线,则该曲线跟原来p0~p3生成的曲线在t∈[1/2,1]的部分“完全重合”。
这里注意新输入点的输入顺序。

    上面漏了说贝塞尔曲线跟输入点的一个关系,就是贝塞尔曲线总是处在 输入点的凸包 内。那么可以得知,曲线上的点到 线段p0p3(就是两个端点) 的距离不大于 max(p1到p0p3的距离, p2到p0p3的距离)。
    而采用 德卡斯特里奥算法,取t=1/2 所求出来的中间点pij,其实就是 p(i-1)(j-1),pi(j-1) 的中点。比划一下也可以看出,中间点全部都在 p00,p10,p20,p30的凸包里面,也就是 p00,p11,p22,p33所形成的凸包 和 p33,p32,p21,p30的凸包也被包含在 p0,p1,p2,p3 的凸包里面。

综上所述:
    p00,p11,p22,p33 和 p33,p32,p31,p30 所生成的曲线拼起来,就是p0,p1,p2,p3生成的曲线。
    对p0,p1,p2,p3,用线段 p0p3 代替 生成的曲线,并以曲线上的点到线段的最大距离为误差,则 p00,p11,p22,p33 和 p33,p32,p31,p30的误差比 p0,p1,p2,p3 的要小。

所以,得到的递归生成曲线步骤如下:
1、
    对 p0,p1,p2,p3 4个点,先检查 
     max( d(p1, p0p3), d(p2, p0p3) ) < E (d()是点到直线距离,E是给定的误差范围,表示p0,p2,p3,p3的凸包的边跟生成曲线的最大差距。) 
    是否成立。
    如果 满足 条件,则简单用线段 p0p3 代替生成的曲线,返回。
    如果不满足,继续。

2、
    对 p0,p1,p2,p3 4个点,进行 t=1/2 的德卡里奥算法。
    对得到的 p00,p11,p22,p33,递归进行步骤 1 的操作;
    对得到的 p33,p32,p31,p30,递归进行步骤 1 的操作。
    返回。
posted @ 2010-09-05 22:06  zhh  阅读(640)  评论(0编辑  收藏  举报