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
@@ -944,12 +945,12 @@ def __init__(self, ax, *args,
944945 ", " .join (map (repr , kwargs ))
945946 )
946947
947- allsegs = _api . deprecated ( "3.8" , pending = True )( property (lambda self : [
948+ allsegs = property (lambda self : [
948949 [subp .vertices for subp in p ._iter_connected_components ()]
949- for p in self .get_paths ()]))
950- allkinds = _api . deprecated ( "3.8" , pending = True )( property (lambda self : [
950+ for p in self .get_paths ()])
951+ allkinds = property (lambda self : [
951952 [subp .codes for subp in p ._iter_connected_components ()]
952- for p in self .get_paths ()]))
953+ for p in self .get_paths ()])
953954 tcolors = _api .deprecated ("3.8" )(property (lambda self : [
954955 (tuple (rgba ),) for rgba in self .to_rgba (self .cvalues , self .alpha )]))
955956 tlinewidths = _api .deprecated ("3.8" )(property (lambda self : [
@@ -1403,7 +1404,6 @@ def _find_nearest_contour(self, xy, indices=None):
14031404
14041405 return idx_level_min , idx_vtx_min , proj_min
14051406
1406- @_api .deprecated ("3.8" )
14071407 def find_nearest_contour (self , x , y , indices = None , pixel = True ):
14081408 """
14091409 Find the point in the contour plot that is closest to ``(x, y)``.
@@ -1424,64 +1424,39 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
14241424
14251425 Returns
14261426 -------
1427- contour : `.Collection`
1428- The contour that is closest to ``(x, y)``.
1429- segment : int
1430- The index of the `.Path` in *contour* that is closest to
1431- ``(x, y)``.
1427+ path : int
1428+ The index of the path that is closest to ``(x, y)``. Each path corresponds
1429+ to one contour level.
1430+ subpath : int
1431+ The index within that closest path of the subpath that is closest to
1432+ ``(x, y)``. Each subpath corresponds to one unbroken contour line.
14321433 index : int
1433- The index of the path segment in *segment* that is closest to
1434+ The index of the vertices within that subpath that are closest to
14341435 ``(x, y)``.
14351436 xmin, ymin : float
14361437 The point in the contour plot that is closest to ``(x, y)``.
14371438 d2 : float
14381439 The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
14391440 """
1441+ segment = index = d2 = None
14401442
1441- # This function uses a method that is probably quite
1442- # inefficient based on converting each contour segment to
1443- # pixel coordinates and then comparing the given point to
1444- # those coordinates for each contour. This will probably be
1445- # quite slow for complex contours, but for normal use it works
1446- # sufficiently well that the time is not noticeable.
1447- # Nonetheless, improvements could probably be made.
1443+ with ExitStack () as stack :
1444+ if not pixel :
1445+ # _find_nearest_contour works in pixel space. We want axes space, so
1446+ # effectively disable the transformation here by setting to identity.
1447+ stack .enter_context (self ._cm_set (
1448+ transform = mtransforms .IdentityTransform ()))
14481449
1449- if self .filled :
1450- raise ValueError ("Method does not support filled contours." )
1450+ i_level , i_vtx , (xmin , ymin ) = self ._find_nearest_contour ((x , y ), indices )
14511451
1452- if indices is None :
1453- indices = range (len (self .collections ))
1452+ if i_level is not None :
1453+ cc_cumlens = np .cumsum (
1454+ [* map (len , self ._paths [i_level ]._iter_connected_components ())])
1455+ segment = cc_cumlens .searchsorted (i_vtx , "right" )
1456+ index = i_vtx if segment == 0 else i_vtx - cc_cumlens [segment - 1 ]
1457+ d2 = (xmin - x )** 2 + (ymin - y )** 2
14541458
1455- d2min = np .inf
1456- conmin = None
1457- segmin = None
1458- imin = None
1459- xmin = None
1460- ymin = None
1461-
1462- point = np .array ([x , y ])
1463-
1464- for icon in indices :
1465- con = self .collections [icon ]
1466- trans = con .get_transform ()
1467- paths = con .get_paths ()
1468-
1469- for segNum , linepath in enumerate (paths ):
1470- lc = linepath .vertices
1471- # transfer all data points to screen coordinates if desired
1472- if pixel :
1473- lc = trans .transform (lc )
1474-
1475- d2 , xc , leg = _find_closest_point_on_path (lc , point )
1476- if d2 < d2min :
1477- d2min = d2
1478- conmin = icon
1479- segmin = segNum
1480- imin = leg [1 ]
1481- xmin = xc [0 ]
1482- ymin = xc [1 ]
1483-
1484- return (conmin , segmin , imin , xmin , ymin , d2min )
1459+ return (i_level , segment , index , xmin , ymin , d2 )
14851460
14861461 def draw (self , renderer ):
14871462 paths = self ._paths
0 commit comments