Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 25ec2c0

Browse files
rcomeranntzer
andcommitted
Update find_nearest_contour to not use collections attribute
Co-authored-by: Antony Lee <[email protected]>
1 parent e394940 commit 25ec2c0

File tree

3 files changed

+27
-51
lines changed

3 files changed

+27
-51
lines changed

lib/matplotlib/contour.py

Lines changed: 23 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Classes to support contour plotting and labelling for the Axes class.
33
"""
44

5+
from contextlib import ExitStack
56
import functools
67
import math
78
from numbers import Integral
@@ -1409,64 +1410,39 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
14091410
14101411
Returns
14111412
-------
1412-
contour : `.Collection`
1413-
The contour that is closest to ``(x, y)``.
1414-
segment : int
1415-
The index of the `.Path` in *contour* that is closest to
1416-
``(x, y)``.
1413+
path : int
1414+
The index of the path that is closest to ``(x, y)``. Each path corresponds
1415+
to one contour level.
1416+
subpath : int
1417+
The index within that closest path of the subpath that is closest to
1418+
``(x, y)``. Each subpath corresponds to one unbroken contour line.
14171419
index : int
1418-
The index of the path segment in *segment* that is closest to
1420+
The index of the vertices within that subpath that are closest to
14191421
``(x, y)``.
14201422
xmin, ymin : float
14211423
The point in the contour plot that is closest to ``(x, y)``.
14221424
d2 : float
14231425
The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
14241426
"""
1427+
segment = index = d2 = None
14251428

1426-
# This function uses a method that is probably quite
1427-
# inefficient based on converting each contour segment to
1428-
# pixel coordinates and then comparing the given point to
1429-
# those coordinates for each contour. This will probably be
1430-
# quite slow for complex contours, but for normal use it works
1431-
# sufficiently well that the time is not noticeable.
1432-
# Nonetheless, improvements could probably be made.
1429+
with ExitStack() as stack:
1430+
if not pixel:
1431+
# _find_nearest_contour works in pixel space. We want axes space, so
1432+
# effectively disable the transformation here by setting to identity.
1433+
stack.enter_context(self._cm_set(
1434+
transform=mtransforms.IdentityTransform()))
14331435

1434-
if self.filled:
1435-
raise ValueError("Method does not support filled contours.")
1436+
i_level, i_vtx, (xmin, ymin) = self._find_nearest_contour((x, y), indices)
14361437

1437-
if indices is None:
1438-
indices = range(len(self.collections))
1438+
if i_level is not None:
1439+
cc_cumlens = np.cumsum(
1440+
[*map(len, self._paths[i_level]._iter_connected_components())])
1441+
segment = cc_cumlens.searchsorted(i_vtx, "right")
1442+
index = i_vtx if segment == 0 else i_vtx - cc_cumlens[segment - 1]
1443+
d2 = (xmin-x)**2 + (ymin-y)**2
14391444

1440-
d2min = np.inf
1441-
conmin = None
1442-
segmin = None
1443-
imin = None
1444-
xmin = None
1445-
ymin = None
1446-
1447-
point = np.array([x, y])
1448-
1449-
for icon in indices:
1450-
con = self.collections[icon]
1451-
trans = con.get_transform()
1452-
paths = con.get_paths()
1453-
1454-
for segNum, linepath in enumerate(paths):
1455-
lc = linepath.vertices
1456-
# transfer all data points to screen coordinates if desired
1457-
if pixel:
1458-
lc = trans.transform(lc)
1459-
1460-
d2, xc, leg = _find_closest_point_on_path(lc, point)
1461-
if d2 < d2min:
1462-
d2min = d2
1463-
conmin = icon
1464-
segmin = segNum
1465-
imin = leg[1]
1466-
xmin = xc[0]
1467-
ymin = xc[1]
1468-
1469-
return (conmin, segmin, imin, xmin, ymin, d2min)
1445+
return (i_level, segment, index, xmin, ymin, d2)
14701446

14711447
def draw(self, renderer):
14721448
paths = self._paths

lib/matplotlib/contour.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,6 @@ class ContourSet(ContourLabeler, Collection):
159159
) -> tuple[list[Artist], list[str]]: ...
160160
def find_nearest_contour(
161161
self, x: float, y: float, indices: Iterable[int] | None = ..., pixel: bool = ...
162-
) -> tuple[Collection, int, int, float, float, float]: ...
162+
) -> tuple[int, int, int, float, float, float]: ...
163163

164164
class QuadContourSet(ContourSet): ...

lib/matplotlib/tests/test_contour.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,15 +557,15 @@ def test_find_nearest_contour_no_filled():
557557
cs = plt.contourf(img, 10)
558558

559559
with pytest.warns(mpl._api.MatplotlibDeprecationWarning), \
560-
pytest.raises(ValueError, match="Method does not support filled contours."):
560+
pytest.raises(ValueError, match="Method does not support filled contours"):
561561
cs.find_nearest_contour(1, 1, pixel=False)
562562

563563
with pytest.warns(mpl._api.MatplotlibDeprecationWarning), \
564-
pytest.raises(ValueError, match="Method does not support filled contours."):
564+
pytest.raises(ValueError, match="Method does not support filled contours"):
565565
cs.find_nearest_contour(1, 10, indices=(5, 7), pixel=False)
566566

567567
with pytest.warns(mpl._api.MatplotlibDeprecationWarning), \
568-
pytest.raises(ValueError, match="Method does not support filled contours."):
568+
pytest.raises(ValueError, match="Method does not support filled contours"):
569569
cs.find_nearest_contour(2, 5, indices=(2, 7), pixel=True)
570570

571571

0 commit comments

Comments
 (0)