@zsh-o
2020-01-01T21:16:06.000000Z
字数 3306
阅读 849
UII
# coding = utf-8
import numpy as np
from matplotlib import pyplot as plt
import math
# bezier
# TODO: LUT
def B(n, k, t):
up = 1
down = 1
for i in range(k):
up *= (n - i)
down *= (i + 1)
left = 1
for i in range(k):
left *= t
right = 1
for i in range(n - k):
right *= (1 - t)
return up / down * left * right
def bezier(c, num_resample=100): # Bezier曲线公式转换,获取x和y
# bezier
n = c.shape[0] - 1
ts = np.linspace(0, 1, num_resample)
line = np.zeros((num_resample, 2))
for i in range(ts.shape[0]):
t = ts[i]
pt = 0
for k in range(n + 1):
pt += (B(n, k, t) * c[k])
line[i] = pt
return line
# bezier End
# bspline
def 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...n
m = n + k + 1 # m > 2k
u = [i for i in range(m + 1)]
u = np.array(u)
u[:k] = k
u[-(k+1):] = m - k
line = []
t = k
step = (n + 1 - t) / (num_resample - 1)
while t <= (n + 1 + 1e-5):
i = math.floor(t)
if i == m - k:
i = m - k - 1
line.append(deboor(k - 1, i, t, u, c, k))
t += step
return np.vstack(line)
# bspline End
class MyB:
def __init__(self, line):
self.line = line
self.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: return
self.press = 1
def on_motion(self, event): # 鼠标拖动调用
if event.inaxes != self.line.axes: return
if self.press is None: return
if self.pick is None: return
if self.motion is None: # 整个if获取鼠标选中的点是哪个点
self.motion = 1
x = self.xs
xdata = event.xdata
ydata = event.ydata
index_01 = 0
for i in x:
if abs(i - xdata) < 0.02: # 0.02 为点的半径
if abs(self.ys[index_01] - ydata) < 0.02: break
index_01 = index_01 + 1
self.index_02 = index_01
if self.index_02 is None: return
self.xs[self.index_02] = event.xdata # 鼠标的坐标覆盖选中的点的坐标
self.ys[self.index_02] = event.ydata
self.draw()
def on_release(self, event): # 鼠标按下调用
if event.inaxes != self.line.axes: return
if self.pick == None: # 如果不是选中点,那就添加点
self.xs.append(event.xdata)
self.ys.append(event.ydata)
if self.pick == 1 and self.motion != 1: # 如果是选中点,但不是拖动点,那就降阶
x = self.xs
xdata = event.xdata
ydata = event.ydata
index_01 = 0
for i in x:
if abs(i - xdata) < 0.02:
if abs(self.ys[index_01] - ydata) < 0.02: break
index_01 = index_01 + 1
self.xs.pop(index_01)
self.ys.pop(index_01)
self.draw()
self.pick = None # 所有状态恢复,鼠标按下到稀放为一个周期
self.motion = None
self.press = None
self.index_02 = None
def on_picker(self, event): # 选中调用
self.pick = 1
def draw(self): # 绘图
self.line.clear() # 不清除的话会保留原有的图
self.line.axis([0, 1, 0, 1]) # x和y范围0到1
control_points = np.array([self.xs, self.ys]).T
k = 4
if 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 line
fig = 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()