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 d8bb1a5

Browse filesBrowse files
authored
ENH: rely on non-rectangular patch paths rather than bboxes for legend auto-placing (fix #9580) (#9598)
* use path rather than bbox for non rectangular patches * Add tests * Add a short breadcrumb note in api_changes
1 parent 7e32745 commit d8bb1a5
Copy full SHA for d8bb1a5

File tree

Expand file treeCollapse file tree

3 files changed

+69
-5
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+69
-5
lines changed
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Change of ``legend(loc="best")`` behavior
2+
-----------------------------------------
3+
4+
The algorithm of the auto-legend locator has been tweaked to better handle
5+
non rectangular patches. Additional details on this change can be found in
6+
:ghissue:`9580` and :ghissue:`9598`.

‎lib/matplotlib/legend.py

Copy file name to clipboardExpand all lines: lib/matplotlib/legend.py
+7-5Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -865,12 +865,14 @@ def _auto_legend_data(self):
865865
bboxes.append(
866866
artist.get_bbox().transformed(artist.get_data_transform()))
867867
elif isinstance(artist, Patch):
868-
bboxes.append(
869-
artist.get_path().get_extents(artist.get_transform()))
868+
lines.append(
869+
artist.get_transform().transform_path(artist.get_path()))
870870
elif isinstance(artist, Collection):
871-
_, offset_trf, hoffsets, _ = artist._prepare_points()
872-
for offset in offset_trf.transform(hoffsets):
873-
offsets.append(offset)
871+
transform, transOffset, hoffsets, _ = artist._prepare_points()
872+
if len(hoffsets):
873+
for offset in transOffset.transform(hoffsets):
874+
offsets.append(offset)
875+
874876
return bboxes, lines, offsets
875877

876878
def get_children(self):

‎lib/matplotlib/tests/test_legend.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_legend.py
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
from matplotlib.testing._markers import needs_usetex
1111
import matplotlib.pyplot as plt
1212
import matplotlib as mpl
13+
import matplotlib.patches as mpatches
1314
import matplotlib.transforms as mtransforms
1415
import matplotlib.collections as mcollections
1516
import matplotlib.lines as mlines
1617
from matplotlib.legend_handler import HandlerTuple
1718
import matplotlib.legend as mlegend
1819
from matplotlib import rc_context
1920
from matplotlib.font_manager import FontProperties
21+
from numpy.testing import assert_allclose
2022

2123

2224
def test_legend_ordereddict():
@@ -69,6 +71,60 @@ def test_legend_auto3():
6971
ax.legend(loc='best')
7072

7173

74+
def test_legend_auto4():
75+
"""
76+
Check that the legend location with automatic placement is the same,
77+
whatever the histogram type is. Related to issue #9580.
78+
"""
79+
# NB: barstacked is pointless with a single dataset.
80+
fig, axs = plt.subplots(ncols=3, figsize=(6.4, 2.4))
81+
leg_bboxes = []
82+
for ax, ht in zip(axs.flat, ('bar', 'step', 'stepfilled')):
83+
ax.set_title(ht)
84+
# A high bar on the left but an even higher one on the right.
85+
ax.hist([0] + 5*[9], bins=range(10), label="Legend", histtype=ht)
86+
leg = ax.legend(loc="best")
87+
fig.canvas.draw()
88+
leg_bboxes.append(
89+
leg.get_window_extent().transformed(ax.transAxes.inverted()))
90+
91+
# The histogram type "bar" is assumed to be the correct reference.
92+
assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds)
93+
assert_allclose(leg_bboxes[2].bounds, leg_bboxes[0].bounds)
94+
95+
96+
def test_legend_auto5():
97+
"""
98+
Check that the automatic placement handle a rather complex
99+
case with non rectangular patch. Related to issue #9580.
100+
"""
101+
fig, axs = plt.subplots(ncols=2, figsize=(9.6, 4.8))
102+
103+
leg_bboxes = []
104+
for ax, loc in zip(axs.flat, ("center", "best")):
105+
# An Ellipse patch at the top, a U-shaped Polygon patch at the
106+
# bottom and a ring-like Wedge patch: the correct placement of
107+
# the legend should be in the center.
108+
for _patch in [
109+
mpatches.Ellipse(
110+
xy=(0.5, 0.9), width=0.8, height=0.2, fc="C1"),
111+
mpatches.Polygon(np.array([
112+
[0, 1], [0, 0], [1, 0], [1, 1], [0.9, 1.0], [0.9, 0.1],
113+
[0.1, 0.1], [0.1, 1.0], [0.1, 1.0]]), fc="C1"),
114+
mpatches.Wedge((0.5, 0.5), 0.5, 0, 360, width=0.05, fc="C0")
115+
]:
116+
ax.add_patch(_patch)
117+
118+
ax.plot([0.1, 0.9], [0.9, 0.9], label="A segment") # sthg to label
119+
120+
leg = ax.legend(loc=loc)
121+
fig.canvas.draw()
122+
leg_bboxes.append(
123+
leg.get_window_extent().transformed(ax.transAxes.inverted()))
124+
125+
assert_allclose(leg_bboxes[1].bounds, leg_bboxes[0].bounds)
126+
127+
72128
@image_comparison(['legend_various_labels'], remove_text=True)
73129
def test_various_labels():
74130
# tests all sorts of label types

0 commit comments

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