BUG: Refresh 3D axis projection before tightbbox to fix constrained layout overlap (GH#31277)#31548
Conversation
4c805f7 to
b8ee493
Compare
|
The testing failures are real and require the baseline images to be regenerated, but I think this is a pretty clean fix that solves things correctly. A note: your PR description and follow-on comment appear AI-generated. Please do not do this, except for direct translation if English is not your first language. There are things in it which do not match the code ( |
b8ee493 to
5959833
Compare
|
Thanks — that's fair feedback. You're right that the description and follow-up comment were not precise and contained a leftover reference to I've also narrowed the fix:
|
|
Does it make more sense to put all this new logic inside the constrained layout check? We should be avoiding code that can have side effects, what was the issue with Also, please comment on the extent of AI usage to write your comments here. |
…ayout overlap (GH#31277) Axis3D.get_tightbbox() read stale tick-label (x, y) positions that were last set during the previous draw() call. When constrained_layout queries the bounding boxes before any draw has occurred, the positions are uninitialised, causing the 3D subplot's reported bbox to be too small. Adjacent subplots then overlap the 3D tick labels. Fix: at the start of Axis3D.get_tightbbox(), refresh the projection matrix (M / invM) via apply_aspect() and get_proj(), then call self.draw() inside renderer._draw_disabled() to update the tick-label positions without producing any visible output. The rest of the method already reads those positions correctly. Two regression tests are added: - test_axis_get_tightbbox_before_draw_matches_after_draw: the bbox returned before any draw() must match the one returned after draw(). - test_constrained_layout_3d_no_overlap_with_subplot_title: with a 3D subplot above a 2D subplot in constrained_layout, the 3D tight bbox bottom must not overlap the 2D subplot title.
5959833 to
b55e1e1
Compare
PR summary
Closes #31277
When using
constrained_layout=Truewith a 3D subplot above a 2D subplot, the 3D tick labels overlap the title of the subplot below.Before / After:
Minimal reproducer:
Root cause
Axis3D.get_tightbbox()reads the 2D (display-space) positions of tick labels — but those positions are only updated duringdraw(). Whenconstrained_layoutqueries the bounding boxes before the first draw, the positions are uninitialised, so the reported bbox is degenerate. The layout engine then allocates too little vertical space for the 3D axes.The code itself acknowledges this dependency with the comment:
Fix
At the start of
Axis3D.get_tightbbox(), whenconstrained_layoutis active and the axis has not yet been initialised:axes.MisNone(before the first real draw), compute the projection matrix viaget_proj()and its inverse.self.draw(renderer)insiderenderer._draw_disabled()to update the per-tick 2D positions without producing any visible output.This initialisation is guarded by a per-axis flag
_tightbbox_initializedso it runs at most once per axis, and only whenconstrained_layoutis active.tight_layoutand plain figures are unaffected.The
test_stem3dbaseline is updated because that test usesconstrained_layout=True: the fix changes its output correctly — 3D tick labels are now fully visible instead of being clipped outside the figure bounds.This is a targeted fix and does not refactor the wider 3D layout machinery (see #19519 for the broader problem).
AI Disclosure
I used GitHub Copilot (Claude Sonnet 4.5) to assist with this PR: exploring the
axis3d.pycode path, drafting the fix, and writing the regression tests. All code was reviewed and tested manually.PR checklist