ENH: compressed layout by jklymak · Pull Request #22289 · matplotlib/matplotlib · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions lib/matplotlib/_constrained_layout.py
2 changes: 1 addition & 1 deletion lib/matplotlib/_layoutgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ def plot_children(fig, lg=None, level=0, printit=False):
import matplotlib.patches as mpatches

if lg is None:
_layoutgrids = fig.execute_constrained_layout()
_layoutgrids = fig.get_layout_engine().execute(fig)
lg = _layoutgrids[fig]
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
col = colors[level]
Expand Down
21 changes: 15 additions & 6 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2245,7 +2245,7 @@ def __init__(self,
The use of this parameter is discouraged. Please use
``layout='constrained'`` instead.

layout : {'constrained', 'tight', `.LayoutEngine`, None}, optional
layout : {'constrained', 'compressed', 'tight', `.LayoutEngine`, None}
The layout mechanism for positioning of plot elements to avoid
overlapping Axes decorations (labels, ticks, etc). Note that
layout managers can have significant performance penalties.
Expand All @@ -2258,6 +2258,10 @@ def __init__(self,
See :doc:`/tutorials/intermediate/constrainedlayout_guide`
for examples.

- 'compressed': uses the same algorithm as 'constrained', but
removes extra space between fixed-aspect-ratio Axes. Best for
simple grids of axes.

- 'tight': Use the tight layout mechanism. This is a relatively
simple algorithm that adjusts the subplot parameters so that
decorations do not overlap. See `.Figure.set_tight_layout` for
Expand Down Expand Up @@ -2388,11 +2392,13 @@ def set_layout_engine(self, layout=None, **kwargs):

Parameters
----------
layout : {'constrained', 'tight'} or `~.LayoutEngine`
'constrained' will use `~.ConstrainedLayoutEngine`, 'tight' will
use `~.TightLayoutEngine`. Users and libraries can define their
own layout engines as well.
kwargs : dict
layout: {'constrained', 'compressed', 'tight'} or `~.LayoutEngine`
'constrained' will use `~.ConstrainedLayoutEngine`,
'compressed' will also use ConstrainedLayoutEngine, but with a
correction that attempts to make a good layout for fixed-aspect
ratio Axes. 'tight' uses `~.TightLayoutEngine`. Users and
libraries can define their own layout engines as well.
kwargs: dict
The keyword arguments are passed to the layout engine to set things
like padding and margin sizes. Only used if *layout* is a string.
"""
Expand All @@ -2408,6 +2414,9 @@ def set_layout_engine(self, layout=None, **kwargs):
new_layout_engine = TightLayoutEngine(**kwargs)
elif layout == 'constrained':
new_layout_engine = ConstrainedLayoutEngine(**kwargs)
elif layout == 'compressed':
new_layout_engine = ConstrainedLayoutEngine(compress=True,
**kwargs)
elif isinstance(layout, LayoutEngine):
new_layout_engine = layout
else:
Expand Down
10 changes: 8 additions & 2 deletions lib/matplotlib/layout_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class ConstrainedLayoutEngine(LayoutEngine):

def __init__(self, *, h_pad=None, w_pad=None,
hspace=None, wspace=None, rect=(0, 0, 1, 1),
**kwargs):
compress=False, **kwargs):
"""
Initialize ``constrained_layout`` settings.

Expand All @@ -201,6 +201,10 @@ def __init__(self, *, h_pad=None, w_pad=None,
rect : tuple of 4 floats
Rectangle in figure coordinates to perform constrained layout in
(left, bottom, width, height), each from 0-1.
compress : bool
Whether to shift Axes so that white space in between them is
removed. This is useful for simple grids of fixed-aspect Axes (e.g.
a grid of images). See :ref:`compressed_layout`.
"""
super().__init__(**kwargs)
# set the defaults:
Expand All @@ -212,6 +216,7 @@ def __init__(self, *, h_pad=None, w_pad=None,
# set anything that was passed in (None will be ignored):
self.set(w_pad=w_pad, h_pad=h_pad, wspace=wspace, hspace=hspace,
rect=rect)
self._compress = compress

def execute(self, fig):
"""
Expand All @@ -229,7 +234,8 @@ def execute(self, fig):
return do_constrained_layout(fig, w_pad=w_pad, h_pad=h_pad,
wspace=self._params['wspace'],
hspace=self._params['hspace'],
rect=self._params['rect'])
rect=self._params['rect'],
compress=self._compress)

def set(self, *, h_pad=None, w_pad=None,
hspace=None, wspace=None, rect=None):
Expand Down
31 changes: 31 additions & 0 deletions lib/matplotlib/tests/test_constrainedlayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,34 @@ def test_rect():
assert ppos.y1 < 0.5
assert ppos.x0 > 0.2
assert ppos.y0 > 0.2


def test_compressed1():
fig, axs = plt.subplots(3, 2, layout='compressed',
sharex=True, sharey=True)
for ax in axs.flat:
pc = ax.imshow(np.random.randn(20, 20))

fig.colorbar(pc, ax=axs)
fig.draw_without_rendering()

pos = axs[0, 0].get_position()
np.testing.assert_allclose(pos.x0, 0.2344, atol=1e-3)
pos = axs[0, 1].get_position()
np.testing.assert_allclose(pos.x1, 0.7024, atol=1e-3)

# wider than tall
fig, axs = plt.subplots(2, 3, layout='compressed',
sharex=True, sharey=True, figsize=(5, 4))
for ax in axs.flat:
pc = ax.imshow(np.random.randn(20, 20))

fig.colorbar(pc, ax=axs)
fig.draw_without_rendering()

pos = axs[0, 0].get_position()
np.testing.assert_allclose(pos.x0, 0.06195, atol=1e-3)
np.testing.assert_allclose(pos.y1, 0.8537, atol=1e-3)
pos = axs[1, 2].get_position()
np.testing.assert_allclose(pos.x1, 0.8618, atol=1e-3)
np.testing.assert_allclose(pos.y0, 0.1934, atol=1e-3)
50 changes: 40 additions & 10 deletions tutorials/intermediate/arranging_axes.py
Loading