8000 BUG: Picking vertical line broken by larsoner · Pull Request #17489 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

BUG: Picking vertical line broken #17489

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 2 commits into from
May 23, 2020
Merged
Show file tree
Hide file tree
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
41 changes: 23 additions & 18 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def segment_hits(cx, cy, x, y, radius):
"""
# Process single points specially
if len(x) <= 1:
res, = np.nonzero(np.hypot(cx - x, cy - y) <= radius)
res, = np.nonzero((cx - x) ** 2 + (cy - y) ** 2 <= radius ** 2)
return res

# We need to lop the last element off a lot.
Expand All @@ -89,24 +89,24 @@ def segment_hits(cx, cy, x, y, radius):
# Only look at line segments whose nearest point to C on the line
# lies within the segment.
dx, dy = x[1:] - xr, y[1:] - yr
u = (cx - xr) * dx + (cy - yr) * dy
candidates = (0 <= u) & (u <= dx ** 2 + dy ** 2)
Lnorm_sq = dx ** 2 + dy ** 2 # Possibly want to eliminate Lnorm==0
u = ((cx - xr) * dx + (cy - yr) * dy) / Lnorm_sq
candidates = (u >= 0) & (u <= 1)

# Note that there is a little area near one side of each point
# which will be near neither segment, and another which will
# be near both, depending on the angle of the lines. The
# following radius test eliminates these ambiguities.
point_hits = np.hypot(cx - x, cy - y) <= radius
point_hits = (cx - x) ** 2 + (cy - y) ** 2 <= radius ** 2
candidates = candidates & ~(point_hits[:-1] | point_hits[1:])

# For those candidates which remain, determine how far they lie away
# from the line.
px, py = xr + u * dx, yr + u * dy
line_hits = np.hypot(cx - px, cy - py) <= radius
line_hits = (cx - px) ** 2 + (cy - py) ** 2 <= radius ** 2
line_hits = line_hits & candidates

points, = point_hits.nonzero()
lines, = line_hits.nonzero()
points, = point_hits.ravel().nonzero()
lines, = line_hits.ravel().nonzero()
return np.concatenate((points, lines))


Expand Down Expand Up @@ -454,16 +454,21 @@ def contains(self, mouseevent):
else:
pixels = self.figure.dpi / 72. * self.pickradius

# Check for collision
if self._linestyle in ['None', None]:
# If no line, return the nearby point(s)
ind, = np.nonzero(
np.hypot(xt - mouseevent.x, yt - mouseevent.y) <= pixels)
else:
# If line, return the nearby segment(s)
ind = segment_hits(mouseevent.x, mouseevent.y, xt, yt, pixels)
if self._drawstyle.startswith("steps"):
ind //= 2
# The math involved in checking for containment (here and inside of
# segment_hits) assumes that it is OK to overflow, so temporarily set
# the error flags accordingly.
with np.errstate(all='ignore'):
# Check for collision
if self._linestyle in ['None', None]:
# If no line, return the nearby point(s)
ind, = np.nonzero(
(xt - mouseevent.x) ** 2 + (yt - mouseevent.y) ** 2
<= pixels ** 2)
else:
# If line, return the nearby segment(s)
ind = segment_hits(mouseevent.x, mouseevent.y, xt, yt, pixels)
if self._drawstyle.startswith("steps"):
ind //= 2

ind += self.ind_offset

Expand Down
8 changes: 8 additions & 0 deletions lib/matplotlib/tests/test_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
from matplotlib.testing.decorators import image_comparison, check_figures_equal


def test_segment_hits():
"""Test a problematic case."""
cx, cy = 553, 902
x, y = np.array([553., 553.]), np.array([95., 947.])
radius = 6.94
assert_array_equal(mlines.segment_hits(cx, cy, x, y, radius), [0])


# Runtimes on a loaded system are inherently flaky. Not so much that a rerun
# won't help, hopefully.
@pytest.mark.flaky(reruns=3)
Expand Down
0