Skip to content

Commit 8d45135

Browse files
authored
Merge pull request matplotlib#31654 from meeseeksmachine/auto-backport-of-pr-30108-on-v3.11.x
Backport PR matplotlib#30108 on branch v3.11.x (Fix constrained layout applying pad multiple times)
2 parents c542bf0 + 782a1f4 commit 8d45135

7 files changed

Lines changed: 66 additions & 25 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Complex layouts and constrained layout
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Constrained layout now produces smaller spacing between subplots in some
4+
circumstances. This should only affect complex layouts where rows or columns
5+
contain different numbers of subplots, for example a layout created with
6+
``plt.subplot_mosaic('AC;BC', layout='constrained')``.

lib/matplotlib/_constrained_layout.py

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -539,14 +539,10 @@ def match_submerged_margins(layoutgrids, fig):
539539

540540
# interior columns:
541541
if len(ss1.colspan) > 1:
542-
maxsubl = np.max(
543-
lg1.margin_vals['left'][ss1.colspan[1:]] +
544-
lg1.margin_vals['leftcb'][ss1.colspan[1:]]
545-
)
546-
maxsubr = np.max(
547-
lg1.margin_vals['right'][ss1.colspan[:-1]] +
548-
lg1.margin_vals['rightcb'][ss1.colspan[:-1]]
549-
)
542+
leftcb = lg1.margin_vals['leftcb'][ss1.colspan[1:]]
543+
rightcb = lg1.margin_vals['rightcb'][ss1.colspan[:-1]]
544+
maxsubl = np.max(lg1.margin_vals['left'][ss1.colspan[1:]] + leftcb)
545+
maxsubr = np.max(lg1.margin_vals['right'][ss1.colspan[:-1]] + rightcb)
550546
for ax2 in axs:
551547
ss2 = ax2.get_subplotspec()
552548
lg2 = layoutgrids[ss2.get_gridspec()]
@@ -561,22 +557,17 @@ def match_submerged_margins(layoutgrids, fig):
561557
lg2.margin_vals['rightcb'][ss2.colspan[:-1]])
562558
if maxsubr2 > maxsubr:
563559
maxsubr = maxsubr2
564-
for i in ss1.colspan[1:]:
565-
lg1.edit_margin_min('left', maxsubl, cell=i)
566-
for i in ss1.colspan[:-1]:
567-
lg1.edit_margin_min('right', maxsubr, cell=i)
560+
for i, cb in zip(ss1.colspan[1:], leftcb):
561+
lg1.edit_margin_min('left', maxsubl - cb, cell=i)
562+
for i, cb in zip(ss1.colspan[:-1], rightcb):
563+
lg1.edit_margin_min('right', maxsubr - cb, cell=i)
568564

569565
# interior rows:
570566
if len(ss1.rowspan) > 1:
571-
maxsubt = np.max(
572-
lg1.margin_vals['top'][ss1.rowspan[1:]] +
573-
lg1.margin_vals['topcb'][ss1.rowspan[1:]]
574-
)
575-
maxsubb = np.max(
576-
lg1.margin_vals['bottom'][ss1.rowspan[:-1]] +
577-
lg1.margin_vals['bottomcb'][ss1.rowspan[:-1]]
578-
)
579-
567+
topcb = lg1.margin_vals['topcb'][ss1.rowspan[1:]]
568+
bottomcb = lg1.margin_vals['bottomcb'][ss1.rowspan[:-1]]
569+
maxsubt = np.max(lg1.margin_vals['top'][ss1.rowspan[1:]] + topcb)
570+
maxsubb = np.max(lg1.margin_vals['bottom'][ss1.rowspan[:-1]] + bottomcb)
580571
for ax2 in axs:
581572
ss2 = ax2.get_subplotspec()
582573
lg2 = layoutgrids[ss2.get_gridspec()]
@@ -590,10 +581,10 @@ def match_submerged_margins(layoutgrids, fig):
590581
lg2.margin_vals['bottom'][ss2.rowspan[:-1]] +
591582
lg2.margin_vals['bottomcb'][ss2.rowspan[:-1]]
592583
), maxsubb])
593-
for i in ss1.rowspan[1:]:
594-
lg1.edit_margin_min('top', maxsubt, cell=i)
595-
for i in ss1.rowspan[:-1]:
596-
lg1.edit_margin_min('bottom', maxsubb, cell=i)
584+
for i, cb in zip(ss1.rowspan[1:], topcb):
585+
lg1.edit_margin_min('top', maxsubt - cb, cell=i)
586+
for i, cb in zip(ss1.rowspan[:-1], bottomcb):
587+
lg1.edit_margin_min('bottom', maxsubb - cb, cell=i)
597588

598589
return axs
599590

19 KB
Loading
12.2 KB
Loading
2.17 KB
Loading
6.33 KB
Loading

lib/matplotlib/tests/test_constrainedlayout.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,3 +809,47 @@ def test_submerged_subfig():
809809
for ax in axs[1:]:
810810
assert np.allclose(ax.get_position().bounds[-1],
811811
axs[0].get_position().bounds[-1], atol=1e-6)
812+
813+
814+
def test_submerged_height_gap():
815+
"""Test that the gap between rows does not depend on the number of columns."""
816+
817+
mosaic1 = "AC;BC"
818+
mosaic2 = "ACDE;BCDE"
819+
820+
fig1, ax_dict1 = plt.subplot_mosaic(mosaic1, layout='constrained')
821+
fig2, ax_dict2 = plt.subplot_mosaic(mosaic2, layout='constrained')
822+
for fig in fig1, fig2:
823+
fig.get_layout_engine().set(h_pad=0.2)
824+
fig.draw_without_rendering()
825+
826+
for label in 'A', 'B':
827+
np.testing.assert_allclose(ax_dict1[label].get_position().bounds[-1],
828+
ax_dict2[label].get_position().bounds[-1])
829+
830+
831+
def test_submerged_width_gap():
832+
"""Test that the gap between columns does not depend on the number of rows."""
833+
834+
mosaic1 = "AB;CC"
835+
mosaic2 = "AB;CC;DD"
836+
837+
fig1, ax_dict1 = plt.subplot_mosaic(mosaic1, layout='constrained')
838+
fig2, ax_dict2 = plt.subplot_mosaic(mosaic2, layout='constrained')
839+
for fig in fig1, fig2:
840+
fig.get_layout_engine().set(w_pad=0.2)
841+
fig.draw_without_rendering()
842+
843+
for label in 'A', 'B':
844+
np.testing.assert_allclose(ax_dict1[label].get_position().bounds[-2],
845+
ax_dict2[label].get_position().bounds[-2])
846+
847+
848+
@image_comparison(['test_submerged_with_colorbar.png'], style='mpl20')
849+
def test_submerged_with_colorbar():
850+
mosaic = "AABBCC;DDDEEE"
851+
852+
fig, ax_dict = plt.subplot_mosaic(mosaic, layout='constrained')
853+
854+
cf = ax_dict['A'].contourf([[0, 1], [2, 3]])
855+
fig.colorbar(cf)

0 commit comments

Comments
 (0)