Skip to content

ENH: Add screen-proportional mutation_aspect to FancyBboxPatch - #32205

Open
Muhtasim-Munif-Fahim wants to merge 1 commit into
matplotlib:mainfrom
Muhtasim-Munif-Fahim:enh/round-boxstyle-mutation-aspect
Open

ENH: Add screen-proportional mutation_aspect to FancyBboxPatch#32205
Muhtasim-Munif-Fahim wants to merge 1 commit into
matplotlib:mainfrom
Muhtasim-Munif-Fahim:enh/round-boxstyle-mutation-aspect

Conversation

@Muhtasim-Munif-Fahim

Copy link
Copy Markdown

PR summary

What problem existed

FancyBboxPatch requires a numeric mutation_aspect to compensate for a non-unit axes aspect ratio. In a plot with aspect="auto" (e.g. a typical bar chart), the rounding of BoxStyle("Round", ...) is drawn as an ellipse on screen, not a circle. The only way to get isotropic rounding was the private workaround mutation_aspect=1/ax._get_aspect_ratio(), which is also computed too early (before layout) and therefore stale by draw time.

How it was reproduced

import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch, BoxStyle

def add_bar(ax, x, y, w, h, mutation_aspect=1):
    patch = FancyBboxPatch(
        (x, y), w, h,
        boxstyle=BoxStyle("Round", pad=0, rounding_size=w / 2),
        linewidth=0, facecolor="C0",
        mutation_aspect=mutation_aspect,
    )
    ax.add_patch(patch)

fig, ax = plt.subplots(figsize=(10, 5))
x, y, w, h = 0.3, 0, 0.18, 1
add_bar(ax, x, y, w, h)
ax.autoscale_view()
fig.canvas.draw()

What caused it

FancyBboxPatch.get_path() applies mutation_aspect in data coordinates only. When the axes display scaling differs between x and y (aspect != 1), a data-space circle is drawn as an on-screen ellipse. There was no public way to opt in to resolving the aspect ratio from the axes.

What changed

FancyBboxPatch.mutation_aspect now additionally accepts the string "screen-proportional". At draw time the patch resolves it from the owning axes using public API (get_window_extent, axis transforms), so the box is scaled like the data on screen and the rounding renders circular regardless of the axes aspect ratio. set_mutation_aspect validates the new value.

Why this approach

The issue proposed an opt-in mode; discussion suggested a name that is more specific than a generic "auto". The resolution is deferred to draw time so the layout (and thus the display scaling) is final, fixing the stale-value problem of the private-API workaround. All existing numeric behavior is unchanged.

How it was tested

  • python -m pytest lib/matplotlib/tests/test_patches.py -k "mutation_aspect" -v → 1 passed
  • python -m pytest lib/matplotlib/tests/test_patches.py → 73 passed, 11 skipped (pre-existing skips)
  • Added tests: display-space circular corner check with a non-unit-aspect axes, fallback to 1 without an Axes, and ValueError on invalid values.

Risks / limitations

  • Without an attached Axes, "screen-proportional" falls back to an aspect of 1 (documented in the new tests).
  • The resolved aspect is a snapshot at draw time; it updates when the figure is redrawn after a resize.

Related issue

Closes #31175

Add opt-in "screen-proportional" mode to FancyBboxPatch.mutation_aspect
that resolves the display-space aspect ratio from the axes at draw time.
Rounded corners of Round/other boxstyles then render circular on screen
regardless of the axes aspect ratio, without requiring the private
ax._get_aspect_ratio() workaround.

Closes matplotlib#31175.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH]: mutation_aspect for Round BoxStyle type

1 participant