@@ -599,31 +599,6 @@ def labels(self, inline, inline_spacing):
599599 paths [:] = additions
600600
601601
602- def _find_closest_point_on_leg (p1 , p2 , p0 ):
603- """Find the closest point to p0 on line segment connecting p1 and p2."""
604-
605- # handle degenerate case
606- if np .all (p2 == p1 ):
607- d = np .sum ((p0 - p1 )** 2 )
608- return d , p1
609-
610- d21 = p2 - p1
611- d01 = p0 - p1
612-
613- # project on to line segment to find closest point
614- proj = np .dot (d01 , d21 ) / np .dot (d21 , d21 )
615- if proj < 0 :
616- proj = 0
617- if proj > 1 :
618- proj = 1
619- pc = p1 + proj * d21
620-
621- # find squared distance
622- d = np .sum ((pc - p0 )** 2 )
623-
624- return d , pc
625-
626-
627602def _is_closed_polygon (X ):
628603 """
629604 Return whether first and last object in a sequence are the same. These are
@@ -633,39 +608,40 @@ def _is_closed_polygon(X):
633608 return np .allclose (X [0 ], X [- 1 ], rtol = 1e-10 , atol = 1e-13 )
634609
635610
636- def _find_closest_point_on_path (lc , point ):
611+ def _find_closest_point_on_path (xys , p ):
637612 """
638613 Parameters
639614 ----------
640- lc : coordinates of vertices
641- point : coordinates of test point
615+ xys : (N, 2) array-like
616+ Coordinates of vertices.
617+ p : (float, float)
618+ Coordinates of point.
619+
620+ Returns
621+ -------
622+ d2min : float
623+ Minimum square distance of *p* to *xys*.
624+ proj : (float, float)
625+ Projection of *p* onto *xys*.
626+ imin : (int, int)
627+ Consecutive indices of vertices of segment in *xys* where *proj* is.
628+ Segments are considered as including their end-points; i.e if the
629+ closest point on the path is a node in *xys* with index *i*, this
630+ returns ``(i-1, i)``. For the special case where *xys* is a single
631+ point, this returns ``(0, 0)``.
642632 """
643-
644- # find index of closest vertex for this segment
645- ds = np .sum ((lc - point [None , :])** 2 , 1 )
646- imin = np .argmin (ds )
647-
648- dmin = np .inf
649- xcmin = None
650- legmin = (None , None )
651-
652- closed = _is_closed_polygon (lc )
653-
654- # build list of legs before and after this vertex
655- legs = []
656- if imin > 0 or closed :
657- legs .append (((imin - 1 ) % len (lc ), imin ))
658- if imin < len (lc ) - 1 or closed :
659- legs .append ((imin , (imin + 1 ) % len (lc )))
660-
661- for leg in legs :
662- d , xc = _find_closest_point_on_leg (lc [leg [0 ]], lc [leg [1 ]], point )
663- if d < dmin :
664- dmin = d
665- xcmin = xc
666- legmin = leg
667-
668- return (dmin , xcmin , legmin )
633+ if len (xys ) == 1 :
634+ return (((p - xys [0 ]) ** 2 ).sum (), xys [0 ], (0 , 0 ))
635+ dxys = xys [1 :] - xys [:- 1 ] # Individual segment vectors.
636+ norms = (dxys ** 2 ).sum (axis = 1 )
637+ norms [norms == 0 ] = 1 # For zero-length segment, replace 0/0 by 0/1.
638+ rel_projs = np .clip ( # Project onto each segment in relative 0-1 coords.
639+ ((p - xys [:- 1 ]) * dxys ).sum (axis = 1 ) / norms ,
640+ 0 , 1 )[:, None ]
641+ projs = xys [:- 1 ] + rel_projs * dxys # Projs. onto each segment, in (x, y).
642+ d2s = ((projs - p ) ** 2 ).sum (axis = 1 ) # Squared distances.
643+ imin = np .argmin (d2s )
644+ return (d2s [imin ], projs [imin ], (imin , imin + 1 ))
669645
670646
671647docstring .interpd .update (contour_set_attributes = r"""
@@ -1341,8 +1317,8 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
13411317 ``(x, y)``.
13421318 xmin, ymin : float
13431319 The point in the contour plot that is closest to ``(x, y)``.
1344- d : float
1345- The distance from ``(xmin, ymin)`` to ``(x, y)``.
1320+ d2 : float
1321+ The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
13461322 """
13471323
13481324 # This function uses a method that is probably quite
@@ -1356,7 +1332,7 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
13561332 if indices is None :
13571333 indices = list (range (len (self .levels )))
13581334
1359- dmin = np .inf
1335+ d2min = np .inf
13601336 conmin = None
13611337 segmin = None
13621338 xmin = None
@@ -1375,16 +1351,16 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
13751351 if pixel :
13761352 lc = trans .transform (lc )
13771353
1378- d , xc , leg = _find_closest_point_on_path (lc , point )
1379- if d < dmin :
1380- dmin = d
1354+ d2 , xc , leg = _find_closest_point_on_path (lc , point )
1355+ if d2 < d2min :
1356+ d2min = d2
13811357 conmin = icon
13821358 segmin = segNum
13831359 imin = leg [1 ]
13841360 xmin = xc [0 ]
13851361 ymin = xc [1 ]
13861362
1387- return (conmin , segmin , imin , xmin , ymin , dmin )
1363+ return (conmin , segmin , imin , xmin , ymin , d2min )
13881364
13891365
13901366@docstring .dedent_interpd
0 commit comments