Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3566405

Browse filesBrowse files
authored
Merge pull request #27197 from meeseeksmachine/auto-backport-of-pr-27045-on-v3.8.x
Backport PR #27045 on branch v3.8.x (Ensure valid path mangling for ContourLabeler)
2 parents 9e4ec86 + cd46813 commit 3566405
Copy full SHA for 3566405

File tree

3 files changed

+52
-3
lines changed
Filter options

3 files changed

+52
-3
lines changed

‎lib/matplotlib/contour.py

Copy file name to clipboardExpand all lines: lib/matplotlib/contour.py
+13-3Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ def _split_path_and_get_label_rotation(self, path, idx, screen_pos, lw, spacing=
350350
taken into account when breaking the path, but not when computing the angle.
351351
"""
352352
if hasattr(self, "_old_style_split_collections"):
353+
vis = False
354+
for coll in self._old_style_split_collections:
355+
vis |= coll.get_visible()
356+
coll.remove()
357+
self.set_visible(vis)
353358
del self._old_style_split_collections # Invalidate them.
354359

355360
xys = path.vertices
@@ -383,7 +388,7 @@ def _split_path_and_get_label_rotation(self, path, idx, screen_pos, lw, spacing=
383388
# If the path is closed, rotate it s.t. it starts at the label.
384389
is_closed_path = codes[stop - 1] == Path.CLOSEPOLY
385390
if is_closed_path:
386-
cc_xys = np.concatenate([xys[idx:-1], xys[:idx+1]])
391+
cc_xys = np.concatenate([cc_xys[idx:-1], cc_xys[:idx+1]])
387392
idx = 0
388393

389394
# Like np.interp, but additionally vectorized over fp.
@@ -418,8 +423,13 @@ def interp_vec(x, xp, fp): return [np.interp(x, xp, col) for col in fp.T]
418423
new_code_blocks = []
419424
if is_closed_path:
420425
if i0 != -1 and i1 != -1:
421-
new_xy_blocks.extend([[(x1, y1)], cc_xys[i1:i0+1], [(x0, y0)]])
422-
new_code_blocks.extend([[Path.MOVETO], [Path.LINETO] * (i0 + 2 - i1)])
426+
# This is probably wrong in the case that the entire contour would
427+
# be discarded, but ensures that a valid path is returned and is
428+
# consistent with behavior of mpl <3.8
429+
points = cc_xys[i1:i0+1]
430+
new_xy_blocks.extend([[(x1, y1)], points, [(x0, y0)]])
431+
nlines = len(points) + 1
432+
new_code_blocks.extend([[Path.MOVETO], [Path.LINETO] * nlines])
423433
else:
424434
if i0 != -1:
425435
new_xy_blocks.extend([cc_xys[:i0 + 1], [(x0, y0)]])
Loading

‎lib/matplotlib/tests/test_contour.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_contour.py
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,31 @@ def test_contour_manual_labels(split_collections):
116116

117117
plt.figure(figsize=(6, 2), dpi=200)
118118
cs = plt.contour(x, y, z)
119+
120+
_maybe_split_collections(split_collections)
121+
119122
pts = np.array([(1.0, 3.0), (1.0, 4.4), (1.0, 6.0)])
120123
plt.clabel(cs, manual=pts)
121124
pts = np.array([(2.0, 3.0), (2.0, 4.4), (2.0, 6.0)])
122125
plt.clabel(cs, manual=pts, fontsize='small', colors=('r', 'g'))
123126

127+
128+
@pytest.mark.parametrize("split_collections", [False, True])
129+
@image_comparison(['contour_disconnected_segments'],
130+
remove_text=True, style='mpl20', extensions=['png'])
131+
def test_contour_label_with_disconnected_segments(split_collections):
132+
x, y = np.mgrid[-1:1:21j, -1:1:21j]
133+
z = 1 / np.sqrt(0.01 + (x + 0.3) ** 2 + y ** 2)
134+
z += 1 / np.sqrt(0.01 + (x - 0.3) ** 2 + y ** 2)
135+
136+
plt.figure()
137+
cs = plt.contour(x, y, z, levels=[7])
138+
139+
# Adding labels should invalidate the old style
140+
_maybe_split_collections(split_collections)
141+
142+
cs.clabel(manual=[(0.2, 0.1)])
143+
124144
_maybe_split_collections(split_collections)
125145

126146

@@ -232,6 +252,9 @@ def test_labels(split_collections):
232252
disp_units = [(216, 177), (359, 290), (521, 406)]
233253
data_units = [(-2, .5), (0, -1.5), (2.8, 1)]
234254

255+
# Adding labels should invalidate the old style
256+
_maybe_split_collections(split_collections)
257+
235258
CS.clabel()
236259

237260
for x, y in data_units:
@@ -338,6 +361,22 @@ def test_clabel_zorder(use_clabeltext, contour_zorder, clabel_zorder):
338361
assert clabel.get_zorder() == expected_clabel_zorder
339362

340363

364+
def test_clabel_with_large_spacing():
365+
# When the inline spacing is large relative to the contour, it may cause the
366+
# entire contour to be removed. In current implementation, one line segment is
367+
# retained between the identified points.
368+
# This behavior may be worth reconsidering, but check to be sure we do not produce
369+
# an invalid path, which results in an error at clabel call time.
370+
# see gh-27045 for more information
371+
x = y = np.arange(-3.0, 3.01, 0.05)
372+
X, Y = np.meshgrid(x, y)
373+
Z = np.exp(-X**2 - Y**2)
374+
375+
fig, ax = plt.subplots()
376+
contourset = ax.contour(X, Y, Z, levels=[0.01, 0.2, .5, .8])
377+
ax.clabel(contourset, inline_spacing=100)
378+
379+
341380
# tol because ticks happen to fall on pixel boundaries so small
342381
# floating point changes in tick location flip which pixel gets
343382
# the tick.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.