[关闭]
@zsh-o 2020-01-01T21:16:06.000000Z 字数 3306 阅读 864

Beizer BSpline

UII


  1. # coding = utf-8
  2. import numpy as np
  3. from matplotlib import pyplot as plt
  4. import math
  5. # bezier
  6. # TODO: LUT
  7. def B(n, k, t):
  8. up = 1
  9. down = 1
  10. for i in range(k):
  11. up *= (n - i)
  12. down *= (i + 1)
  13. left = 1
  14. for i in range(k):
  15. left *= t
  16. right = 1
  17. for i in range(n - k):
  18. right *= (1 - t)
  19. return up / down * left * right
  20. def bezier(c, num_resample=100): # Bezier曲线公式转换,获取x和y
  21. # bezier
  22. n = c.shape[0] - 1
  23. ts = np.linspace(0, 1, num_resample)
  24. line = np.zeros((num_resample, 2))
  25. for i in range(ts.shape[0]):
  26. t = ts[i]
  27. pt = 0
  28. for k in range(n + 1):
  29. pt += (B(n, k, t) * c[k])
  30. line[i] = pt
  31. return line
  32. # bezier End
  33. # bspline
  34. def deboor(r, i, t, u, c, k):
  35. if r == 0:
  36. return c[i - 1]
  37. omega = (t - u[i]) / (u[i + k - r] - u[i] + 1e-7)
  38. return (1 - omega) * deboor(r - 1, i - 1, t, u, c, k) + \
  39. omega * deboor(r - 1, i, t, u, c, k)
  40. def bspline(c, num_resample=100, k=3):
  41. # c = np.concatenate([c, c[-1].reshape(-1, 2)])
  42. n = c.shape[0] # for travel 0...n
  43. m = n + k + 1 # m > 2k
  44. u = [i for i in range(m + 1)]
  45. u = np.array(u)
  46. u[:k] = k
  47. u[-(k+1):] = m - k
  48. line = []
  49. t = k
  50. step = (n + 1 - t) / (num_resample - 1)
  51. while t <= (n + 1 + 1e-5):
  52. i = math.floor(t)
  53. if i == m - k:
  54. i = m - k - 1
  55. line.append(deboor(k - 1, i, t, u, c, k))
  56. t += step
  57. return np.vstack(line)
  58. # bspline End
  59. class MyB:
  60. def __init__(self, line):
  61. self.line = line
  62. self.index_02 = None # 保存拖动的这个点的索引
  63. self.press = None # 状态标识,1为按下,None为没按下
  64. self.pick = None # 状态标识,1为选中点并按下,None为没选中
  65. self.motion = None # 状态标识,1为进入拖动,None为不拖动
  66. self.xs = list() # 保存点的x坐标
  67. self.ys = list() # 保存点的y坐标
  68. self.cidpress = line.figure.canvas.mpl_connect('button_press_event', self.on_press) # 鼠标按下事件
  69. self.cidrelease = line.figure.canvas.mpl_connect('button_release_event', self.on_release) # 鼠标放开事件
  70. self.cidmotion = line.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) # 鼠标拖动事件
  71. self.cidpick = line.figure.canvas.mpl_connect('pick_event', self.on_picker) # 鼠标选中事件
  72. def on_press(self, event): # 鼠标按下调用
  73. if event.inaxes != self.line.axes: return
  74. self.press = 1
  75. def on_motion(self, event): # 鼠标拖动调用
  76. if event.inaxes != self.line.axes: return
  77. if self.press is None: return
  78. if self.pick is None: return
  79. if self.motion is None: # 整个if获取鼠标选中的点是哪个点
  80. self.motion = 1
  81. x = self.xs
  82. xdata = event.xdata
  83. ydata = event.ydata
  84. index_01 = 0
  85. for i in x:
  86. if abs(i - xdata) < 0.02: # 0.02 为点的半径
  87. if abs(self.ys[index_01] - ydata) < 0.02: break
  88. index_01 = index_01 + 1
  89. self.index_02 = index_01
  90. if self.index_02 is None: return
  91. self.xs[self.index_02] = event.xdata # 鼠标的坐标覆盖选中的点的坐标
  92. self.ys[self.index_02] = event.ydata
  93. self.draw()
  94. def on_release(self, event): # 鼠标按下调用
  95. if event.inaxes != self.line.axes: return
  96. if self.pick == None: # 如果不是选中点,那就添加点
  97. self.xs.append(event.xdata)
  98. self.ys.append(event.ydata)
  99. if self.pick == 1 and self.motion != 1: # 如果是选中点,但不是拖动点,那就降阶
  100. x = self.xs
  101. xdata = event.xdata
  102. ydata = event.ydata
  103. index_01 = 0
  104. for i in x:
  105. if abs(i - xdata) < 0.02:
  106. if abs(self.ys[index_01] - ydata) < 0.02: break
  107. index_01 = index_01 + 1
  108. self.xs.pop(index_01)
  109. self.ys.pop(index_01)
  110. self.draw()
  111. self.pick = None # 所有状态恢复,鼠标按下到稀放为一个周期
  112. self.motion = None
  113. self.press = None
  114. self.index_02 = None
  115. def on_picker(self, event): # 选中调用
  116. self.pick = 1
  117. def draw(self): # 绘图
  118. self.line.clear() # 不清除的话会保留原有的图
  119. self.line.axis([0, 1, 0, 1]) # x和y范围0到1
  120. control_points = np.array([self.xs, self.ys]).T
  121. k = 4
  122. if control_points.shape[0] >= k:
  123. line = self.b(control_points, k, 100) # B样条曲线
  124. self.line.plot(line[:, 0], line[:, 1])
  125. self.line.scatter(self.xs, self.ys, color='b', s=200, marker="o", picker=5) # 画点
  126. # self.line.plot(self.xs, self.ys, color='r') # 画线
  127. self.line.figure.canvas.draw() # 重构子图
  128. def b(self, c, k=3, num_resample=100): # Bezier曲线公式转换,获取x和y
  129. # line = bezier(c, num_resample)
  130. line = bspline(c, num_resample, k)
  131. return line
  132. fig = plt.figure(2, figsize=(12, 6)) # 创建第2个绘图对象,1200*600像素
  133. ax = fig.add_subplot(111) # 一行一列第一个子图
  134. ax.set_title('My B')
  135. myBezier = MyB(ax)
  136. plt.xlabel('X')
  137. plt.ylabel('Y')
  138. plt.show()

image.png-45.1kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注