Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
ENH: box aspect for axes#14917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
ENH: box aspect for axes#14917
Uh oh!
There was an error while loading.Please reload this page.
Conversation
jklymak commentedJul 30, 2019
Seems cool. But I think we need to sit down and think about all the axes sizing and sharing options a bit more holistically. I think if you change the non-original position then you can keep constrained layout working. |
ImportanceOfBeingErnest commentedJul 30, 2019
I would have thought that is what I'm doing in line 1527. Will need to check this at some later point.
Sure, that's why I put it out in its current state, to see what is possible and to define some working use cases. In general what's still missing is a sharing of geometries (as opposed to sharing of data); or maybe call it a twinning across subplots. |
jklymak commentedJul 30, 2019
Great, then I'm surprised there are issues with |
jklymak commentedJul 30, 2019
anntzer commentedJul 30, 2019
I think it's a great idea :) it's not too surprising if the second call overwrites the first one, whereas in ... do we want the second to overwrite the first one? do we want to add a check and error out? or whatnot. |
ImportanceOfBeingErnest commentedJul 30, 2019
No, the usual (data-)aspect and the box aspect are not mutually exclusive. In fact using both may make perfect sense, as shown in caseF.. (It's just that |
anntzer commentedJul 30, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Ah, thanks for the clarification, I missed that; ignore the nonsense I wrote above. |
jklymak commentedJul 30, 2019
Some things to consider: What happens if the user manually sets the position and the ax.set_position([0.1,0.1,0.9,0.2])ax.set_box_aspect(1.) For constrained_layout, the I'm not sure which behaviour you want here, but you'll need to decide which call trumps which here. Even more tricky is ax.set_box_aspect(1.)ax.set_position([0.1,0.1,0.9,0.2]) |
25da964 tocdd2843CompareImportanceOfBeingErnest commentedJul 31, 2019
The order of setting position and box_aspect isn't relevant. But I added a test to make sure that's the case. |
3b19944 to0e2fdb9Compareandrzejnovak commentedAug 8, 2019
Just so it doesn't get lost from#15010 , I would really like if it was possible to choose this behaviour as a default setting in rcParams. |
ImportanceOfBeingErnest commentedAug 9, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I mentionned my problem with an rc parameter for this in#15001 (comment):
I could imagine though to let this be taken as argument by the axes' init, to allow for something like |
andrzejnovak commentedAug 9, 2019
I think that's a good compromise. It could be made to work by only applying it to the first axes. |
68af0c2 tof4cd76eCompare458ff92 tof40a5b7Comparetacaswell commentedAug 12, 2019
Power cycled to try to re-run against master and then belated realized this won't help with circle... Half of the warnings were from the required target not being hit, pushed a commit making sure they are generated (we do not auto-doc |
tacaswell commentedAug 12, 2019
ok, I understand the other half of the errors. we auto-generate and insert ..table:::class: property-table ======================================================================================= ===================================================================================================== Property Description ======================================================================================= =====================================================================================================:meth:`adjustable <matplotlib.axes._base._AxesBase.set_adjustable>` {'box', 'datalim'}:meth:`agg_filter <matplotlib.artist.Artist.set_agg_filter>` a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array:meth:`alpha <matplotlib.artist.Artist.set_alpha>` float or None:meth:`anchor <matplotlib.axes._base._AxesBase.set_anchor>` 2-tuple of floats or {'C', 'SW', 'S', 'SE', ...}:meth:`animated <matplotlib.artist.Artist.set_animated>` bool:meth:`aspect <matplotlib.axes._base._AxesBase.set_aspect>` {'auto', 'equal'} or num:meth:`autoscale_on <matplotlib.axes._base._AxesBase.set_autoscale_on>` bool:meth:`autoscalex_on <matplotlib.axes._base._AxesBase.set_autoscalex_on>` bool:meth:`autoscaley_on <matplotlib.axes._base._AxesBase.set_autoscaley_on>` bool:meth:`axes_locator <matplotlib.axes._base._AxesBase.set_axes_locator>` Callable[[Axes, Renderer], Bbox]:meth:`axisbelow <matplotlib.axes._base._AxesBase.set_axisbelow>` bool or 'line':meth:`box_aspect <matplotlib.axes._base._AxesBase.set_box_aspect>` None, or a number:meth:`clip_box <matplotlib.artist.Artist.set_clip_box>` `.Bbox`:meth:`clip_on <matplotlib.artist.Artist.set_clip_on>` bool:meth:`clip_path <matplotlib.artist.Artist.set_clip_path>` [(`~matplotlib.path.Path`, `.Transform`) | `.Patch` | None]:meth:`contains <matplotlib.artist.Artist.set_contains>` callable:meth:`facecolor <matplotlib.axes._base._AxesBase.set_facecolor>` color:meth:`fc <matplotlib.axes._base._AxesBase.set_facecolor>` color:meth:`figure <matplotlib.axes._base._AxesBase.set_figure>` `.Figure`:meth:`frame_on <matplotlib.axes._base._AxesBase.set_frame_on>` bool:meth:`gid <matplotlib.artist.Artist.set_gid>` str:meth:`in_layout <matplotlib.artist.Artist.set_in_layout>` bool:meth:`label <matplotlib.artist.Artist.set_label>` object:meth:`navigate <matplotlib.axes._base._AxesBase.set_navigate>` bool:meth:`navigate_mode <matplotlib.axes._base._AxesBase.set_navigate_mode>` unknown:meth:`path_effects <matplotlib.artist.Artist.set_path_effects>` `.AbstractPathEffect`:meth:`picker <matplotlib.artist.Artist.set_picker>` None or bool or float or callable:meth:`position <matplotlib.axes._base._AxesBase.set_position>` [left, bottom, width, height] or `~matplotlib.transforms.Bbox`:meth:`prop_cycle <matplotlib.axes._base._AxesBase.set_prop_cycle>` unknown:meth:`rasterization_zorder <matplotlib.axes._base._AxesBase.set_rasterization_zorder>` float or None:meth:`rasterized <matplotlib.artist.Artist.set_rasterized>` bool or None:meth:`sketch_params <matplotlib.artist.Artist.set_sketch_params>` (scale: float, length: float, randomness: float):meth:`snap <matplotlib.artist.Artist.set_snap>` bool or None:meth:`title <matplotlib.axes._axes.Axes.set_title>` str:meth:`transform <matplotlib.artist.Artist.set_transform>` `.Transform`:meth:`url <matplotlib.artist.Artist.set_url>` str:meth:`visible <matplotlib.artist.Artist.set_visible>` bool:meth:`xbound <matplotlib.axes._base._AxesBase.set_xbound>` unknown:meth:`xlabel <matplotlib.axes._axes.Axes.set_xlabel>` str:meth:`xlim <matplotlib.axes._base._AxesBase.set_xlim>` (left: float, right: float):meth:`xmargin <matplotlib.axes._base._AxesBase.set_xmargin>` float greater than -0.5:meth:`xscale <matplotlib.axes._base._AxesBase.set_xscale>` {"linear", "log", "symlog", "logit", ...}:meth:`xticklabels <matplotlib.axes._base._AxesBase.set_xticklabels>` List[str]:meth:`xticks <matplotlib.axes._base._AxesBase.set_xticks>` unknown:meth:`ybound <matplotlib.axes._base._AxesBase.set_ybound>` unknown:meth:`ylabel <matplotlib.axes._axes.Axes.set_ylabel>` str:meth:`ylim <matplotlib.axes._base._AxesBase.set_ylim>` (bottom: float, top: float):meth:`ymargin <matplotlib.axes._base._AxesBase.set_ymargin>` float greater than -0.5:meth:`yscale <matplotlib.axes._base._AxesBase.set_yscale>` {"linear", "log", "symlog", "logit", ...}:meth:`yticklabels <matplotlib.axes._base._AxesBase.set_yticklabels>` List[str]:meth:`yticks <matplotlib.axes._base._AxesBase.set_yticks>` unknown:meth:`zorder <matplotlib.artist.Artist.set_zorder>` float ======================================================================================= ===================================================================================================== into a bunch of docstrings which points to the base class (because it asks the methods what class they belong to). Will push a commit 'fixing' this soon... |
fb8ddd7 todb45c2fComparedb45c2f to20134e2Compareanntzer commentedSep 11, 2019
I think this is basically good to go. |
ImportanceOfBeingErnest commentedSep 11, 2019
I would agree that if we had In general we have the semantics of I therefore find |
jklymak commentedSep 11, 2019
OK, I'll check later, but what if you set two axes to have different aspect ratio, and use a layout manager? I'm just a little concerned there are all sorts of edge cases here that will cause problems, but I may be completely incorrect. |
jklymak left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Actually because this works on theactive position of the axes, it works fine w/ the layout managers. Or at least I couldn't easily break it.
jklymak commentedSep 11, 2019
I'll merge tomorrow, if no one else does, or@ImportanceOfBeingErnest you can self-merge if we give this a day or so to make sure there are no further objections. |
jklymak commentedOct 1, 2019
Thanks@ImportanceOfBeingErnest I think this will be a pretty valuable tool in the axes shaping toolbox! |
The way that bbox_inches='tight' is implemented we need to ensure thatwe do not try to adjust the aspect during the draw (because we havetemporarily de-coupled the reported figure size from the transformswhich results in the being distorted). Previously we did not have away to fix the aspect ratio in screen space of the Axes (only theaspect ratio in dataspace) however in 3.3 we gained this ability forboth Axes (matplotlib#14917) and Axes3D (matplotlib#8896 /matplotlib#16472).Rather than add an aspect value to `set_aspect` to handle this case,in the tight_bbox code we monkey-patch the `apply_aspect` method witha no-op function and then restore it when we are done. Previously wewould set the aspect to "auto" and restore it in the same places.closesmatplotlib#16463.
The way that bbox_inches='tight' is implemented we need to ensure thatwe do not try to adjust the aspect during the draw (because we havetemporarily de-coupled the reported figure size from the transformswhich results in the being distorted). Previously we did not have away to fix the aspect ratio in screen space of the Axes (only theaspect ratio in dataspace) however in 3.3 we gained this ability forboth Axes (matplotlib#14917) and Axes3D (matplotlib#8896 /matplotlib#16472).Rather than add an aspect value to `set_aspect` to handle this case,in the tight_bbox code we monkey-patch the `apply_aspect` method witha no-op function and then restore it when we are done. Previously wewould set the aspect to "auto" and restore it in the same places.closesmatplotlib#16463.
Uh oh!
There was an error while loading.Please reload this page.
PR Summary
Matplotlib axes'
aspectrefers to thedata, i.e. it defines the ratio of vertical vs. horizontal data units. However, it seems people often (mis)use it in order to set the aspect of the axes box, which is of course possible by knowing the limits of the data and usingadjustable="box". But it inevitably leads to problems, e.g.axes_grid1toolkit is able to solve natively.In short, sometimes people just want to make a square plot.
So it seems useful to introduce a
box_aspectparameter, which sets the aspect (i.e. the ratio between height and width) of theaxes box, independent of the data.Some usecases:
A. A square axes, independent of data
B. Shared square axes
C. Square twin axes
D. Normal plot next to image (works with constrained_layout)
E. Square joint/marginal plot
F. Equal data aspect, unequal box aspect
Issues:
As@jklymak alreadypointed out, there might be a problem with layout managers, and indeed at least caseE. from above fails with contrained_layout.(fixed byFIX constrained_layout w/ hidden axes #14919)Obviously a fixed box_aspect only makes sense with
adjustable="datalim". Since the order in whichbox_aspectandadjustableare set is arbitrary, this still needs to define in which cases to error out, or silently fall back.PR Checklist