8000 Jointly track x and y in PolygonSelector. by anntzer · Pull Request #21894 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Jointly track x and y in PolygonSelector. #21894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 12, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 38 additions & 56 deletions lib/matplotlib/widgets.py
46D8
Original file line number Diff line number Diff line change
Expand Up @@ -3643,21 +3643,20 @@ def __init__(self, ax, onselect, useblit=False,
super().__init__(ax, onselect, useblit=useblit,
state_modifier_keys=state_modifier_keys)

self._xs, self._ys = [0], [0]
self._xys = [(0, 0)]

if props is None:
props = dict(color='k', linestyle='-', linewidth=2, alpha=0.5)
props['animated'] = self.useblit
self._props = props
line = Line2D(self._xs, self._ys, **self._props)
self._selection_artist = line = Line2D([], [], **self._props)
self.ax.add_line(line)
self._selection_artist = line

if handle_props is None:
handle_props = dict(markeredgecolor='k',
markerfacecolor=self._props.get('color', 'k'))
self._handle_props = handle_props
self._polygon_handles = ToolHandles(self.ax, self._xs, self._ys,
self._polygon_handles = ToolHandles(self.ax, [], [],
useblit=self.useblit,
marker_props=self._handle_props)

Expand Down Expand Up @@ -3728,10 +3727,9 @@ def _scale_polygon(self, event):
.scale(w1, h1)
.translate(x1, y1))

# Update polygon verts
new_verts = t.transform(np.array(self.verts))
self._xs = list(np.append(new_verts[:, 0], new_verts[0, 0]))
self._ys = list(np.append(new_verts[:, 1], new_verts[0, 1]))
# Update polygon verts. Must be a list of tuples for consistency.
new_verts = [(x, y) for x, y in t.transform(np.array(self.verts))]
self._xys = [*new_verts, new_verts[0]]
self._draw_polygon()
self._old_box_extents = self._box.extents

Expand All @@ -3745,33 +3743,25 @@ def _scale_polygon(self, event):
lambda self, value: setattr(self, "grab_range", value))
)

@property
def _nverts(self):
return len(self._xs)

@property
def _handles_artists(self):
return self._polygon_handles.artists

def _remove_vertex(self, i):
"""Remove vertex with index i."""
if (self._nverts > 2 and
if (len(self._xys) > 2 and
self._selection_completed and
i in (0, self._nverts - 1)):
i in (0, len(self._xys) - 1)):
# If selecting the first or final vertex, remove both first and
# last vertex as they are the same for a closed polygon
self._xs.pop(0)
self._ys.pop(0)
self._xs.pop(-1)
self._ys.pop(-1)
self._xys.pop(0)
self._xys.pop(-1)
# Close the polygon again by appending the new first vertex to the
# end
self._xs.append(self._xs[0])
self._ys.append(self._ys[0])
self._xys.append(self._xys[0])
else:
self._xs.pop(i)
self._ys.pop(i)
if self._nverts <= 2:
self._xys.pop(i)
if len(self._xys) <= 2:
# If only one point left, return to incomplete state to let user
# start drawing again
self._selection_completed = False
Expand All @@ -3781,13 +3771,13 @@ def _press(self, event):
"""Button press event handler."""
# Check for selection of a tool handle.
if ((self._selection_completed or 'move_vertex' in self._state)
and len(self._xs) > 0):
and len(self._xys) > 0):
h_idx, h_dist = self._polygon_handles.closest(event.x, event.y)
if h_dist < self.grab_range:
self._active_handle_idx = h_idx
# Save the vertex positions at the time of the press event (needed to
# support the 'move_all' state modifier).
self._xs_at_press, self._ys_at_press = self._xs.copy(), self._ys.copy()
self._xys_at_press = self._xys.copy()

def _release(self, event):
"""Button release event handler."""
Expand All @@ -3799,9 +3789,7 @@ def _release(self, event):
self._active_handle_idx = -1

# Complete the polygon.
elif (len(self._xs) > 3
and self._xs[-1] == self._xs[0]
and self._ys[-1] == self._ys[0]):
elif len(self._xys) > 3 and self._xys[-1] == self._xys[0]:
self._selection_completed = True
if self._draw_box and self._box is None:
self._add_box()
Expand All @@ -3810,8 +3798,7 @@ def _release(self, event):
elif (not self._selection_completed
and 'move_all' not in self._state
and 'move_vertex' not in self._state):
self._xs.insert(-1, event.xdata)
self._ys.insert(-1, event.ydata)
self._xys.insert(-1, (event.xdata, event.ydata))

