Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not add padding to 3D axis limits when limits are manually set #25272

Merged
merged 1 commit into from
Sep 18, 2023

Conversation

scottshambaugh
Copy link
Contributor

@scottshambaugh scottshambaugh commented Feb 21, 2023

PR Summary

Closes #18052

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, subplot_kw={'projection': '3d'})
ax.set_xlim(0, 0.8)
ax.set_ylim(0, 0.8)
ax.set(xlabel='x', ylabel='y', zlabel='z')
plt.show()

Before:
Figure_2

After (exact limits for x and y, z retains default padded limits):
Figure_1

There are a ton of test image changes, gonna be a hefty commit.

PR Checklist

Documentation and Tests

  • Has pytest style unit tests (and pytest passes)
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • [N/A] New plotting related features are documented with examples.

Release Notes

  • [N/A] New features are marked with a .. versionadded:: directive in the docstring and documented in doc/users/next_whats_new/
  • [N/A] API changes are marked with a .. versionchanged:: directive in the docstring and documented in doc/api/next_api_changes/
  • [N/A] Release notes conform with instructions in next_whats_new/README.rst or next_api_changes/README.rst

@scottshambaugh scottshambaugh marked this pull request as draft February 21, 2023 07:15
@oscargus
Copy link
Contributor

Although I think that this is really how it should be, the question is if we can just enforce it or if there should be a deprecation path? Better ping in @timhoffm directly...

@scottshambaugh
Copy link
Contributor Author

This is changing intended behavior in some of the tests so some tweaking to do, but should be enough here right now to get an idea.

@scottshambaugh
Copy link
Contributor Author

Talked about this in the dev call today, consensus was to make the new behavior default without deprecation, but add an rc_param to switch back to current behavior.

@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Apr 30, 2023

This is ready for review!

Note that there are a large number (57) of baseline image edits. In all cases, these are very minor - either a 1 pixel shift in some labels, or a subpixel shift in tick locations. I believe these are likely due to accumulated floating point errors as a result of several scale conversion factors that I had to add to match the baseline formatting.

Take for example axes3d_labelpad-failed-diff.png:
axes3d_labelpad-failed-diff

Which when thresholded to maximum contrast in photoshop shows some slight subpixel shifts for the tick marks:
axes3d_labelpad-failed-diff

@scottshambaugh scottshambaugh marked this pull request as ready for review April 30, 2023 03:27
@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Apr 30, 2023

Well, it looks like the floating point theory is likely correct, because the tests are all passing on my machine, and the different CI platforms are having different tests throw image comparison errors. What's the proper way to handle this? Loosen the image comparison tolerance? I'm not sure how to examine the test images from the different CI environments if I can't recreate the issue locally.

@oscargus
Copy link
Contributor

The ideal way is to add platform-specific margins, like:

@image_comparison(
['line_collection_dashes'], remove_text=True, style='mpl20',
tol=0.65 if platform.machine() in ('aarch64', 'ppc64le', 's390x') else 0)

I think one way to make it at least slightly better is to pre-compute the constants. In this way one error source is removed and the code will execute slightly faster. Always possible to use a comment to illustrate where it comes from.

Copy link
Contributor

@oscargus oscargus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the constants should be pre-computed I think.

Doc-strings should typically have a single-sentence/line description (hence, the need for an empty line after).

A change note is required for the new rcParam

I think the purpose of the PR is for sure worthwhile, but I do not know enough to have any real comments on the implentation.

lib/matplotlib/axis.py Outdated Show resolved Hide resolved
lib/matplotlib/testing/decorators.py Outdated Show resolved Hide resolved
lib/matplotlib/ticker.py Outdated Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axes3d.py Outdated Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axes3d.py Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axis3d.py Outdated Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axes3d.py Outdated Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axes3d.py Outdated Show resolved Hide resolved
lib/matplotlib/axis.py Show resolved Hide resolved
lib/mpl_toolkits/mplot3d/axes3d.py Outdated Show resolved Hide resolved
@scottshambaugh
Copy link
Contributor Author

scottshambaugh commented Apr 30, 2023

Thank you for the thorough review @oscargus!

To give some context for where these constants are coming from, the original behavior when generating the view limits for 3d plots was fairly broken. There was always 1/48 of the axis range added to either side of the axis bounds when drawing the axes, however this extra "view margin" was purely visual. The calculations for the zoom level for the plot, which grid/tick lines to show, and several other spots in the code all used the non-padded axis limits for their calculation. So, when I converted the backed to use the actual axis limits everywhere, this required putting in a lot of scale factors to keep the visual appearance (23/24 = 1-2*1/48, and 25/24 = 1+2*1/48).

@scottshambaugh scottshambaugh added this to the v3.8.0 milestone Jul 11, 2023
@tacaswell
Copy link
Member

@scottshambaugh this needs a rebase because we merged some other PRs

@tacaswell
Copy link
Member

