Skip to content

Too much white space around graphs in 3d projection #19519

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

Open
ghost opened this issue Feb 15, 2021 · 19 comments
Open

Too much white space around graphs in 3d projection #19519

ghost opened this issue Feb 15, 2021 · 19 comments

Comments

@ghost
Copy link

ghost commented Feb 15, 2021

When using 3d projection:

fig = plt.figure(figsize=(20,15))
ax = fig.gca(projection='3d')

and save the figure into png file, there is way too much white space from left right top and bottom, so when I want to add this graph into latex and use it in publication, I have to do post-processing of the image and remove unnecessary amount of blank space.

@ianhi
Copy link
Contributor

ianhi commented Feb 16, 2021

Hi @Pepe78 that sounds annoying indeed. However, I think that this can likely be resolved with the existing tools in matplotlib. Can you please post about this (with a picture of the output and a description of what you'd expect) on https://discourse.matplotlib.org/ which is the best place for getting help with matplotlib.

@ianhi ianhi added Community support Users in need of help. topic: mplot3d labels Feb 16, 2021
@ghost
Copy link
Author

ghost commented Feb 16, 2021

Done (reported at https://discourse.matplotlib.org/). As far as "resolved with the existing tools in matplotlib", it would be nice to have by default less amount of blank space as it is done when not doing 3d projections (2d graphs). Thanks!

@jklymak
Copy link
Member

jklymak commented Feb 16, 2021

I'm not sure why there is such a large margin around axes3d plots. I have a sneaking suspicion that the margin was developed outside the subplot paradigm, which also have margins, and now we have two sets of margins. Definitely when you add an axes directly via Axes3d(fig) (which we want to deprecate), the margins are smaller.

@ghost
Copy link
Author

ghost commented Feb 16, 2021

This was great advice I got on discourse:

fig = plt.figure(figsize=(20,15), constrained_layout=True)
plt.savefig('1.png', bbox_inches='tight')

Left and right margins were minimal when saved like that (and top and bottom seem also smaller).

@ghost ghost closed this as completed Feb 16, 2021
@jklymak jklymak reopened this Feb 16, 2021
@jklymak
Copy link
Member

jklymak commented Feb 16, 2021

I'll actually re-open, because to me, at least, there is still a mystery about why the margins are so much larger than other axes.

@ghost
Copy link
Author

ghost commented Feb 16, 2021

That makes sense - as I said before, it would be nice to have less margins on sides by default. When doing 2d plots, this actually really works nicely by default.

@WeatherGod
Copy link
Member

Yes, the original Axes3D didn't work within the subplot() framework. And subplots leave extra room for the static axes labels, meanwhile, Axes3D needed to account for extra space for its axes labels within the axes plotting area. Originally, this was fine because you didn't make Axes3D objects using subplots, so you didn't get allocated extra margins for axes labels. When it became possible to specify projection='3d' to a subplot call, this had the inadvertent effect of getting extra margin space that wasn't really needed.

I never did figure out a good solution for this that wouldn't act counter-intuitively. Perhaps Axes3D needs to be reworked such that it can interpret and express the margin parameters itself rather than letting it be handled by 2D-based assumptions?

@srkunze
Copy link

srkunze commented Sep 24, 2021

I stumbled over this issue as well while trying to plot a representation of the Riemann zeta function.

Zooming in just cuts off left and right of the plot. :-/

image

@astromancer
Copy link
Contributor

Just a note that you can control the margin sizes to some extent using gridspec_kws.

fig, ax = plt.subplots(subplot_kw={'projection': '3d'},
                       gridspec_kw=dict(top=1.07, bottom=0.02, left=0, right=1))
fig.savefig('axes3d.png', dpi=600, bbox_inches='tight')

axes3d

@cgadal
Copy link
Contributor

cgadal commented Dec 2, 2024

Is there a current workaround for this ? I am having some problems with a figure similar to this situation:

import matplotlib.pyplot as plt
import numpy as np

X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)


fig, ax = plt.subplots(1, 1, figsize=(10, 5), subplot_kw=dict(projection="3d"))
plt.subplots_adjust(0, 0, 1, 1, 0, 0)


surf = ax.plot_surface(X, Y, Z, linewidth=0, antialiased=False)

ax.view_init(elev=20, azim=-45, roll=0)

ax.set_box_aspect([5, 1, 1])  # Aspect ratio
ax.set_aspect("auto")

plt.show()

Not that I do not want to use the bbox_inches='tight' when saving the plot, as the figure is already prepared at the right size to be inserted in a paper.

Fig

@jklymak
Copy link
Member

jklymak commented Dec 3, 2024

Yes, the workaround is stated above - add Axes3D manually.

@cgadal
Copy link
Contributor

cgadal commented Dec 3, 2024

Could you give a brief example of this? Simply doing

fig = plt.figure(figsize=(10, 5))
ax = Axes3D(fig)

does not show any ax on the figure.

@jklymak
Copy link
Member

jklymak commented Dec 3, 2024

OK, my mistake. I guess I don't know how you can fix this. Maybe @scottshambaugh has some insight. The overall issue is that you set the box_aspect. I guess the algorithm is conservatice about guessing which direction to shrink based on the current view.

mahan-hosseini added a commit to mahan-hosseini/AutoGaitA that referenced this issue Feb 5, 2025
…at was going on here

I tried to figure this out and was kinda getting there but then I realised that because of the 3d axes being generally different than 2d axes and apparently them interacting strangely with subplots() there was a LOT of white space vertically in my subplot grid which is apparently not trivial to get rid of. see here for a discussion:
    matplotlib/matplotlib#19519
@AndrejHafner
Copy link

Is there any progress regarding topic? I'm having the same problem trying to fit the axis to the full width of the window - left and right sides just get cut off. I've tried many things, like the tight layout, adjusting the margins and other things, but none of them worked.

Image

@cgadal
Copy link
Contributor

cgadal commented Mar 27, 2025

@AndrejHafner

My current workaround is to play with the ax position ax.set_position([-0.04, -0.32, 1.05, 1.1]) inside the figure.

If for some reason you are using this in a figure, this ax will overlap the other ones, so you might need to hide the background: ax.set_facecolor("none")

if in this figure you are using layout='constrained', you can do:

fig.canvas.draw()
fig.set_layout_engine("none")
axarr[1].set_position([-0.04, -0.32, 1.05, 1.1])
axarr[1].set_facecolor("none")

to fix axis positions from constrained layout, and then disable it to only change the ax position of you 3d figure.

@AndrejHafner
Copy link

AndrejHafner commented Mar 27, 2025

@AndrejHafner

My current workaround is to play with the ax position ax.set_position([-0.04, -0.32, 1.05, 1.1]) inside the figure.

If for some reason you are using this in a figure, this ax will overlap the other ones, so you might need to hide the background: ax.set_facecolor("none")

if in this figure you are using layout='constrained', you can do:

fig.canvas.draw()
fig.set_layout_engine("none")
axarr[1].set_position([-0.04, -0.32, 1.05, 1.1])
axarr[1].set_facecolor("none")

to fix axis positions from constrained layout, and then disable it to only change the ax position of you 3d figure.

@cgadal is the axarr in your code the Axes3D? I'm not sure which axis you are actually setting the position for. I tried this approach, but I couldn't get the plot to become wider.

@cgadal
Copy link
Contributor

cgadal commented Mar 31, 2025

@AndrejHafner

Yes, it is the Axes3D.

import matplotlib.pyplot as plt
import numpy as np

X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

fig = plt.figure(figsize=(10, 5), layout="constrained")
ax = fig.add_subplot(111, projection="3d")

surf = ax.plot_surface(X, Y, Z, linewidth=0, antialiased=False)

ax.view_init(elev=20, azim=-45, roll=0)
ax.set_box_aspect([5, 1, 1])  # Aspect ratio


# fig.canvas.draw()
# fig.set_layout_engine("none")
# ax.set_position([-0.75, -0.75, 2.5, 2.5])
# ax.set_facecolor("none")

plt.savefig("Fig_2.png")
plt.show()

gives:

Image

Uncommenting the commented lines gives:

Image

The two first numbers in ax.set_position will control the position, and the two last ones the scaling factor.

@AndrejHafner
Copy link

@cgadal

Thank you for the detailed response, it works for my use case as well. I was hoping that I would be able to only change the width and keep the height as it is, but I guess it kind of works as a zoom workaround on the figure level.

@scottshambaugh
Copy link
Contributor

scottshambaugh commented Apr 2, 2025

There are two issues being talked about here:

  1. The default 3D margins are really large (still an open question, and should be the focus of this issue).
  2. 3D data is being clipped too aggressively. I have a separate issue for that, and discussion on that should move over here: [Bug]: 3D artist clipbox cuts off data sooner than axis panes/spines/etc #23450.

I took a stab at fixing (2) today but my approach wasn't the right one - I don't think that messing with the box_aspect calcs makes sense with the way 3D axes are set up. I believe the fix will be to expand the clip boxes for each of the 3D artists to the whole figure, since there's no real reason to truncate them. That's available for anyone to pick up and work on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants