22Classes to support contour plotting and labelling for the Axes class.
33"""
44
5+ from contextlib import ExitStack
56import functools
67import math
78from numbers import Integral
@@ -937,12 +938,12 @@ def __init__(self, ax, *args,
937938 ", " .join (map (repr , kwargs ))
938939 )
939940
940- allsegs = _api . deprecated ( "3.8" , pending = True )( property (lambda self : [
941+ allsegs = property (lambda self : [
941942 [subp .vertices for subp in p ._iter_connected_components ()]
942- for p in self .get_paths ()]))
943- allkinds = _api . deprecated ( "3.8" , pending = True )( property (lambda self : [
943+ for p in self .get_paths ()])
944+ allkinds = property (lambda self : [
944945 [subp .codes for subp in p ._iter_connected_components ()]
945- for p in self .get_paths ()]))
946+ for p in self .get_paths ()])
946947 tcolors = _api .deprecated ("3.8" )(property (lambda self : [
947948 (tuple (rgba ),) for rgba in self .to_rgba (self .cvalues , self .alpha )]))
948949 tlinewidths = _api .deprecated ("3.8" )(property (lambda self : [
@@ -1396,7 +1397,6 @@ def _find_nearest_contour(self, xy, indices=None):
13961397
13971398 return idx_level_min , idx_vtx_min , proj_min
13981399
1399- @_api .deprecated ("3.8" )
14001400 def find_nearest_contour (self , x , y , indices = None , pixel = True ):
14011401 """
14021402 Find the point in the contour plot that is closest to ``(x, y)``.
@@ -1417,64 +1417,39 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
14171417
14181418 Returns
14191419 -------
1420- contour : `.Collection`
1421- The contour that is closest to ``(x, y)``.
1422- segment : int
1423- The index of the `.Path` in *contour* that is closest to
1424- ``(x, y)``.
1420+ path : int
1421+ The index of the path that is closest to ``(x, y)``. Each path corresponds
1422+ to one contour level.
1423+ subpath : int
1424+ The index within that closest path of the subpath that is closest to
1425+ ``(x, y)``. Each subpath corresponds to one unbroken contour line.
14251426 index : int
1426- The index of the path segment in *segment* that is closest to
1427+ The index of the vertices within that subpath that are closest to
14271428 ``(x, y)``.
14281429 xmin, ymin : float
14291430 The point in the contour plot that is closest to ``(x, y)``.
14301431 d2 : float
14311432 The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
14321433 """
1434+ segment = index = d2 = None
14331435
1434- # This function uses a method that is probably quite
1435- # inefficient based on converting each contour segment to
1436- # pixel coordinates and then comparing the given point to
1437- # those coordinates for each contour. This will probably be
1438- # quite slow for complex contours, but for normal use it works
1439- # sufficiently well that the time is not noticeable.
1440- # Nonetheless, improvements could probably be made.
1436+ with ExitStack () as stack :
1437+ if not pixel :
1438+ # _find_nearest_contour works in pixel space. We want axes space, so
1439+ # effectively disable the transformation here by setting to identity.
1440+ stack .enter_context (self ._cm_set (
1441+ transform = mtransforms .IdentityTransform ()))
14411442
1442- if self .filled :
1443- raise ValueError ("Method does not support filled contours." )
1443+ i_level , i_vtx , (xmin , ymin ) = self ._find_nearest_contour ((x , y ), indices )
14441444
1445- if indices is None :
1446- indices = range (len (self .collections ))
1445+ if i_level is not None :
1446+ cc_cumlens = np .cumsum (
1447+ [* map (len , self ._paths [i_level ]._iter_connected_components ())])
1448+ segment = cc_cumlens .searchsorted (i_vtx , "right" )
1449+ index = i_vtx if segment == 0 else i_vtx - cc_cumlens [segment - 1 ]
1450+ d2 = (xmin - x )** 2 + (ymin - y )** 2
14471451
1448- d2min = np .inf
1449- conmin = None
1450- segmin = None
1451- imin = None
1452- xmin = None
1453- ymin = None
1454-
1455- point = np .array ([x , y ])
1456-
1457- for icon in indices :
1458- con = self .collections [icon ]
1459- trans = con .get_transform ()
1460- paths = con .get_paths ()
1461-
1462- for segNum , linepath in enumerate (paths ):
1463- lc = linepath .vertices
1464- # transfer all data points to screen coordinates if desired
1465- if pixel :
1466- lc = trans .transform (lc )
1467-
1468- d2 , xc , leg = _find_closest_point_on_path (lc , point )
1469- if d2 < d2min :
1470- d2min = d2
1471- conmin = icon
1472- segmin = segNum
1473- imin = leg [1 ]
1474- xmin = xc [0 ]
1475- ymin = xc [1 ]
1476-
1477- return (conmin , segmin , imin , xmin , ymin , d2min )
1452+ return (i_level , segment , index , xmin , ymin , d2 )
14781453
14791454 def draw (self , renderer ):
14801455 paths = self ._paths
0 commit comments