1. 原理解释(均匀节点)
- 对于给定Bezier曲线上的插值点 P i P_i Pi,通过 P i = r ( u i ) = ∑ j = 0 n J n , j ( u i ) V P_i = r(u_i)=\sum_{j=0}^n{J_{n,j}(u_i)V} Pi=r(ui)=∑j=0nJn,j(ui)V;
- 其中 V = [ V 0 , V 1 , ⋯ , V n ] V=[V_0,V_1,\cdots,V_n] V=[V0,V1,⋯,Vn],称 V i V_i Vi为型值点, P = [ P 1 , P 2 , ⋯ , P n ] P = [P_1,P_2,\cdots,P_n] P=[P1,P2,⋯,Pn],且 P i P_i Pi为给定值,而 u i u_i ui为 [ 0 , 1 ] [0,1] [0,1]范围内的一个节点,在本程序中我们认为 u i u_i ui为均匀节点,即n次Bezier曲线在 [ 0 , 1 ] [0,1] [0,1]上有n+1个节点,例 n = 4 , u = [ 0 , 0.25 , 0.5 , 0.75 , 1 ] n=4,u = [0,0.25,0.5,0.75,1] n=4,u=[0,0.25,0.5,0.75,1]。
- 由Bezier曲线的端点性质可以知道曲线在端点处型值点与插值点为同一个点。
- 结果展示:
bezier反解作图
2. 原理解释(非均匀节点)
3. Jupyter程序
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import math
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #该语句解决图像中的“-”负号的乱码问题
# 阶乘函数
def jc(x):
return math.factorial(x)
def Bernstein(n,m):
u1 = np.linspace(0,1,n+1) # u1 代表插值点对应的均匀点 ui
u2 = np.linspace(0,1,m) # m 代表画曲线时 u 的取点数量
J1 = pd.DataFrame() # 存储伯恩斯坦基函数在给定 u1 下系数矩阵
J2 = pd.DataFrame() # 存储为绘制曲线的系数矩阵
for i in range(n+1):
k = i
column = 'C'+str(i)
C_nk = jc(n)/(jc(k)*jc(n-k))
J_nk1 = C_nk*pow(u1,k)*pow(1-u1,n-k)
J_nk2 = C_nk*pow(u2,k)*pow(1-u2,n-k)
J1[column] = J_nk1
J2[column] = J_nk2
J1 = np.around(np.matrix(J1).I,2) # 将伯恩斯坦基函数取逆后截取两位小数
# print(J1,J2)
return J1,J2
# 输入n
def Bezier_inverse():
n = int(input("请输入n(n为Bezier次数):"))
m = int(input("请输入m(m为u的取值个数):"))
print("请输入{0}个插值点".format(n+1))
x = []
y = []
for i in range(n+1):
x1 = float(input("请输入第{0}个点的x坐标:".format(i+1)))
y1 = float(input("请输入第{0}个点的y坐标:".format(i+1)))
print("添加坐标点({0},{1})\n".format(x1,y1))
x.append(x1)
y.append(y1)
J1,J2 = Bernstein(n,m) # J1为一个 n+1*n+1 的矩阵
X_con = [] # 存储控制点的坐标
Y_con = []
X_bez = []
Y_bez = []
X_con = J1*np.matrix(x).T # 计算控制点的X值
Y_con = J1*np.matrix(y).T # 计算控制点的Y值
for i in range(m):
a = sum(J2.iloc[i,:]*X_con.T.tolist()[0]) # x为所有点x轴的值
b = sum(J2.iloc[i,:]*Y_con.T.tolist()[0]) # y为所有点y轴的值
X_bez.append(a)
Y_bez.append(b)
# 绘制Bezier曲线
fig = plt.figure(figsize=(10,8))
fig.suptitle(str(n)+'次Bezier曲线',fontsize=16)
plt.plot(X_con,Y_con,marker='o',markerfacecolor='white') # 画空心圆
plt.plot(x,y,'ro')
plt.plot(X_bez,Y_bez) # 画空心圆
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.show()
4. 封装至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):
self.ax = ax
self.m = m
self.n = n
self.x = []
self.y = []
self.i = 0
self.cid = self.ax.figure.canvas.mpl_connect('button_press_event', self)
def jc(self,a): # 阶乘
return math.factorial(a)
def Bernstein(self):
u1 = np.linspace(0,1,self.n+1)
u2 = np.linspace(0,1,self.m) # m代表[0,1]上取多少个点
J1 = pd.DataFrame() # 存储伯恩斯坦基函数在给定u1下的的值
J2 = pd.DataFrame() # 存储为给定u的值
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_nk1 = C_nk*pow(u1,k)*pow(1-u1,self.n-k)
J_nk2 = C_nk*pow(u2,k)*pow(1-u2,self.n-k)
J1[column] = J_nk1
J2[column] = J_nk2
J1 = np.around(np.matrix(J1).I,2)
return J1,J2
# 输入n
def Bezier_inverse(self):
# 调用Bernstein基函数
J1,J2 = self.Bernstein()
X_con = [] # 存储控制点的坐标
Y_con = []
X_bez = [] # 存储Bezier曲线上的所有点
Y_bez = []
X_con = J1*np.matrix(self.x).T # 计算控制点的X值
Y_con = J1*np.matrix(self.y).T # 计算控制点的Y值
for i in range(self.m):
a = sum(J2.iloc[i,:]*X_con.T.tolist()[0]) # x为所有点x轴的值
b = sum(J2.iloc[i,:]*Y_con.T.tolist()[0]) # y为所有点y轴的值
X_bez.append(a)
Y_bez.append(b)
self.ax.plot(X_con,Y_con,marker='o',markerfacecolor='white') # 画空心圆
# self.ax.plot(self.x,self.y,'b') # 连线
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_inverse()
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的取值个数):"))
print("\n请在画布上依次点击{0}个点".format(n+1))
x1 = [] # 存储figure的x轴区间
y1 = [] # 存储figure的y轴区间
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)
# test.click1()
plt.show()
if __name__ == '__main__':
main()