Skip to content

Fix containment test with nonlinear transforms. #7844

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
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
16 changes: 10 additions & 6 deletions lib/matplotlib/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@

import numpy as np

from matplotlib import _path
from matplotlib.cbook import simple_linear_interpolation, maxdict
from matplotlib import rcParams
from . import _path, rcParams
from .cbook import simple_linear_interpolation, maxdict


class Path(object):
Expand Down Expand Up @@ -493,9 +492,14 @@ def contains_point(self, point, transform=None, radius=0.0):
"""
if transform is not None:
transform = transform.frozen()
result = _path.point_in_path(point[0], point[1], radius, self,
transform)
return result
# `point_in_path` does not handle nonlinear transforms, so we
# transform the path ourselves. If `transform` is affine, letting
# `point_in_path` handle the transform avoids allocating an extra
# buffer.
if transform and not transform.is_affine:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason not to just do the transformation here even if it is an affine transformation? (thus removing the if block)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you may save the creation of a large temporary (if the c++ code does the transform one point at a time), which would be relevant for very large paths.

To be honest I don't know but decided not to take any risks.

self = transform.transform_path(self)
transform = None
return _path.point_in_path(point[0], point[1], radius, self, transform)

def contains_points(self, points, transform=None, radius=0.0):
"""
Expand Down
30 changes: 21 additions & 9 deletions lib/matplotlib/tests/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ def test_contains_points_negative_radius():
assert np.all(result == expected)


def test_point_in_path_nan():
box = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
p = Path(box)
test = np.array([[np.nan, 0.5]])
contains = p.contains_points(test)
assert len(contains) == 1
assert not contains[0]


def test_nonlinear_containment():
fig, ax = plt.subplots()
ax.set(xscale="log", ylim=(0, 1))
polygon = ax.axvspan(1, 10)
assert polygon.get_path().contains_point(
ax.transData.transform_point((5, .5)), ax.transData)
assert not polygon.get_path().contains_point(
ax.transData.transform_point((.5, .5)), ax.transData)
assert not polygon.get_path().contains_point(
ax.transData.transform_point((50, .5)), ax.transData)


@image_comparison(baseline_images=['path_clipping'],
extensions=['svg'], remove_text=True)
def test_path_clipping():
Expand All @@ -66,15 +87,6 @@ def test_path_clipping():
xy, facecolor='none', edgecolor='red', closed=True))


def test_point_in_path_nan():
box = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
p = Path(box)
test = np.array([[np.nan, 0.5]])
contains = p.contains_points(test)
assert len(contains) == 1
assert not contains[0]


@image_comparison(baseline_images=['semi_log_with_zero'], extensions=['png'])
def test_log_transform_with_zero():
x = np.arange(-10, 10)
Expand Down