Skip to content

Commit 59c7303

Browse files
committed
added path editor
svn path=/trunk/matplotlib/; revision=5311
1 parent 430c133 commit 59c7303

File tree

2 files changed

+149
-4
lines changed

2 files changed

+149
-4
lines changed

examples/api/path_patch_demo.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
Path = mpath.Path
77

8+
fig = plt.figure()
9+
ax = fig.add_subplot(111)
10+
811
pathdata = [
912
(Path.MOVETO, (0, 0)),
1013
(Path.CURVE4, (-1, 0)),
@@ -19,13 +22,10 @@
1922

2023
codes, verts = zip(*pathdata)
2124
path = mpath.Path(verts, codes)
22-
2325
patch = mpatches.PathPatch(path, facecolor='green', edgecolor='yellow', alpha=0.5)
24-
25-
fig = plt.figure()
26-
ax = fig.add_subplot(111)
2726
ax.add_patch(patch)
2827

28+
2929
ax.set_xlim(-5,5)
3030
ax.set_ylim(-5,5)
3131

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import numpy as np
2+
import matplotlib.path as mpath
3+
import matplotlib.patches as mpatches
4+
import matplotlib.pyplot as plt
5+
import matplotlib.mlab as mlab
6+
7+
Path = mpath.Path
8+
9+
fig = plt.figure()
10+
ax = fig.add_subplot(111)
11+
12+
pathdata = [
13+
(Path.MOVETO, (0, 0)),
14+
(Path.CURVE4, (-1, 0.1)),
15+
(Path.CURVE4, (-1, 0.9)),
16+
(Path.CURVE4, (0, 1)),
17+
(Path.LINETO, (2, 1)),
18+
(Path.CURVE4, (3, 0.9)),
19+
(Path.CURVE4, (3, 0.1)),
20+
(Path.CURVE4, (2, 0)),
21+
(Path.CLOSEPOLY, (0, 0)),
22+
]
23+
24+
codes, verts = zip(*pathdata)
25+
path = mpath.Path(verts, codes)
26+
patch = mpatches.PathPatch(path, facecolor='green', edgecolor='yellow', alpha=0.5)
27+
ax.add_patch(patch)
28+
29+
30+
class PathInteractor:
31+
"""
32+
An path editor.
33+
34+
Key-bindings
35+
36+
't' toggle vertex markers on and off. When vertex markers are on,
37+
you can move them, delete them
38+
39+
40+
"""
41+
42+
showverts = True
43+
epsilon = 5 # max pixel distance to count as a vertex hit
44+
45+
def __init__(self, pathpatch):
46+
47+
self.ax = pathpatch.axes
48+
canvas = self.ax.figure.canvas
49+
self.pathpatch = pathpatch
50+
self.pathpatch.set_animated(True)
51+
52+
x, y = zip(*self.pathpatch.get_path().vertices)
53+
54+
self.line, = ax.plot(x,y,marker='o', markerfacecolor='r', animated=True)
55+
56+
self._ind = None # the active vert
57+
58+
canvas.mpl_connect('draw_event', self.draw_callback)
59+
canvas.mpl_connect('button_press_event', self.button_press_callback)
60+
canvas.mpl_connect('key_press_event', self.key_press_callback)
61+
canvas.mpl_connect('button_release_event', self.button_release_callback)
62+
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
63+
self.canvas = canvas
64+
65+
66+
def draw_callback(self, event):
67+
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
68+
self.ax.draw_artist(self.pathpatch)
69+
self.ax.draw_artist(self.line)
70+
self.canvas.blit(self.ax.bbox)
71+
72+
def pathpatch_changed(self, pathpatch):
73+
'this method is called whenever the pathpatchgon object is called'
74+
# only copy the artist props to the line (except visibility)
75+
vis = self.line.get_visible()
76+
Artist.update_from(self.line, pathpatch)
77+
self.line.set_visible(vis) # don't use the pathpatch visibility state
78+
79+
80+
def get_ind_under_point(self, event):
81+
'get the index of the vertex under point if within epsilon tolerance'
82+
83+
# display coords
84+
xy = np.asarray(self.pathpatch.get_path().vertices)
85+
xyt = self.pathpatch.get_transform().transform(xy)
86+
xt, yt = xyt[:, 0], xyt[:, 1]
87+
d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
88+
ind = d.argmin()
89+
90+
if d[ind]>=self.epsilon:
91+
ind = None
92+
93+
return ind
94+
95+
def button_press_callback(self, event):
96+
'whenever a mouse button is pressed'
97+
if not self.showverts: return
98+
if event.inaxes==None: return
99+
if event.button != 1: return
100+
self._ind = self.get_ind_under_point(event)
101+
102+
def button_release_callback(self, event):
103+
'whenever a mouse button is released'
104+
if not self.showverts: return
105+
if event.button != 1: return
106+
self._ind = None
107+
108+
def key_press_callback(self, event):
109+
'whenever a key is pressed'
110+
if not event.inaxes: return
111+
if event.key=='t':
112+
self.showverts = not self.showverts
113+
self.line.set_visible(self.showverts)
114+
if not self.showverts: self._ind = None
115+
116+
self.canvas.draw()
117+
118+
def motion_notify_callback(self, event):
119+
'on mouse movement'
120+
if not self.showverts: return
121+
if self._ind is None: return
122+
if event.inaxes is None: return
123+
if event.button != 1: return
124+
x,y = event.xdata, event.ydata
125+
126+
# todo: expose me
127+
vertices = self.pathpatch.get_path().vertices
128+
129+
vertices[self._ind] = x,y
130+
self.line.set_data(zip(*vertices))
131+
132+
self.canvas.restore_region(self.background)
133+
self.ax.draw_artist(self.pathpatch)
134+
self.ax.draw_artist(self.line)
135+
self.canvas.blit(self.ax.bbox)
136+
137+
138+
interactor = PathInteractor(patch)
139+
ax.set_title('drag vertices to update path')
140+
ax.set_xlim(-5,5)
141+
ax.set_ylim(-5,5)
142+
143+
plt.show()
144+
145+

0 commit comments

Comments
 (0)