@@ -217,6 +217,7 @@ def too_close(self, x, y, lw):
217217 return any ((x - loc [0 ]) ** 2 + (y - loc [1 ]) ** 2 < thresh
218218 for loc in self .labelXYs )
219219
220+ @_api .deprecated ("3.4" )
220221 def get_label_coords (self , distances , XX , YY , ysize , lw ):
221222 """
222223 Return x, y, and the index of a label location.
@@ -278,37 +279,35 @@ def locate_label(self, linecontour, labelwidth):
278279 """
279280 Find good place to draw a label (relatively flat part of the contour).
280281 """
281-
282- # Number of contour points
283- nsize = len (linecontour )
284- if labelwidth > 1 :
285- xsize = int (np .ceil (nsize / labelwidth ))
286- else :
287- xsize = 1
288- if xsize == 1 :
289- ysize = nsize
290- else :
291- ysize = int (labelwidth )
292-
293- XX = np .resize (linecontour [:, 0 ], (xsize , ysize ))
294- YY = np .resize (linecontour [:, 1 ], (xsize , ysize ))
295- # I might have fouled up the following:
296- yfirst = YY [:, :1 ]
297- ylast = YY [:, - 1 :]
298- xfirst = XX [:, :1 ]
299- xlast = XX [:, - 1 :]
300- s = (yfirst - YY ) * (xlast - xfirst ) - (xfirst - XX ) * (ylast - yfirst )
301- L = np .hypot (xlast - xfirst , ylast - yfirst )
282+ ctr_size = len (linecontour )
283+ n_blocks = int (np .ceil (ctr_size / labelwidth )) if labelwidth > 1 else 1
284+ block_size = ctr_size if n_blocks == 1 else int (labelwidth )
285+ # Split contour into blocks of length ``block_size``, filling the last
286+ # block by cycling the contour start (per `np.resize` semantics). (Due
287+ # to cycling, the index returned is taken modulo ctr_size.)
288+ xx = np .resize (linecontour [:, 0 ], (n_blocks , block_size ))
289+ yy = np .resize (linecontour [:, 1 ], (n_blocks , block_size ))
290+ yfirst = yy [:, :1 ]
291+ ylast = yy [:, - 1 :]
292+ xfirst = xx [:, :1 ]
293+ xlast = xx [:, - 1 :]
294+ s = (yfirst - yy ) * (xlast - xfirst ) - (xfirst - xx ) * (ylast - yfirst )
295+ l = np .hypot (xlast - xfirst , ylast - yfirst )
302296 # Ignore warning that divide by zero throws, as this is a valid option
303297 with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
304- dist = np .sum (np .abs (s ) / L , axis = - 1 )
305- x , y , ind = self .get_label_coords (dist , XX , YY , ysize , labelwidth )
306-
307- # There must be a more efficient way...
308- lc = [tuple (l ) for l in linecontour ]
309- dind = lc .index ((x , y ))
310-
311- return x , y , dind
298+ distances = (abs (s ) / l ).sum (axis = - 1 )
299+ # Labels are drawn in the middle of the block (``hbsize``) where the
300+ # contour is the closest (per ``distances``) to a straight line, but
301+ # not `too_close()` to a preexisting label.
302+ hbsize = block_size // 2
303+ adist = np .argsort (distances )
304+ # If all candidates are `too_close()`, go back to the straightest part
305+ # (``adist[0]``).
306+ for idx in np .append (adist , adist [0 ]):
307+ x , y = xx [idx , hbsize ], yy [idx , hbsize ]
308+ if not self .too_close (x , y , labelwidth ):
309+ break
310+ return x , y , (idx * block_size + hbsize ) % ctr_size
312311
313312 def calc_label_rot_and_inline (self , slc , ind , lw , lc = None , spacing = 5 ):
314313 """
0 commit comments