@zsh-o
2020-01-01T13:16:06.000000Z
字数 3306
阅读 1136
UII
# coding = utf-8import numpy as npfrom matplotlib import pyplot as pltimport math# bezier# TODO: LUTdef B(n, k, t):up = 1down = 1for i in range(k):up *= (n - i)down *= (i + 1)left = 1for i in range(k):left *= tright = 1for i in range(n - k):right *= (1 - t)return up / down * left * rightdef bezier(c, num_resample=100): # Bezier曲线公式转换,获取x和y# beziern = c.shape[0] - 1ts = np.linspace(0, 1, num_resample)line = np.zeros((num_resample, 2))for i in range(ts.shape[0]):t = ts[i]pt = 0for k in range(n + 1):pt += (B(n, k, t) * c[k])line[i] = ptreturn line# bezier End# bsplinedef deboor(r, i, t, u, c, k):if r == 0:return c[i - 1]omega = (t - u[i]) / (u[i + k - r] - u[i] + 1e-7)return (1 - omega) * deboor(r - 1, i - 1, t, u, c, k) + \omega * deboor(r - 1, i, t, u, c, k)def bspline(c, num_resample=100, k=3):# c = np.concatenate([c, c[-1].reshape(-1, 2)])n = c.shape[0] # for travel 0...nm = n + k + 1 # m > 2ku = [i for i in range(m + 1)]u = np.array(u)u[:k] = ku[-(k+1):] = m - kline = []t = kstep = (n + 1 - t) / (num_resample - 1)while t <= (n + 1 + 1e-5):i = math.floor(t)if i == m - k:i = m - k - 1line.append(deboor(k - 1, i, t, u, c, k))t += stepreturn np.vstack(line)# bspline Endclass MyB:def __init__(self, line):self.line = lineself.index_02 = None # 保存拖动的这个点的索引self.press = None # 状态标识,1为按下,None为没按下self.pick = None # 状态标识,1为选中点并按下,None为没选中self.motion = None # 状态标识,1为进入拖动,None为不拖动self.xs = list() # 保存点的x坐标self.ys = list() # 保存点的y坐标self.cidpress = line.figure.canvas.mpl_connect('button_press_event', self.on_press) # 鼠标按下事件self.cidrelease = line.figure.canvas.mpl_connect('button_release_event', self.on_release) # 鼠标放开事件self.cidmotion = line.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) # 鼠标拖动事件self.cidpick = line.figure.canvas.mpl_connect('pick_event', self.on_picker) # 鼠标选中事件def on_press(self, event): # 鼠标按下调用if event.inaxes != self.line.axes: returnself.press = 1def on_motion(self, event): # 鼠标拖动调用if event.inaxes != self.line.axes: returnif self.press is None: returnif self.pick is None: returnif self.motion is None: # 整个if获取鼠标选中的点是哪个点self.motion = 1x = self.xsxdata = event.xdataydata = event.ydataindex_01 = 0for i in x:if abs(i - xdata) < 0.02: # 0.02 为点的半径if abs(self.ys[index_01] - ydata) < 0.02: breakindex_01 = index_01 + 1self.index_02 = index_01if self.index_02 is None: returnself.xs[self.index_02] = event.xdata # 鼠标的坐标覆盖选中的点的坐标self.ys[self.index_02] = event.ydataself.draw()def on_release(self, event): # 鼠标按下调用if event.inaxes != self.line.axes: returnif self.pick == None: # 如果不是选中点,那就添加点self.xs.append(event.xdata)self.ys.append(event.ydata)if self.pick == 1 and self.motion != 1: # 如果是选中点,但不是拖动点,那就降阶x = self.xsxdata = event.xdataydata = event.ydataindex_01 = 0for i in x:if abs(i - xdata) < 0.02:if abs(self.ys[index_01] - ydata) < 0.02: breakindex_01 = index_01 + 1self.xs.pop(index_01)self.ys.pop(index_01)self.draw()self.pick = None # 所有状态恢复,鼠标按下到稀放为一个周期self.motion = Noneself.press = Noneself.index_02 = Nonedef on_picker(self, event): # 选中调用self.pick = 1def draw(self): # 绘图self.line.clear() # 不清除的话会保留原有的图self.line.axis([0, 1, 0, 1]) # x和y范围0到1control_points = np.array([self.xs, self.ys]).Tk = 4if control_points.shape[0] >= k:line = self.b(control_points, k, 100) # B样条曲线self.line.plot(line[:, 0], line[:, 1])self.line.scatter(self.xs, self.ys, color='b', s=200, marker="o", picker=5) # 画点# self.line.plot(self.xs, self.ys, color='r') # 画线self.line.figure.canvas.draw() # 重构子图def b(self, c, k=3, num_resample=100): # Bezier曲线公式转换,获取x和y# line = bezier(c, num_resample)line = bspline(c, num_resample, k)return linefig = plt.figure(2, figsize=(12, 6)) # 创建第2个绘图对象,1200*600像素ax = fig.add_subplot(111) # 一行一列第一个子图ax.set_title('My B')myBezier = MyB(ax)plt.xlabel('X')plt.ylabel('Y')plt.show()
