Skip to content

Commit ff2fe6b

Browse files
committed
Inline and optimize ContourLabeler.get_label_coords.
`get_label_coords` is clearly an internal helper to `locate_label`; e.g. the `distances` parameter needs to be filled in with a very specific array that's internally computed by `locate_label`. Inline it, which also makes clearer the possibility to save some further computation for `locate_label` to return a linearized index (the part that was commented "There must be a more efficient way..."; indeed converting the array to a list of tuples to get back the index seems pretty inefficient). Also better document the implementation of `get_label_coords` (I'm not actually changing any of the code logic).
1 parent f50fe72 commit ff2fe6b

File tree

2 files changed

+30
-20
lines changed

2 files changed

+30
-20
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``ContourLabeler.get_label_coords`` is deprecated
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
It is considered an internal helper.

lib/matplotlib/contour.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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,21 +279,20 @@ 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)
282+
contour_size = len(linecontour)
284283
if labelwidth > 1:
285-
xsize = int(np.ceil(nsize / labelwidth))
284+
n_blocks = int(np.ceil(contour_size / labelwidth))
286285
else:
287-
xsize = 1
288-
if xsize == 1:
289-
ysize = nsize
286+
n_blocks = 1
287+
if n_blocks == 1:
288+
block_size = contour_size
290289
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:
290+
block_size = int(labelwidth)
291+
# Split contour into blocks of length ``block_size``, filling the last
292+
# block by cycling the contour start (per `np.resize` semantics). (Due
293+
# to cycling, the index returned is taken modulo contour_size.)
294+
XX = np.resize(linecontour[:, 0], (n_blocks, block_size))
295+
YY = np.resize(linecontour[:, 1], (n_blocks, block_size))
296296
yfirst = YY[:, :1]
297297
ylast = YY[:, -1:]
298298
xfirst = XX[:, :1]
@@ -301,14 +301,21 @@ def locate_label(self, linecontour, labelwidth):
301301
L = np.hypot(xlast - xfirst, ylast - yfirst)
302302
# Ignore warning that divide by zero throws, as this is a valid option
303303
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
304+
distances = np.sum(np.abs(s) / L, axis=-1)
305+
# Labels are drawn in the middle of the block (``hbsize``) where the
306+
# contour is the closest (per ``distances``) to a straight line, but
307+
# not `too_close()` to a preexisting label.
308+
hbsize = block_size // 2
309+
adist = np.argsort(distances)
310+
for idx in adist:
311+
x, y = XX[idx, hbsize], YY[idx, hbsize]
312+
if self.too_close(x, y, labelwidth):
313+
continue
314+
return x, y, (idx * block_size + hbsize) % contour_size
315+
# If all candidates are `too_close()`, just use the straightest part.
316+
idx = adist[0]
317+
x, y = XX[idx, hbsize], YY[idx, hbsize]
318+
return x, y, hbsize % contour_size
312319

313320
def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5):
314321
"""

0 commit comments

Comments
 (0)