1. 几何作图原理
r i , l ( u ) = ( 1 − u ) r i , l − 1 ( u ) + u r i + 1 , l − 1 ( u ) r_{i,l}(u)=(1-u)r_{i,l-1}(u)+ur_{i+1,l-1}(u) ri,l(u)=(1−u)ri,l−1(u)+uri+1,l−1(u)
0 ≤ u ≤ 1 ; l = 1 , 2 , ⋯ , n ; i = 0 , 1 , ⋯ , n − l 0\le u\le1;l=1,2,\cdots,n;i=0,1,\cdots,n-l 0≤u≤1;l=1,2,⋯,n;i=0,1,⋯,n−l
r i , l ( u ) r_{i,l}(u) ri,l(u)直接理解为坐标点,当 l = 0 l=0 l=0时为控制顶点。
2. 程序
- 在Bezier曲线作图法的基础上添加了一个更新坐标点的函数renew_poin();
- 本程序依然使用class封装,实现了交互式取点的功能。
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import rand
import pandas as pd
import math
import random
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #该语句解决图像中的“-”负号的乱码问题
class click_curve:
# 在类中定义的带self的变量均为全局变量
def __init__(self,n,m,ax,t):
self.t = t
self.ax = ax
self.m = m
self.n = n
self.x = []
self.y = []
self.i = 0
self.ax.figure.canvas.mpl_connect('button_press_event', self) # 调用__call__执行
def jc(self,a): # 阶乘
return math.factorial(a)
def Bernstein(self):
u = np.linspace(0,1,self.m) # m代表[0,1]上取多少个点
J = pd.DataFrame() # 存储伯恩斯坦基函数的值
for i in range(self.n+1): # 输入n个点,n不为次数
k = i
column = 'C'+str(i)
C_nk = self.jc(self.n)/(self.jc(k)*self.jc(self.n-k))
J_nk = C_nk*pow(u,k)*pow(1-u,self.n-k)
J[column] = J_nk
return J
def renew_point(self,a,b,u):
x_new = []
y_new = []
for i in range(len(a)-1):
x_new.append((1-u)*a[i]+u*a[i+1])
y_new.append((1-u)*b[i]+u*b[i+1])
return x_new,y_new
# 输入n
def Bezier(self):
# 调用Bernstein基函数
J = self.Bernstein()
X_bez = []
Y_bez = []
for i in range(self.m):
a = sum(J.iloc[i,:]*list(self.x)) # x为所有点x轴的值
b = sum(J.iloc[i,:]*list(self.y)) # y为所有点y轴的值
X_bez.append(a)
Y_bez.append(b)
self.ax.plot(self.x,self.y,'b') # 连线
for i in range(self.n):
self.x,self.y = self.renew_point(self.x,self.y,self.t)
plt.plot(self.x,self.y,marker='o',markerfacecolor='white')
self.ax.plot(X_bez,Y_bez,'r') # 画曲线
print('success')
self.ax.figure.canvas.draw()
def __call__(self,event):
if event.inaxes != None:
if event.button==1 and self.i < self.n+1:
self.i+=1
# plt.ax.scatter(event.xdata, event.ydata)
self.x.append(event.xdata)
self.y.append(event.ydata)
print("输入坐标点为:({0:.2f},{1:.2f})".format(event.xdata, event.ydata))
self.ax.plot(event.xdata, event.ydata, 'bo',markerfacecolor='white')
self.ax.text(event.xdata+0.05, event.ydata+0.05,"({0:.2f},{1:.2f})".format(event.xdata, event.ydata))
self.ax.figure.canvas.draw()
elif self.i >= self.n+1:
print("绘图结束")
print("x取值:{0}".format(self.x))
print("y取值:{0}".format(self.y))
self.Bezier()
print("end")
return
else:
print("出界了")
# def click1(self):
# self.ax.figure.canvas.mpl_connect('button_press_event', self.click)
def main():
n = int(input("请输入次数:"))
x = input("请输入你希望的X轴区间(用空格隔开):").split(" ")
y = input("请输入你希望的Y轴区间(用空格隔开):").split(" ")
m = int(input("请输入m(m为u的取值个数):"))
t = float(input("请输入t(t为直线的分割位置,取值为[0,1]):"))
print("\n请在画布上依次点击{0}个点".format(n+1))
x1 = []
y1 = []
for i in range(len(x)):
x1.append(int(x[i]))
y1.append(int(y[i]))
fig = plt.figure()
ax1 = fig.add_subplot(111)
plt.xlim(x1[0],x1[1])
plt.ylim(y1[0],y1[1])
plt.title("{0}次Bezier曲线".format(n))
plt.yticks(fontsize=14)
plt.xticks(fontsize=14)
click_curve(n,m,ax1,t)
# test.click1()
plt.show()
if __name__ == '__main__':
main()
3. 结果
交互取点绘图——Bezier几何作图