if self._selection_completed:
self.onselect(self.verts)
Expand All @@ -3833,19 +3820,19 @@ def _onmove(self, event):
# Move the active vertex (ToolHandle).
if self._active_handle_idx >= 0:
idx = self._active_handle_idx
self._xs[idx], self._ys[idx] = event.xdata, event.ydata
self._xys[idx] = event.xdata, event.ydata
# Also update the end of the polygon line if the first vertex is
# the active handle and the polygon is completed.
if idx == 0 and self._selection_completed:
self._xs[-1], self._ys[-1] = event.xdata, event.ydata
self._xys[-1] = event.xdata, event.ydata

# Move all vertices.
elif 'move_all' in self._state and self._eventpress:
dx = event.xdata - self._eventpress.xdata
dy = event.ydata - self._eventpress.ydata
for k in range(len(self._xs)):
self._xs[k] = self._xs_at_press[k] + dx
self._ys[k] = self._ys_at_press[k] + dy
for k in range(len(self._xys)):
x_at_press, y_at_press = self._xys_at_press[k]
self._xys[k] = x_at_press + dx, y_at_press + dy

# Do nothing if completed or waiting for a move.
elif (self._selection_completed
Expand All @@ -3855,15 +3842,14 @@ def _onmove(self, event):
# Position pending vertex.
else:
# Calculate distance to the start vertex.
x0, y0 = self._selection_artist.get_transform().transform(
(self._xs[0], self._ys[0])
)
x0, y0 = \
self._selection_artist.get_transform().transform(self._xys[0])
v0_dist = np.hypot(x0 - event.x, y0 - event.y)
# Lock on to the start vertex if near it and ready to complete.
if len(self._xs) > 3 and v0_dist < self.grab_range:
self._xs[-1], self._ys[-1] = self._xs[0], self._ys[0]
if len(self._xys) > 3 and v0_dist < self.grab_range:
self._xys[-1] = self._xys[0]
else:
self._xs[-1], self._ys[-1] = event.xdata, event.ydata
self._xys[-1] = event.xdata, event.ydata

self._draw_polygon()

Expand All @@ -3874,7 +3860,7 @@ def _on_key_press(self, event):
if (not self._selection_completed
and ('move_vertex' in self._state or
'move_all' in self._state)):
self._xs, self._ys = self._xs[:-1], self._ys[:-1]
self._xys.pop()
self._draw_polygon()

def _on_key_release(self, event):
Expand All @@ -3885,37 +3871,36 @@ def _on_key_release(self, event):
and
(event.key == self._state_modifier_keys.get('move_vertex')
or event.key == self._state_modifier_keys.get('move_all'))):
self._xs.append(event.xdata)
self._ys.append(event.ydata)
self._xys.append((event.xdata, event.ydata))
self._draw_polygon()
# Reset the polygon if the released key is the 'clear' key.
elif event.key == self._state_modifier_keys.get('clear'):
event = self._clean_event(event)
self._xs, self._ys = [event.xdata], [event.ydata]
self._xys = [(event.xdata, event.ydata)]
self._selection_completed = False
self._remove_box()
self.set_visible(True)

def _draw_polygon(self):
"""Redraw the polygon based on the new vertex positions."""
self._selection_artist.set_data(self._xs, self._ys)
xs, ys = zip(*self._xys) if self._xys else ([], [])
self._selection_artist.set_data(xs, ys)
self._update_box()
# Only show one tool handle at the start and end vertex of the polygon
# if the polygon is completed or the user is locked on to the start
# vertex.
if (self._selection_completed
or (len(self._xs) > 3
and self._xs[-1] == self._xs[0]
and self._ys[-1] == self._ys[0])):
self._polygon_handles.set_data(self._xs[:-1], self._ys[:-1])
or (len(self._xys) > 3
and self._xys[-1] == self._xys[0])):
self._polygon_handles.set_data(xs[:-1], ys[:-1])
else:
self._polygon_handles.set_data(self._xs, self._ys)
self._polygon_handles.set_data(xs, ys)
self.update()

@property
def verts(self):
"""The polygon vertices, as a list of ``(x, y)`` pairs."""
return list(zip(self._xs[:-1], self._ys[:-1]))
return self._xys[:-1]

@verts.setter
def verts(self, xys):
Expand All @@ -3925,10 +3910,7 @@ def verts(self, xys):
This will remove any pre-existing vertices, creating a complete polygon
with the new vertices.
"""
self._xs = [xy[0] for xy in xys]
self._xs.append(self._xs[0])
self._ys = [xy[1] for xy in xys]
self._ys.append(self._ys[0])
self._xys = [*xys, xys[0]]
self._selection_completed = True
self.set_visible(True)
self._draw_polygon()
Expand Down
0