@@ -599,31 +599,6 @@ def labels(self, inline, inline_spacing):
599
599
paths [:] = additions
600
600
601
601
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
-
627
602
def _is_closed_polygon (X ):
628
603
"""
629
604
Return whether first and last object in a sequence are the same. These are
@@ -633,39 +608,40 @@ def _is_closed_polygon(X):
633
608
return np .allclose (X [0 ], X [- 1 ], rtol = 1e-10 , atol = 1e-13 )
634
609
635
610
636
- def _find_closest_point_on_path (lc , point ):
611
+ def _find_closest_point_on_path (xys , p ):
637
612
"""
638
613
Parameters
639
614
----------
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)``.
642
632
"""
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 ))
669
645
670
646
671
647
docstring .interpd .update (contour_set_attributes = r"""
@@ -1341,8 +1317,8 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
1341
1317
``(x, y)``.
1342
1318
xmin, ymin : float
1343
1319
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)``.
1346
1322
"""
1347
1323
1348
1324
# This function uses a method that is probably quite
@@ -1356,7 +1332,7 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
1356
1332
if indices is None :
1357
1333
indices = list (range (len (self .levels )))
1358
1334
1359
- dmin = np .inf
1335
+ d2min = np .inf
1360
1336
conmin = None
1361
1337
segmin = None
1362
1338
xmin = None
@@ -1375,16 +1351,16 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
1375
1351
if pixel :
1376
1352
lc = trans .transform (lc )
1377
1353
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
1381
1357
conmin = icon
1382
1358
segmin = segNum
1383
1359
imin = leg [1 ]
1384
1360
xmin = xc [0 ]
1385
1361
ymin = xc [1 ]
1386
1362
1387
- return (conmin , segmin , imin , xmin , ymin , dmin )
1363
+ return (conmin , segmin , imin , xmin , ymin , d2min )
1388
1364
1389
1365
1390
1366
@docstring .dedent_interpd
0 commit comments