@@ -572,6 +572,18 @@ def add_label_near(self, x, y, inline=True, inline_spacing=5,
572
572
conmin , segmin , imin , xmin , ymin = self .find_nearest_contour (
573
573
x , y , self .labelIndiceList )[:5 ]
574
574
575
+ # The calc_label_rot_and_inline routine requires that (xmin,ymin)
576
+ # be a vertex in the path. So, if it isn't, add a vertex here
577
+ paths = self .collections [conmin ].get_paths ()
578
+ lc = paths [segmin ].vertices
579
+ if transform :
580
+ xcmin = transform .inverted ().transform ([xmin ,ymin ])
581
+ else :
582
+ xcmin = np .array ([xmin ,ymin ])
583
+ if not np .allclose (xcmin , lc [imin ]):
584
+ lc = np .r_ [lc [:imin ], np .array (xcmin )[None ,:], lc [imin :]]
585
+ paths [segmin ] = mpath .Path (lc )
586
+
575
587
# Get index of nearest level in subset of levels used for labeling
576
588
lmin = self .labelIndiceList .index (conmin )
577
589
@@ -673,6 +685,59 @@ def labels(self, inline, inline_spacing):
673
685
del paths [:]
674
686
paths .extend (additions )
675
687
688
+ def _find_closest_point_on_leg (p1 , p2 , p0 ):
689
+ '''find closest point to p0 on line segment connecting p1 and p2'''
690
+
691
+ # handle degenerate case
692
+ if np .all (p2 == p1 ):
693
+ d = np .sum ((p0 - p1 )** 2 )
694
+ return d , p1
695
+
696
+ d21 = p2 - p1
697
+ d01 = p0 - p1
698
+
699
+ # project on to line segment to find closest point
700
+ proj = np .dot (d01 , d21 ) / np .dot (d21 , d21 )
701
+ if proj < 0 : proj = 0
702
+ if proj > 1 : proj = 1
703
+ pc = p1 + proj * d21
704
+
705
+ # find squared distance
706
+ d = np .sum ((pc - p0 )** 2 )
707
+
708
+ return d , pc
709
+
710
+ def _find_closest_point_on_path (lc , point ):
711
+ '''
712
+ lc: coordinates of vertices
713
+ point: coordinates of test point
714
+ '''
715
+
716
+ # find index of closest vertex for this segment
717
+ ds = np .sum ((lc - point [None ,:])** 2 , 1 )
718
+ imin = np .argmin (ds )
719
+
720
+ dmin = 1e10
721
+ xcmin = None
722
+ legmin = (None ,None )
723
+
724
+ closed = mlab .is_closed_polygon (lc )
725
+
726
+ # build list of legs before and after this vertex
727
+ legs = []
728
+ if imin > 0 or closed :
729
+ legs .append (((imin - 1 )% len (lc ),imin ))
730
+ if imin < len (lc ) - 1 or closed :
731
+ legs .append ((imin ,(imin + 1 )% len (lc )))
732
+
733
+ for leg in legs :
734
+ d , xc = _find_closest_point_on_leg (lc [leg [0 ]], lc [leg [1 ]], point )
735
+ if d < dmin :
736
+ dmin = d
737
+ xcmin = xc
738
+ legmin = leg
739
+
740
+ return (dmin , xcmin , legmin )
676
741
677
742
class ContourSet (cm .ScalarMappable , ContourLabeler ):
678
743
"""
@@ -1262,26 +1327,27 @@ def find_nearest_contour(self, x, y, indices=None, pixel=True):
1262
1327
xmin = None
1263
1328
ymin = None
1264
1329
1330
+ point = np .array ([x ,y ])
1331
+
1265
1332
for icon in indices :
1266
1333
con = self .collections [icon ]
1267
1334
trans = con .get_transform ()
1268
1335
paths = con .get_paths ()
1336
+
1269
1337
for segNum , linepath in enumerate (paths ):
1270
1338
lc = linepath .vertices
1271
-
1272
1339
# transfer all data points to screen coordinates if desired
1273
1340
if pixel :
1274
1341
lc = trans .transform (lc )
1275
1342
1276
- ds = (lc [:, 0 ] - x ) ** 2 + (lc [:, 1 ] - y ) ** 2
1277
- d = min (ds )
1343
+ d , xc , leg = _find_closest_point_on_path (lc , point )
1278
1344
if d < dmin :
1279
1345
dmin = d
1280
1346
conmin = icon
1281
1347
segmin = segNum
1282
- imin = mpl . mlab . find ( ds == d )[ 0 ]
1283
- xmin = lc [ imin , 0 ]
1284
- ymin = lc [ imin , 1 ]
1348
+ imin = leg [ 1 ]
1349
+ xmin = xc [ 0 ]
1350
+ ymin = xc [ 1 ]
1285
1351
1286
1352
return (conmin , segmin , imin , xmin , ymin , dmin )
1287
1353
0 commit comments