贝塞尔曲线
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋。
贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算法开发,以稳定数值的方法求出贝兹曲线。
贝塞尔曲线原理
贝塞尔曲线由n个点来决定,其曲线轨迹可以由一个公式来得出:
其中n就代表了贝塞尔曲线是几阶曲线,该公式描述了曲线运动的路径。
以下我们来讨论一下,贝塞尔公式如何推导。
一阶贝塞尔曲线

设定图中运动的点为\(P_t\),\(t\)为运动时间,\(t∈(0,1)\),可得如下公式
二阶贝塞尔曲线

 
在二阶贝塞尔曲线中,已知三点恒定\((P_0,P_1,P_2)\),设定在\(P_0P_1\)中的点为\(P_a\),在\(P_1P_2\)中的点为\(P_b\),\(P_t\)在\(P_aP_b\)上的点,这三点都在相同时间t内做匀速运动。
由公式(2)可知:
将公式(3)(4)代入公式(5)中,可得:
三阶贝塞尔曲线

 
同理,根据以上的推导过程可得
由此可以推导
n阶贝塞尔曲线


设\(n+1\)个控制点\(P_0,P_1…P_n\),其中\(P_k=(X_k,Y_k,Z_k), 0≤k≤n\)
n次Bezier曲线为:
其中, \(B_in(t)\)是Bernstern基函数,即
曲线在两个端点处的边界条件
起点是第一个控制点,终点是最后一个控制点
\(P(0)=P_0,P(1)=P_n\)
证明
其他
Bezier曲线在端点处的一阶导数值可以由控制点坐标计算:
曲线起点的切线在头两个控制点连线上。
曲线终点的切线在最后两个控制点连线上。
Bezier曲线落在控制点的凸包内(凸多边形边界)
Bezier曲线的Casteljau算法
详细算法可看:https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html
前提:三切线定理

设\(P_0\)、\(P_0^2\)、\(P_2\)是一条抛物线上顺序三个不同的点。过\(P_0\)和\(P_2\)点的两切线交于\(P_1\)点,在\(P_0^2\)点的切线交\(P_0P_1\)和\(P_2P_1\)于\(P_0^1\)和\(P_1^1\),则如下比例成立:

这是所谓抛物线的三切线定理。
证明
当\(P_0\),\(P_2\)固定,引入参数\(t\),令上述比值为\(\frac {t}{1-t}\),即有:

\(t\)从\(0\)变到\(1\),第一、二式就分别表示控制二边形的第一、二条边,它们是两条一次Bezier曲线。将一、.二式代入第三式得:
当\(t\)从\(0\)变到\(1\)时,它表示了由三顶点\(P_0\)、\(P_1\)、\(P_2\)三点定义的一条二次Bezier曲线。并且表明:这二次Bezier曲线\(P_0^2\)可以定义为分别由前两个顶点\((P_0,P_1)\)和后两个顶点\((P_1,P_2)\)决定的一次Bezier曲线的线性组合。依次类推,由四个控制点定义的三次Bezier曲线\(P_0^3\)可被定义为分别由\((P_0,P_1,P_2)\)和\((P_1,P_2,P_3)\)确定的2条二次Bezier曲线的线性组合,由\((n+1)\)个控制点\(P_i(i=0,1,...,n)\)定义的n次Bezier曲线\(P_0^n\)可被定义为分别由前、后\(n\)个控制点定义的两条\((n-1)\)次Bezier曲线\(P_0^{n-1}\)与\(P_1^{n-1}\)的线性组合:
由此得到Bezier曲线的递推计算公式,也就是de Casteljau算法:

代码示例
import numpy as np
import matplotlib.pyplot as plt
import bezier
b_xs = []
b_ys = []
# xs表示原始数据
# n表示阶数
# k表示索引
def one_bezier_curve(a, b, t):
    return (1 - t) * a + t * b
def n_bezier_curve(xs, n, k, t):
    if n == 1:
        return one_bezier_curve(xs[k], xs[k + 1], t)
    else:
        return (1 - t) * n_bezier_curve(xs, n - 1, k, t) + t * n_bezier_curve(xs, n - 1, k + 1, t)
def bezier_curve(xs, ys, num, b_xs, b_ys):
    n = 5  # 采用5次bezier曲线拟合
    t_step = 1.0 / (num - 1)
    # t_step = 1.0 / num
    print(t_step)
    t = np.arange(0.0, 1 + t_step, t_step)
    print(len(t))
    for each in t:
        b_xs.append(n_bezier_curve(xs, n, 0, each))
        b_ys.append(n_bezier_curve(ys, n, 0, each))
def func():
    xs = [1.0, 2.1, 3.0, 4.0, 5.0, 6.0]
    ys = [0, 1.1, 2.1, 1.0, 0.2, 0]
    num = 20
    bezier_curve(xs, ys, num, b_xs, b_ys)  # 将计算结果加入到列表中
    print(b_xs, b_ys)
    plt.figure()
    plt.plot(b_xs, b_ys, 'r')  # bezier曲线
    # plt.plot(xs, ys)  # 原曲线
    # plt.show()
func()
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号