1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
| import numpy as np from matplotlib.lines import Line2D from matplotlib.artist import Artist from matplotlib.mlab import dist_point_to_segment
class PolygonInteractor(object): """ A polygon editor.
Key-bindings
't' toggle vertex markers on and off. When vertex markers are on, you can move them, delete them
'd' delete the vertex under point
'i' insert a vertex at point. You must be within epsilon of the line connecting two existing vertices
"""
showverts = True epsilon = 5
def __init__(self, ax, poly): if poly.figure is None: raise RuntimeError('You must first add the polygon to a figure ' 'or canvas before defining the interactor') self.ax = ax canvas = poly.figure.canvas self.poly = poly
x, y = zip(*self.poly.xy) self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True) self.ax.add_line(self.line)
self.cid = self.poly.add_callback(self.poly_changed) self._ind = None
canvas.mpl_connect('draw_event', self.draw_callback) canvas.mpl_connect('button_press_event', self.button_press_callback) canvas.mpl_connect('key_press_event', self.key_press_callback) canvas.mpl_connect('button_release_event', self.button_release_callback) canvas.mpl_connect('motion_notify_event', self.motion_notify_callback) self.canvas = canvas
def draw_callback(self, event): self.background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line)
def poly_changed(self, poly): 'this method is called whenever the polygon object is called' vis = self.line.get_visible() Artist.update_from(self.line, poly) self.line.set_visible(vis)
def get_ind_under_point(self, event): 'get the index of the vertex under point if within epsilon tolerance'
xy = np.asarray(self.poly.xy) xyt = self.poly.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.hypot(xt - event.x, yt - event.y) indseq, = np.nonzero(d == d.min()) ind = indseq[0]
if d[ind] >= self.epsilon: ind = None
return ind
def button_press_callback(self, event): 'whenever a mouse button is pressed' if not self.showverts: return if event.inaxes is None: return if event.button != 1: return self._ind = self.get_ind_under_point(event)
def button_release_callback(self, event): 'whenever a mouse button is released' if not self.showverts: return if event.button != 1: return self._ind = None
def key_press_callback(self, event): 'whenever a key is pressed' if not event.inaxes: return if event.key == 't': self.showverts = not self.showverts self.line.set_visible(self.showverts) if not self.showverts: self._ind = None elif event.key == 'd': ind = self.get_ind_under_point(event) if ind is not None: self.poly.xy = np.delete(self.poly.xy, ind, axis=0) self.line.set_data(zip(*self.poly.xy)) elif event.key == 'i': xys = self.poly.get_transform().transform(self.poly.xy) p = event.x, event.y for i in range(len(xys) - 1): s0 = xys[i] s1 = xys[i + 1] d = dist_point_to_segment(p, s0, s1) if d <= self.epsilon: self.poly.xy = np.insert( self.poly.xy, i+1, [event.xdata, event.ydata], axis=0) self.line.set_data(zip(*self.poly.xy)) break if self.line.stale: self.canvas.draw_idle()
def motion_notify_callback(self, event): 'on mouse movement' if not self.showverts: return if self._ind is None: return if event.inaxes is None: return if event.button != 1: return x, y = event.xdata, event.ydata
self.poly.xy[self._ind] = x, y if self._ind == 0: self.poly.xy[-1] = x, y elif self._ind == len(self.poly.xy) - 1: self.poly.xy[0] = x, y self.line.set_data(zip(*self.poly.xy))
self.canvas.restore_region(self.background) self.ax.draw_artist(self.poly) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox)
if __name__ == '__main__': import matplotlib.pyplot as plt from matplotlib.patches import Polygon
theta = np.arange(0, 2*np.pi, 0.1) r = 1.5
xs = r * np.cos(theta) ys = r * np.sin(theta)
poly = Polygon(np.column_stack([xs, ys]), animated=True)
fig, ax = plt.subplots() ax.add_patch(poly) p = PolygonInteractor(ax, poly)
ax.set_title('Click and drag a point to move it') ax.set_xlim((-2, 2)) ax.set_ylim((-2, 2)) plt.show()
|