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
| import numpy as np import matplotlib.path as mpath import matplotlib.patches as mpatches import matplotlib.pyplot as plt
Path = mpath.Path
fig, ax = plt.subplots()
pathdata = [ (Path.MOVETO, (1.58, -2.57)), (Path.CURVE4, (0.35, -1.1)), (Path.CURVE4, (-1.75, 2.0)), (Path.CURVE4, (0.375, 2.0)), (Path.LINETO, (0.85, 1.15)), (Path.CURVE4, (2.2, 3.2)), (Path.CURVE4, (3, 0.05)), (Path.CURVE4, (2.0, -0.5)), (Path.CLOSEPOLY, (1.58, -2.57)), ]
codes, verts = zip(*pathdata) path = mpath.Path(verts, codes) patch = mpatches.PathPatch(path, facecolor='green', edgecolor='yellow', alpha=0.5) ax.add_patch(patch)
class PathInteractor(object): """ An path editor.
Key-bindings
't' toggle vertex markers on and off. When vertex markers are on, you can move them, delete them
"""
showverts = True epsilon = 5
def __init__(self, pathpatch):
self.ax = pathpatch.axes canvas = self.ax.figure.canvas self.pathpatch = pathpatch self.pathpatch.set_animated(True)
x, y = zip(*self.pathpatch.get_path().vertices)
self.line, = ax.plot(x, y, marker='o', markerfacecolor='r', animated=True)
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.pathpatch) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox)
def pathpatch_changed(self, pathpatch): 'this method is called whenever the pathpatchgon object is called' vis = self.line.get_visible() plt.Artist.update_from(self.line, pathpatch) 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.pathpatch.get_path().vertices) xyt = self.pathpatch.get_transform().transform(xy) xt, yt = xyt[:, 0], xyt[:, 1] d = np.sqrt((xt - event.x)**2 + (yt - event.y)**2) ind = d.argmin()
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
self.canvas.draw()
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
vertices = self.pathpatch.get_path().vertices
vertices[self._ind] = x, y self.line.set_data(zip(*vertices))
self.canvas.restore_region(self.background) self.ax.draw_artist(self.pathpatch) self.ax.draw_artist(self.line) self.canvas.blit(self.ax.bbox)
interactor = PathInteractor(patch) ax.set_title('drag vertices to update path') ax.set_xlim(-3, 4) ax.set_ylim(-3, 4)
plt.show()
|