matplotlib.pyplot polygon with equal aspect and tight layout

Viewed 26

I want to draw a set of polygons on an axis of equal aspect and apply tight layout on the figure.

However, it seems like the figure size doesn't get correctly adjusted when equal aspect and tight layout are used together.

Attempt 1

Equal aspect and tight layout

import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

fig, ax = plt.subplots(1,1)
l = 5
h = 1
poly = Polygon(
    np.array([
        [0,0],
        [l,0],
        [l,h],
        [0,h]
    ]),
    edgecolor="black",
    facecolor="gray",
)
ax.add_patch(poly)
ax.set_aspect("equal")
ax.set_xlim(0,l)
ax.set_ylim(0,h)
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$", rotation=0, labelpad=5)
fig.tight_layout(pad=0.25)
plt.show()

Output: notice the extra space on top and bottom. Also notice that the y axis label is out of bounds.

enter image description here

Attempt 2

Tight layout without equal aspect

import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

fig, ax = plt.subplots(1,1)
l = 5
h = 1
poly = Polygon(
    np.array([
        [0,0],
        [l,0],
        [l,h],
        [0,h]
    ]),
    edgecolor="black",
    facecolor="gray",
)
ax.add_patch(poly)
# ax.set_aspect("equal")
ax.set_xlim(0,l)
ax.set_ylim(0,h)
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$", rotation=0, labelpad=5)
fig.tight_layout(pad=0.25)
plt.show()

Output: works well, except that the aspect ratio is unequal

enter image description here

How to achieve tight layout and equal aspect ratio?

1 Answers

Additional margins appear if the aspect ratio of the Figure (default size from rcParams["figure.figsize"] defaults to [6.4, 4.8], i.e. an aspect ratio of 1.33) differs from the aspect ratio of the Axes. To avoid this, you need to adjust the aspect ratio of the Figure to match the aspect ratio of the Axes including ticks and labels. You can get it using get_tightbbox after you have drawn the Figure (for instance using draw_without_rendering).

For better visual result I recommend using constrained rather than tight layout.

import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import numpy as np

fig, ax = plt.subplots(1,1, layout='constrained')
l = 5
h = 1
fig.set_size_inches(10,2)
poly = Polygon(
    np.array([
        [0,0],
        [l,0],
        [l,h],
        [0,h]
    ]),
    edgecolor="black",
    facecolor="gray",
)
ax.add_patch(poly)
ax.set_aspect("equal")
ax.set_xlim(0,l)
ax.set_ylim(0,h)
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$", rotation=0, labelpad=5)

# adjust Figure aspect ratio to match Axes
fig.draw_without_rendering()
tb = fig.get_tightbbox(fig.canvas.get_renderer())
fig.set_size_inches(tb.width, tb.height)

plt.show()

enter image description here

Related