Talking about this with @ksunden and @QuLogic we want to take this, but given this is an API change and it is the 11th hour of the 3.8 prep, we are going to hold this for 3.9 (which means it should get merged as soon we branch).

@tacaswell tacaswell modified the milestones: v3.8.0, v3.9.0 Aug 2, 2023
@QuLogic
Copy link
Member

QuLogic commented Aug 2, 2023

Essentially - the old padding was 1/48, and was applied 2x (to the min and max limits), so that's where the 1 ± 1/24 scaling came from.

OK, can we mention that in the comments? It currently says something like "to match mpl3.7 appearance", so it can say "to match mpl3.7 appearance that added 1/48 padding". Also feel free to put a longer explanation in the commit message.

@scottshambaugh
Copy link
Contributor Author

Thanks! Rebased/squashed, and added a lot more detail to the comments on scale factors. @QuLogic do those address your concerns?

Fix remaining large image diffs

Fix remaining large image diffs

Fix the rest of the tests

Fix docs error

Code review updates

Code review updates

Fix docs error

One more constant

Tweaks

Keep fixing CI errors

Docstrings

Update baseline images

More test images

Images

Test new scaling factor

Test new scaling factor

Test for exact limits

Cleanup

Tests

Update doc/users/next_whats_new/3d_axis_limits.rst

Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>

rebase fixes

Apply suggestions from code review

Co-authored-by: Oscar Gustafsson <oscar.gustafsson@gmail.com>

Code review cleanup

Merge conflicts and better comments

Merge conflicts and better comments
@scottshambaugh
Copy link
Contributor Author

Bump on merging this now that 3.8.0 is out

@timhoffm timhoffm merged commit 8f3e9e6 into matplotlib:main Sep 18, 2023
40 checks passed
@timhoffm
Copy link
Member

@scottshambaugh Thanks!

@ksunden
Copy link
Member

ksunden commented Sep 18, 2023

Unclear to me why this didn't happen for this PR, but references to [xy]axis_inverted and invert_[xy]axis in the docstrings from this PR are causing doc build failures on main now:

https://app.circleci.com/pipelines/github/matplotlib/matplotlib/26910/workflows/8053f52d-d0f9-45f0-8b9e-e759ea3c4215/jobs/78885

/home/circleci/project/lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.get_xbound:20: WARNING: py:obj reference target not found: invert_xaxis
/home/circleci/project/lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.get_xbound:20: WARNING: py:obj reference target not found: xaxis_inverted
/home/circleci/project/lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.get_ybound:20: WARNING: py:obj reference target not found: invert_yaxis
/home/circleci/project/lib/matplotlib/axes/_base.py:docstring of matplotlib.axes._base._AxesBase.get_ybound:20: WARNING: py:obj reference target not found: yaxis_inverted
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_xbound:31: WARNING: py:obj reference target not found: invert_xaxis
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_xbound:31: WARNING: py:obj reference target not found: xaxis_inverted
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_xlim:53: WARNING: py:obj reference target not found: invert_xaxis
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_xlim:53: WARNING: py:obj reference target not found: xaxis_inverted
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_ybound:31: WARNING: py:obj reference target not found: invert_yaxis
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_ybound:31: WARNING: py:obj reference target not found: yaxis_inverted
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_ylim:53: WARNING: py:obj reference target not found: invert_yaxis
/home/circleci/project/lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D.set_ylim:53: WARNING: py:obj reference target not found: yaxis_inverted

It looks like the warning is that it can't find them at all, rather than that it e.g. sees both Axes.invert_yaxis and Axes3D.invert_yaxis, which was my first thought.

Sphinx version is different (7.2.4 here, 7.2.6 on main), though only as patch releases, and nothing obviously related in release notes.

mpl-sphinx-theme version is different, though I can't think of a way that should affect this linking behavior.

timhoffm added a commit to timhoffm/matplotlib that referenced this pull request Sep 18, 2023
Attempt to fix doc build failure introduced with matplotlib#25272.

I suspect this is sphinx scoping: matplotlib#25272 reused methods
like `Axes.set_xbounds` to generate documentation for Axes3D. However,
a see also of invert_xaxis only searches within the same module (or
class), and invert_xaxis is not documented for Axes3D. This PR broadens
the search scope by using the leading dot. An alternative may be to
attempt to explicitly document the missing methods for Axes3D.
timhoffm added a commit to timhoffm/matplotlib that referenced this pull request Sep 18, 2023
Attempt to fix doc build failure introduced with matplotlib#25272.

I suspect this is sphinx scoping: matplotlib#25272 reused methods
like `Axes.set_xbounds` to generate documentation for Axes3D. However,
a see also of invert_xaxis only searches within the same module (or
class), and invert_xaxis is not documented for Axes3D. This PR
attempt to explicitly document the missing methods for Axes3D.
This was referenced Sep 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

the limits of axes are inexact with mplot3d
6 participants