贝塞尔样条是一个允许用户控制节点处斜率的样条,是一种特殊的三次样条。
皮埃尔·贝塞尔在其为雷诺(Renault)汽车公司工作时有了这个创意。Paul de Casteljau在与雷诺竞争关系的汽车公司雪铁龙Citroen公司工作时也独立有了这个想法。这在两家公司都认为是工业秘密,事实上在贝塞尔发表了他的研究后,大家才知道两家公司都进行了相同的研究。今天贝塞尔曲线(Bézier curve)是计算机辅助设计和制造的奠基石。
平面贝塞尔样条的每一段由4个点
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
(
x
3
,
y
3
)
,
(
x
4
,
y
4
)
(x_{1}, y_{1}), (x_{2}, y_{2}), (x_{3}, y_{3}),(x_{4}, y_{4})
(x1,y1),(x2,y2),(x3,y3),(x4,y4)所确定。第一个点和最后一个点是样条的起点和终点,中间的两个点是控制点,曲线以切线方向
(
x
2
−
x
1
,
y
2
−
y
1
)
(x_{2}-x_{1}, y_{2}-y_{1})
(x2−x1,y2−y1)离开
(
x
1
,
y
1
)
(x_{1}, y_{1})
(x1,y1),并以切线方向
(
x
4
−
x
3
,
y
4
−
y
3
)
(x_{4}-x_{3}, y_{4}-y_{3})
(x4−x3,y4−y3)在
(
x
4
,
y
4
)
(x_{4}, y_{4})
(x4,y4)点结束。
对于贝塞尔曲线,给定端点
(
x
1
,
y
1
)
,
(
x
4
,
y
4
)
(x_{1}, y_{1}), (x_{4}, y_{4})
(x1,y1),(x4,y4)和控制点
(
x
2
,
y
2
)
,
(
x
3
,
y
3
)
(x_{2}, y_{2}), (x_{3}, y_{3})
(x2,y2),(x3,y3),设
b
x
=
3
(
x
2
−
x
1
)
c
x
=
3
(
x
3
−
x
2
)
−
b
x
d
x
=
x
4
−
x
1
−
b
x
−
c
x
b
y
=
3
(
y
2
−
y
1
)
c
y
=
3
(
y
3
−
y
2
)
−
b
y
d
y
=
y
4
−
y
1
−
b
y
−
c
y
b_{x}=3(x_{2}-x_{1})\\ c_{x}=3(x_{3}-x_{2})-b_{x}\\ d_{x}=x_{4}-x_{1}-b_{x}-c_{x}\\ b_{y}=3(y_{2}-y_{1})\\ c_{y}=3(y_{3}-y_{2})-b_{y}\\ d_{y}=y_{4}-y_{1}-b_{y}-c_{y}
bx=3(x2−x1)cx=3(x3−x2)−bxdx=x4−x1−bx−cxby=3(y2−y1)cy=3(y3−y2)−bydy=y4−y1−by−cy
定义在
0
≤
t
≤
1
0\leq t \leq 1
0≤t≤1的贝塞尔曲线如下:
x ( t ) = x 1 + b x t + c x t 2 + d x t 3 y ( t ) = y 1 + b y t + c y t 2 + d y t 3 x(t) = x_{1}+b_{x}t+c_{x}t^{2}+d_{x}t^{3}\\ y(t) = y_{1}+b_{y}t+c_{y}t^{2}+d_{y}t^{3} x(t)=x1+bxt+cxt2+dxt3y(t)=y1+byt+cyt2+dyt3
应用1 绘制贝塞尔曲线
找出贝塞尔曲线(x(t), y(t))通过点(x,y)=(1, 1)和(2, 2),控制点为(1, 3)和(3, 3)。
Python实现程序如下:
# -*- coding: utf-8 -*-
# @Time : 2023/3/9 10:56
# @Author : Jclian91
# @File : Bezier_curve.py
# @Place : Xuhui, Shanghai
# solve Bézier curve with given four points
import matplotlib.pyplot as plt
def get_bezier_curve(pass_points, control_points):
p_point1, p_point2 = pass_points
x1, y1 = p_point1
x4, y4 = p_point2
c_point1, c_point2 = control_points
x2, y2 = c_point1
x3, y3 = c_point2
# parameter in bezier curve
b_x = 3 * (x2-x1)
c_x = 3 * (x3-x2) - b_x
d_x = x4 - x1 - b_x - c_x
b_y = 3 * (y2 - y1)
c_y = 3 * (y3 - y2) - b_y
d_y = y4 - y1 - b_y - c_y
# curve
n = 1000 # sample points in interval [0, 1]
step = 1/n
x_list, y_list = [], []
t = 0
for i in range(n):
t += step
x_list.append(x1 + b_x * t + c_x * (t**2) + d_x * (t**3))
y_list.append(y1 + b_y * t + c_y * (t ** 2) + d_y * (t ** 3))
return x_list, y_list
# plot bezier curve
def plot_bezier_curve(pass_points, control_points):
x_list, y_list = get_bezier_curve(pass_points, control_points)
plt.plot(x_list, y_list, label='bezier_curve', color='red')
plt.title(f'Bezier Curve')
plt.legend()
# plt.show()
plt.savefig(f"bezier_curve.png")
if __name__ == '__main__':
# example curve
p_points = [(1, 1), (2, 2)]
c_points = [(1, 3), (3, 3)]
plot_bezier_curve(p_points, c_points)
绘制的贝塞尔曲线如下:
绘制计算机字符
在这个例子中,我们将解释如何使用二维贝塞尔曲线划出Times Roman字体的T字母。
Python实现程序如下:
# -*- coding: utf-8 -*-
# @Time : 2023/3/9 10:56
# @Author : Jclian91
# @File : Bezier_curve.py
# @Place : Xuhui, Shanghai
# solve Bézier curve with given four points
import matplotlib.pyplot as plt
def get_bezier_curve(pass_points, control_points):
p_point1, p_point2 = pass_points
x1, y1 = p_point1
x4, y4 = p_point2
c_point1, c_point2 = control_points
x2, y2 = c_point1
x3, y3 = c_point2
# parameter in bezier curve
b_x = 3 * (x2-x1)
c_x = 3 * (x3-x2) - b_x
d_x = x4 - x1 - b_x - c_x
b_y = 3 * (y2 - y1)
c_y = 3 * (y3 - y2) - b_y
d_y = y4 - y1 - b_y - c_y
# curve
n = 1000 # sample points in interval [0, 1]
step = 1/n
x_list, y_list = [], []
t = 0
for i in range(n):
t += step
x_list.append(x1 + b_x * t + c_x * (t**2) + d_x * (t**3))
y_list.append(y1 + b_y * t + c_y * (t ** 2) + d_y * (t ** 3))
return x_list, y_list
# plot character T in Times Roman Font
def plot_t_in_times_roman_font():
points = [[(237, 620), (237, 620), (237, 120), (237, 120)],
[(237, 120), (237, 35), (226, 24), (143, 19)],
[(143, 19), (143, 19), (143, 0), (143, 0)],
[(143, 0), (143, 0), (435, 0), (435, 0)],
[(435, 0), (435, 0), (435, 19), (435, 19)],
[(435, 19), (353, 23), (339, 36), (339, 109)],
[(339, 109), (339, 108), (339, 620), (339, 620)],
[(339, 620), (339, 620), (339, 620), (339, 620)],
[(339, 620), (507, 620), (529, 602), (552, 492)],
[(552, 492), (552, 492), (576, 492), (576, 492)],
[(576, 492), (576, 492), (570, 662), (570, 662)],
[(570, 662), (570, 662), (6, 662), (6, 662)],
[(6, 662), (6, 662), (0, 492), (0, 492)],
[(0, 492), (0, 492), (24, 492), (24, 492)],
[(24, 492), (48, 602), (71, 620), (183, 620)],
[(183, 620), (183, 620), (237, 620), (237, 620)]
]
for point_list in points:
pass_points, control_points = [point_list[0], point_list[-1]], point_list[1:3]
x_list, y_list = get_bezier_curve(pass_points, control_points)
plt.plot(x_list, y_list, color='black')
plt.title('Bezier Curve')
# plt.show()
plt.savefig("T_in_Times_Roman_Font.png")
if __name__ == '__main__':
# T in Times Roman Font with Bézier Curve
plot_t_in_times_roman_font()
运行结果如下:
当前使用贝塞尔曲线可以生成数百种不同字体的字符,显示在计算机屏幕或者打印机上。尽管字体信息很多年来都是一个秘密,但是现在一部分在网上已经公开。因此,今天贝塞尔曲线(Bézier curve)是计算机辅助设计和制造的奠基石。