@@ -598,31 +598,6 @@ def labels(self, inline, inline_spacing):
598
598
paths [:] = additions
599
599
600
600
601
- def _find_closest_point_on_leg (p1 , p2 , p0 ):
602
- """Find the closest point to p0 on line segment connecting p1 and p2."""
603
-
604
- # handle degenerate case
605
- if np .all (p2 == p1 ):
606
- d = np .sum ((p0 - p1 )** 2 )
607
- return d , p1
608
-
609
- d21 = p2 - p1
610
- d01 = p0 - p1
611
-
612
- # project on to line segment to find closest point
613
- proj = np .dot (d01 , d21 ) / np .dot (d21 , d21 )
614
- if proj < 0 :
615
- proj = 0
616
- if proj > 1 :
617
- proj = 1
618
- pc = p1 + proj * d21
619
-
620
- # find squared distance
621
- d = np .sum ((pc - p0 )** 2 )
622
-
623
- return d , pc
624
-
625
-
626
601
def _is_closed_polygon (X ):
627
602
"""
628
603
Return whether first and last object in a sequence are the same. These are
@@ -632,39 +607,36 @@ def _is_closed_polygon(X):
632
607
return np .all (X [0 ] == X [- 1 ])
633
608
634
609
635
- def _find_closest_point_on_path (lc , point ):
610
+ def _find_closest_point_on_path (xys , p ):
636
611
"""
637
612
Parameters
638
613
----------
639
- lc : coordinates of vertices
640
- point : coordinates of test point
614
+ xys : (N, 2) array-like
615
+ Coordinates of vertices.
616
+ p : (float, float)
617
+ Coordinates of point.
618
+
619
+ Returns
620
+ -------
621
+ d2min : float
622
+ Minimum square distance of *p* to *xys*.
623
+ proj : (float, float)
624
+ Projection of *p* onto *xys*.
625
+ imin : (int, int)
626
+ Consecutive indices of vertices of segment in *xys* where *proj* is.
641
627
"""
642
-
643
- # find index of closest vertex for this segment
644
- ds = np .sum ((lc - point [None , :])** 2 , 1 )
645
- imin = np .argmin (ds )
646
-
647
- dmin = np .inf
648
- xcmin = None
649
- legmin = (None , None )
650
-
651
- closed = _is_closed_polygon (lc )
652
-
653
- # build list of legs before and after this vertex
654
- legs = []
655
- if imin > 0 or closed :
656
- legs .append (((imin - 1 ) % len (lc ), imin ))
657
- if imin < len (lc ) - 1 or closed :
658
- legs .append ((imin , (imin + 1 ) % len (lc )))
659
-
660
- for leg in legs :
661
- d , xc = _find_closest_point_on_leg (lc [leg [0 ]], lc [leg [1 ]], point )
662
- if d < dmin :
663
- dmin = d
664
- xcmin = xc
665
- legmin = leg
666
-
667
- return (dmin , xcmin , legmin )
628
+ if len (xys ) == 1 :
629
+ return (((p - xys [0 ]) ** 2 ).sum (), xys [0 ], (0 , 0 ))
630
+ dxys = xys [1 :] - xys [:- 1 ] # Individual segment vectors.
631
+ with np .errstate (invalid = "ignore" ): # 0/0 from zero-length segments.
632
+ rel_projs = np .clip ( # Proj. onto each segment in relative 0-1 coords.
633
+ ((p - xys [:- 1 ]) * dxys ).sum (axis = 1 )
634
+ / np .maximum ((dxys ** 2 ).sum (axis = 1 ), 1 ),
635
+ 0 , 1 )[:, None ]
636
+ projs = xys [:- 1 ] + rel_projs * dxys # Projs. onto each segment, in (x, y).
637
+ d2s = ((projs - p ) ** 2 ).sum (axis = 1 ) # Distances.
638
+ imin = np .argmin (d2s )
639
+ return (d2s [imin ], projs [imin ], (imin , imin + 1 ))
668
640
669
641
670
642
class ContourSet (cm .ScalarMappable , ContourLabeler ):
@@ -1333,7 +1305,7 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
1333
1305
xmin, ymin : float
1334
1306
The point in the contour plot that is closest to ``(x, y)``.
1335
1307
d : float
1336
- The distance from ``(xmin, ymin)`` to ``(x, y)``.
1308
+ The squared distance from ``(xmin, ymin)`` to ``(x, y)``.
1337
1309
"""
1338
1310
1339
1311
# This function uses a method that is probably quite
0 commit comments