Repository navigation
Description
I take an example from
https://stackoverflow.com/questions/23951230/python-axis-limit-in-matplotlib
In mplot3d, when you set, for example, set_xlim(0, 0.8), the outcome has an extra like set_xlim(0 - eps, 0.8 + eps), where eps is related to deltas.
matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Line 187 in 6e76411
| deltas = (maxs - mins) / 12. |
To show the exact lims, you have to change deltas to, for example, 0 * (maxs - mins) / 12.
You need something to set exact lims without touching the inside of matplotlib.
Code for reproduction
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_xlim(0,0.8)
plt.show()Matplotlib version
- Operating system:macOS 10.14.6
- Matplotlib version: 3.1.1
- Python version: 3.7.4
The problem was discussed many times in STACK OVERFLOW:
https://stackoverflow.com/questions/23951230/python-axis-limit-in-matplotlib
https://stackoverflow.com/questions/46380464/matplotlib-3d-axis-bounds-always-too-large-doesnt-set-lims-correctly
https://stackoverflow.com/questions/16488182/removing-axes-margins-in-3d-plot

Activity
timhoffm commentedon Jul 25, 2020
The relevant code is
matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Line 188 in 7e95303
which auto-expands the bounds. We actually want auto-expansion for automatically determined bounds, but not for user bounds.
👉 Things to do:
set_xlim. Note that the xaxis line is blurry.neil-ptr commentedon Aug 3, 2020
I'd be interested in looking into this
tacaswell commentedon Aug 3, 2020
Great @Neilzon ! Please let us know if you have any questions (either here or on https://gitter.im/matplotlib/matplotlib )
kentcr commentedon Mar 3, 2021
A default plot immediately sets both
_stale_viewlim_*variables to True:matplotlib/lib/matplotlib/axes/_axes.py
Line 1608 in bc97294
matplotlib/lib/matplotlib/axes/_base.py
Lines 787 to 793 in bc97294
A draw calls _unstale_viewLim():
matplotlib/lib/matplotlib/axes/_base.py
Line 2859 in bc97294
The
_stale_viewlim_*values cause it to get autoscaled:matplotlib/lib/matplotlib/axes/_base.py
Lines 766 to 778 in bc97294
This inner function is called for each, which does the (much more detailed) 2D bound calculations:
matplotlib/lib/matplotlib/axes/_base.py
Lines 2696 to 2762 in bc97294
If you call, e.g.,
set_xlimthe_stale_viewlim_xgets set to False which prevents the above:matplotlib/lib/matplotlib/axes/_base.py
Lines 3565 to 3567 in bc97294
Meanwhile,
axes3d.pyhas an identical-but-for-z_unstale_viewLimfunction, but itsautoscale_viewis wildly different. Normally I'd think it'd be desirable to make the code as dimension-agnostic as possible and share code between them, but I haven't noticed a lot of precedence for that. Is it avoided for some reason?m4reko commentedon Mar 7, 2021
We are a group of students that have looked into this for a while as part of a course.
Thank you @kentcr for some excellent documentation of the 2D-case. It helped us a lot.
Actually, it seems that this is already done in the
autoscale_viewand the only step needed to get the desired behaviour is to remove these lines:matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Lines 188 to 189 in 7e95303
The
autoscale_viewadds margins (_xmargin,_ymargin,_zmargin) with to the same logic as in the 2D case. It's a bit of a mystery to us as to why thesedeltaswhere added in the beginning. We ran the whole test suite with those lines removed and looked through all the generated images inresult_imagesand it seems to behave properly apart from this:This problem is not correlating with setting the
xlimmanually but it happens every time a gridline is aligned with the xaxis (only the xaxis however, not the other ones). We tried to solve this problem both by experimenting with thezorderand the order in which the different components of the axis (gridlines, ticks, spines) are drawn but could not find a solution. Does anybody have an idea of what is happening here?kentcr commentedon Mar 8, 2021
It's a y-tick being drawn. If you clear the y-ticks, it disappears:
Further, if you remove the first tick in each axis here...
matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Line 221 in 7e95303
e.g.
...it fixes this problem, but you lose the gridline where the background planes intersect:

I'd initially hoped to just change
_update_ticks()for the y case, but it's actually inherited from the 2D class:matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Line 35 in 7e95303
matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py
Lines 8 to 9 in 7e95303
matplotlib/lib/matplotlib/axis.py
Line 1972 in 7e95303
matplotlib/lib/matplotlib/axis.py
Line 1020 in 7e95303
It does seem possible to just change the ticks just for the y-axis, but the obvious ways don't seem very elegant to me. Of course, it's also rather fishy that the 3D YAxis and ZAxis classes are inheriting from the 2D XAxis class, so maybe elegance is a lost cause without more significant changes.
It may also be that the ticks are actually fine and it's some lower-level rendering issue, but I haven't dug that far yet.
Edit: I should say it's actually clear that the
ticksare fine, but the gridlines (which are generated from the ticks and put in thexyz0variable) may also be fine. I haven't dug into whether or not they are drawn when overlapping with the axis on purpose.