Python matplotlib绘制Bezier曲线
给定控制点的数量num和各控制点的坐标,动态绘制Bezier曲线。
思路:递归
1)当num=3时,已知三个控制点P0,P1,P2的坐标,连接P0P1,P1P2,给定一个0到1之间的数t,分别在P0P1和P1P2中寻找点T0和T1,使得满足:
P0T0 = tP0P1,P1T1 = tP1P2
然后在直线T0T1上寻找点Q0,使得 T0Q0 = t*T0T1,此时Q0即为Bezier曲线上一点。
2)当num=4时,连接四个控制点,给定一个0到1之间的数t,在三条线段上分别找T0,T1,T2三点,使得满足:
PiTi = t * PiPi+1,例如:P0T0 = t * P0P1
连接T0T1T2,此时可以转换成三个控制点T0,T1,T2的情况,利用1)过程得到Q0.
3)当num>4时,反复利用2)过程,直到控制点减少到3个时,利用1)过程得到Q0点。
4)过程1)到过程3)实际上只得到了Bezier曲线上的一个点,这时候只需要遍历在0到1范围内的所有实数t,就会得到无数个点Q,所有点连接即得Bezier曲线。
函数介绍:
def getValue(x1,x2,t):
return x1+(x2-x1)*t
t是0到1之间的数,用于计算直线上某一位置的点的坐标,x1与x2分别是线段起点和终点的x/y坐标值,可得到实数为t时,所求得的点的坐标值。
def Input(x,y):
pointNum = int(input("请输入控制点的个数:"))
for i in range(pointNum):
x.append(int(input("请输入第" + str(i + 1) + "个控制点的x值:")))
y.append(int(input("请输入第" + str(i + 1) + "个控制点的y值:")))
return
1)x,y为所有控制点的坐标值。比如x=[x0,x1,x2,…],为所有控制点的x值组成的数组(列表)。
2) x.append()向数组x从后插入一个数,这里为xi。
def PointTest():
for i in range(len(x)):
plt.text(x[i], y[i], 'P' + str(i))
return
在图像上显示所有控制点的标号,即P0,P1…
def BezierPoint():
for i in range(len(xLast)):
plt.plot(xLast[i],yLast[i],'*')
return
在图像上显示已得到的Bezier曲线上的点。其中xLast数组存放Bezier曲线上的点的x值。
def Draw(x,y,num):
if num==3:
x0 = getValue(x[0], x[1], i / cnt)
y0 = getValue(y[0], y[1], i / cnt)
x1 = getValue(x[1], x[2], i / cnt)
y1 = getValue(y[1], y[2], i / cnt)
plt.plot([x0, x1], [y0, y1])
#绘制端点为(x0,y0)和(x1,y1)的线段
xLast.append(getValue(x0, x1, i / cnt))
yLast.append(getValue(y0, y1, i / cnt))
plt.plot(xLast[i], yLast[i], '*')
#在(xLast[i], yLast[i])处绘制一点
plt.pause(0.2)#在此处暂停0.2秒
else:
xNext=[]
yNext=[]
#存放num=num-1时的控制点坐标值
for j in range(num-1):
xNext.append(getValue(x[j], x[j + 1], i / cnt))
yNext.append(getValue(y[j], y[j + 1], i / cnt))
plt.plot(xNext,yNext)
#依次连接数组中的所有点
plt.pause(0.2)
Draw(xNext,yNext,num-1)#递归调用
return
1)num为当前控制点的个数,该函数会递归调用Draw()函数,直到num=3。
2)将0到1平分成cnt份,此时 t = i / cnt(i是0到cnt之间的整数)。
def Show():
plt.grid(True)
plt.plot(x, y)
PointTest()
BezierPoint()
return
显示函数。
以下为完整代码:
import matplotlib.pyplot as plt
def getValue(x1,x2,t):
return x1+(x2-x1)*t
def Input(x,y):
pointNum = int(input("请输入控制点的个数:"))
for i in range(pointNum):
x.append(int(input("请输入第" + str(i + 1) + "个控制点的x值:")))
y.append(int(input("请输入第" + str(i + 1) + "个控制点的y值:")))
return
def PointTest():
for i in range(len(x)):
plt.text(x[i], y[i], 'P' + str(i))
return
def BezierPoint():
for i in range(len(xLast)):
plt.plot(xLast[i],yLast[i],'*')
return
def Draw(x,y,num):
if num==3:
x0 = getValue(x[0], x[1], i / cnt)
y0 = getValue(y[0], y[1], i / cnt)
x1 = getValue(x[1], x[2], i / cnt)
y1 = getValue(y[1], y[2], i / cnt)
plt.plot([x0, x1], [y0, y1])
xLast.append(getValue(x0, x1, i / cnt))
yLast.append(getValue(y0, y1, i / cnt))
plt.plot(xLast[i], yLast[i], '*')
plt.pause(0.2)
else:
xNext=[]
yNext=[]
for j in range(num-1):
xNext.append(getValue(x[j], x[j + 1], i / cnt))
yNext.append(getValue(y[j], y[j + 1], i / cnt))
plt.plot(xNext,yNext)
plt.pause(0.2)
Draw(xNext,yNext,num-1)
return
def Show():
plt.grid(True)
plt.plot(x, y)
PointTest()
BezierPoint()
return
#主函数
x=[]
y=[]
xLast=[]
yLast=[]
cnt = 10
Input(x,y)
fig = plt.figure()
ax = fig.add_subplot(111)
Show()
for i in range(cnt+1):
Draw(x,y,len(x))
plt.pause(1)
plt.cla()
Show()
plt.cla()
Show()
plt.plot(xLast,yLast)
plt.show()
结果展示:
请输入控制点的个数:3
请输入第1个控制点的x值:1
请输入第1个控制点的y值:1
请输入第2个控制点的x值:2
请输入第2个控制点的y值:5
请输入第3个控制点的x值:3
请输入第3个控制点的y值:1
请输入控制点的个数:4
请输入第1个控制点的x值:0
请输入第1个控制点的y值:0
请输入第2个控制点的x值:1
请输入第2个控制点的y值:5
请输入第3个控制点的x值:2
请输入第3个控制点的y值:7
请输入第4个控制点的x值:3
请输入第4个控制点的y值:4