给定控制点的数量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