some new features
This commit is contained in:
301
.venv/lib/python3.12/site-packages/matplotlib/_tight_layout.py
Normal file
301
.venv/lib/python3.12/site-packages/matplotlib/_tight_layout.py
Normal file
@ -0,0 +1,301 @@
|
||||
"""
|
||||
Routines to adjust subplot params so that subplots are
|
||||
nicely fit in the figure. In doing so, only axis labels, tick labels, Axes
|
||||
titles and offsetboxes that are anchored to Axes are currently considered.
|
||||
|
||||
Internally, this module assumes that the margins (left margin, etc.) which are
|
||||
differences between ``Axes.get_tightbbox`` and ``Axes.bbox`` are independent of
|
||||
Axes position. This may fail if ``Axes.adjustable`` is ``datalim`` as well as
|
||||
such cases as when left or right margin are affected by xlabel.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import _api, artist as martist
|
||||
from matplotlib.font_manager import FontProperties
|
||||
from matplotlib.transforms import Bbox
|
||||
|
||||
|
||||
def _auto_adjust_subplotpars(
|
||||
fig, renderer, shape, span_pairs, subplot_list,
|
||||
ax_bbox_list=None, pad=1.08, h_pad=None, w_pad=None, rect=None):
|
||||
"""
|
||||
Return a dict of subplot parameters to adjust spacing between subplots
|
||||
or ``None`` if resulting Axes would have zero height or width.
|
||||
|
||||
Note that this function ignores geometry information of subplot itself, but
|
||||
uses what is given by the *shape* and *subplot_list* parameters. Also, the
|
||||
results could be incorrect if some subplots have ``adjustable=datalim``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
shape : tuple[int, int]
|
||||
Number of rows and columns of the grid.
|
||||
span_pairs : list[tuple[slice, slice]]
|
||||
List of rowspans and colspans occupied by each subplot.
|
||||
subplot_list : list of subplots
|
||||
List of subplots that will be used to calculate optimal subplot_params.
|
||||
pad : float
|
||||
Padding between the figure edge and the edges of subplots, as a
|
||||
fraction of the font size.
|
||||
h_pad, w_pad : float
|
||||
Padding (height/width) between edges of adjacent subplots, as a
|
||||
fraction of the font size. Defaults to *pad*.
|
||||
rect : tuple
|
||||
(left, bottom, right, top), default: None.
|
||||
"""
|
||||
rows, cols = shape
|
||||
|
||||
font_size_inch = (FontProperties(
|
||||
size=mpl.rcParams["font.size"]).get_size_in_points() / 72)
|
||||
pad_inch = pad * font_size_inch
|
||||
vpad_inch = h_pad * font_size_inch if h_pad is not None else pad_inch
|
||||
hpad_inch = w_pad * font_size_inch if w_pad is not None else pad_inch
|
||||
|
||||
if len(span_pairs) != len(subplot_list) or len(subplot_list) == 0:
|
||||
raise ValueError
|
||||
|
||||
if rect is None:
|
||||
margin_left = margin_bottom = margin_right = margin_top = None
|
||||
else:
|
||||
margin_left, margin_bottom, _right, _top = rect
|
||||
margin_right = 1 - _right if _right else None
|
||||
margin_top = 1 - _top if _top else None
|
||||
|
||||
vspaces = np.zeros((rows + 1, cols))
|
||||
hspaces = np.zeros((rows, cols + 1))
|
||||
|
||||
if ax_bbox_list is None:
|
||||
ax_bbox_list = [
|
||||
Bbox.union([ax.get_position(original=True) for ax in subplots])
|
||||
for subplots in subplot_list]
|
||||
|
||||
for subplots, ax_bbox, (rowspan, colspan) in zip(
|
||||
subplot_list, ax_bbox_list, span_pairs):
|
||||
if all(not ax.get_visible() for ax in subplots):
|
||||
continue
|
||||
|
||||
bb = []
|
||||
for ax in subplots:
|
||||
if ax.get_visible():
|
||||
bb += [martist._get_tightbbox_for_layout_only(ax, renderer)]
|
||||
|
||||
tight_bbox_raw = Bbox.union(bb)
|
||||
tight_bbox = fig.transFigure.inverted().transform_bbox(tight_bbox_raw)
|
||||
|
||||
hspaces[rowspan, colspan.start] += ax_bbox.xmin - tight_bbox.xmin # l
|
||||
hspaces[rowspan, colspan.stop] += tight_bbox.xmax - ax_bbox.xmax # r
|
||||
vspaces[rowspan.start, colspan] += tight_bbox.ymax - ax_bbox.ymax # t
|
||||
vspaces[rowspan.stop, colspan] += ax_bbox.ymin - tight_bbox.ymin # b
|
||||
|
||||
fig_width_inch, fig_height_inch = fig.get_size_inches()
|
||||
|
||||
# margins can be negative for Axes with aspect applied, so use max(, 0) to
|
||||
# make them nonnegative.
|
||||
if not margin_left:
|
||||
margin_left = max(hspaces[:, 0].max(), 0) + pad_inch/fig_width_inch
|
||||
suplabel = fig._supylabel
|
||||
if suplabel and suplabel.get_in_layout():
|
||||
rel_width = fig.transFigure.inverted().transform_bbox(
|
||||
suplabel.get_window_extent(renderer)).width
|
||||
margin_left += rel_width + pad_inch/fig_width_inch
|
||||
if not margin_right:
|
||||
margin_right = max(hspaces[:, -1].max(), 0) + pad_inch/fig_width_inch
|
||||
if not margin_top:
|
||||
margin_top = max(vspaces[0, :].max(), 0) + pad_inch/fig_height_inch
|
||||
if fig._suptitle and fig._suptitle.get_in_layout():
|
||||
rel_height = fig.transFigure.inverted().transform_bbox(
|
||||
fig._suptitle.get_window_extent(renderer)).height
|
||||
margin_top += rel_height + pad_inch/fig_height_inch
|
||||
if not margin_bottom:
|
||||
margin_bottom = max(vspaces[-1, :].max(), 0) + pad_inch/fig_height_inch
|
||||
suplabel = fig._supxlabel
|
||||
if suplabel and suplabel.get_in_layout():
|
||||
rel_height = fig.transFigure.inverted().transform_bbox(
|
||||
suplabel.get_window_extent(renderer)).height
|
||||
margin_bottom += rel_height + pad_inch/fig_height_inch
|
||||
|
||||
if margin_left + margin_right >= 1:
|
||||
_api.warn_external('Tight layout not applied. The left and right '
|
||||
'margins cannot be made large enough to '
|
||||
'accommodate all Axes decorations.')
|
||||
return None
|
||||
if margin_bottom + margin_top >= 1:
|
||||
_api.warn_external('Tight layout not applied. The bottom and top '
|
||||
'margins cannot be made large enough to '
|
||||
'accommodate all Axes decorations.')
|
||||
return None
|
||||
|
||||
kwargs = dict(left=margin_left,
|
||||
right=1 - margin_right,
|
||||
bottom=margin_bottom,
|
||||
top=1 - margin_top)
|
||||
|
||||
if cols > 1:
|
||||
hspace = hspaces[:, 1:-1].max() + hpad_inch / fig_width_inch
|
||||
# axes widths:
|
||||
h_axes = (1 - margin_right - margin_left - hspace * (cols - 1)) / cols
|
||||
if h_axes < 0:
|
||||
_api.warn_external('Tight layout not applied. tight_layout '
|
||||
'cannot make Axes width small enough to '
|
||||
'accommodate all Axes decorations')
|
||||
return None
|
||||
else:
|
||||
kwargs["wspace"] = hspace / h_axes
|
||||
if rows > 1:
|
||||
vspace = vspaces[1:-1, :].max() + vpad_inch / fig_height_inch
|
||||
v_axes = (1 - margin_top - margin_bottom - vspace * (rows - 1)) / rows
|
||||
if v_axes < 0:
|
||||
_api.warn_external('Tight layout not applied. tight_layout '
|
||||
'cannot make Axes height small enough to '
|
||||
'accommodate all Axes decorations.')
|
||||
return None
|
||||
else:
|
||||
kwargs["hspace"] = vspace / v_axes
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
def get_subplotspec_list(axes_list, grid_spec=None):
|
||||
"""
|
||||
Return a list of subplotspec from the given list of Axes.
|
||||
|
||||
For an instance of Axes that does not support subplotspec, None is inserted
|
||||
in the list.
|
||||
|
||||
If grid_spec is given, None is inserted for those not from the given
|
||||
grid_spec.
|
||||
"""
|
||||
subplotspec_list = []
|
||||
for ax in axes_list:
|
||||
axes_or_locator = ax.get_axes_locator()
|
||||
if axes_or_locator is None:
|
||||
axes_or_locator = ax
|
||||
|
||||
if hasattr(axes_or_locator, "get_subplotspec"):
|
||||
subplotspec = axes_or_locator.get_subplotspec()
|
||||
if subplotspec is not None:
|
||||
subplotspec = subplotspec.get_topmost_subplotspec()
|
||||
gs = subplotspec.get_gridspec()
|
||||
if grid_spec is not None:
|
||||
if gs != grid_spec:
|
||||
subplotspec = None
|
||||
elif gs.locally_modified_subplot_params():
|
||||
subplotspec = None
|
||||
else:
|
||||
subplotspec = None
|
||||
|
||||
subplotspec_list.append(subplotspec)
|
||||
|
||||
return subplotspec_list
|
||||
|
||||
|
||||
def get_tight_layout_figure(fig, axes_list, subplotspec_list, renderer,
|
||||
pad=1.08, h_pad=None, w_pad=None, rect=None):
|
||||
"""
|
||||
Return subplot parameters for tight-layouted-figure with specified padding.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fig : Figure
|
||||
axes_list : list of Axes
|
||||
subplotspec_list : list of `.SubplotSpec`
|
||||
The subplotspecs of each Axes.
|
||||
renderer : renderer
|
||||
pad : float
|
||||
Padding between the figure edge and the edges of subplots, as a
|
||||
fraction of the font size.
|
||||
h_pad, w_pad : float
|
||||
Padding (height/width) between edges of adjacent subplots. Defaults to
|
||||
*pad*.
|
||||
rect : tuple (left, bottom, right, top), default: None.
|
||||
rectangle in normalized figure coordinates
|
||||
that the whole subplots area (including labels) will fit into.
|
||||
Defaults to using the entire figure.
|
||||
|
||||
Returns
|
||||
-------
|
||||
subplotspec or None
|
||||
subplotspec kwargs to be passed to `.Figure.subplots_adjust` or
|
||||
None if tight_layout could not be accomplished.
|
||||
"""
|
||||
|
||||
# Multiple Axes can share same subplotspec (e.g., if using axes_grid1);
|
||||
# we need to group them together.
|
||||
ss_to_subplots = {ss: [] for ss in subplotspec_list}
|
||||
for ax, ss in zip(axes_list, subplotspec_list):
|
||||
ss_to_subplots[ss].append(ax)
|
||||
if ss_to_subplots.pop(None, None):
|
||||
_api.warn_external(
|
||||
"This figure includes Axes that are not compatible with "
|
||||
"tight_layout, so results might be incorrect.")
|
||||
if not ss_to_subplots:
|
||||
return {}
|
||||
subplot_list = list(ss_to_subplots.values())
|
||||
ax_bbox_list = [ss.get_position(fig) for ss in ss_to_subplots]
|
||||
|
||||
max_nrows = max(ss.get_gridspec().nrows for ss in ss_to_subplots)
|
||||
max_ncols = max(ss.get_gridspec().ncols for ss in ss_to_subplots)
|
||||
|
||||
span_pairs = []
|
||||
for ss in ss_to_subplots:
|
||||
# The intent here is to support Axes from different gridspecs where
|
||||
# one's nrows (or ncols) is a multiple of the other (e.g. 2 and 4),
|
||||
# but this doesn't actually work because the computed wspace, in
|
||||
# relative-axes-height, corresponds to different physical spacings for
|
||||
# the 2-row grid and the 4-row grid. Still, this code is left, mostly
|
||||
# for backcompat.
|
||||
rows, cols = ss.get_gridspec().get_geometry()
|
||||
div_row, mod_row = divmod(max_nrows, rows)
|
||||
div_col, mod_col = divmod(max_ncols, cols)
|
||||
if mod_row != 0:
|
||||
_api.warn_external('tight_layout not applied: number of rows '
|
||||
'in subplot specifications must be '
|
||||
'multiples of one another.')
|
||||
return {}
|
||||
if mod_col != 0:
|
||||
_api.warn_external('tight_layout not applied: number of '
|
||||
'columns in subplot specifications must be '
|
||||
'multiples of one another.')
|
||||
return {}
|
||||
span_pairs.append((
|
||||
slice(ss.rowspan.start * div_row, ss.rowspan.stop * div_row),
|
||||
slice(ss.colspan.start * div_col, ss.colspan.stop * div_col)))
|
||||
|
||||
kwargs = _auto_adjust_subplotpars(fig, renderer,
|
||||
shape=(max_nrows, max_ncols),
|
||||
span_pairs=span_pairs,
|
||||
subplot_list=subplot_list,
|
||||
ax_bbox_list=ax_bbox_list,
|
||||
pad=pad, h_pad=h_pad, w_pad=w_pad)
|
||||
|
||||
# kwargs can be none if tight_layout fails...
|
||||
if rect is not None and kwargs is not None:
|
||||
# if rect is given, the whole subplots area (including
|
||||
# labels) will fit into the rect instead of the
|
||||
# figure. Note that the rect argument of
|
||||
# *auto_adjust_subplotpars* specify the area that will be
|
||||
# covered by the total area of axes.bbox. Thus we call
|
||||
# auto_adjust_subplotpars twice, where the second run
|
||||
# with adjusted rect parameters.
|
||||
|
||||
left, bottom, right, top = rect
|
||||
if left is not None:
|
||||
left += kwargs["left"]
|
||||
if bottom is not None:
|
||||
bottom += kwargs["bottom"]
|
||||
if right is not None:
|
||||
right -= (1 - kwargs["right"])
|
||||
if top is not None:
|
||||
top -= (1 - kwargs["top"])
|
||||
|
||||
kwargs = _auto_adjust_subplotpars(fig, renderer,
|
||||
shape=(max_nrows, max_ncols),
|
||||
span_pairs=span_pairs,
|
||||
subplot_list=subplot_list,
|
||||
ax_bbox_list=ax_bbox_list,
|
||||
pad=pad, h_pad=h_pad, w_pad=w_pad,
|
||||
rect=(left, bottom, right, top))
|
||||
|
||||
return kwargs
|
||||
Reference in New Issue
Block a user