6

I am plotting a surface with bunch of polygons. The plotting is quite simple as shown below.

def plotSurface(cell, numOfLayer, name=None, alpha = 0.5):
    #import the libraries
    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib as mpl
    from mpl_toolkits.mplot3d.art3d import Poly3DCollection
    import numpy as np
    import matplotlib.pyplot as plt
    #limits of the plot
    radius = (numOfLayer>1)*(np.sqrt(3.)*(numOfLayer-1)-Length)+Length#the radius of circle to be projected on
    #plotting part
    fig = plt.figure(frameon=False,figsize=(12,10))
    ax = Axes3D(fig)
    ax.set_xlim((-2*radius,2*radius))
    ax.set_ylim((-2*radius,2*radius))
    ax.set_zlim((-0.5*radius,2*radius))
    ax.axis('off')
    #fig = plt.figure()
    #ax = fig.gca(projection='3d')
    ##iterating through the cell##
    for stuff happening here : verts are the polygon vertices
          #adding to 3d plot
          ax.add_collection3d(Poly3DCollection(verts,alpha = alpha))
    if name == None:#plot the figure
        plt.show()
    else:
        plt.savefig(name,bbox_inches='tight')
    return

The image i get is like below. Big white space with tiny figure. I want the figure to cover most of the space. How can I achieve that ?

enter image description here

jkhadka
  • 2,443
  • 8
  • 34
  • 56

2 Answers2

10

Several ways you can modify the whitespace:

  1. Reduce the whitespace inside the axes. To do this, you could modify the x, y and z limits using:

    ax.set_xlim()
    ax.set_ylim()
    ax.set_zlim()
    
  2. Reduce the whitespace outside the axes. To do this, you can use:

    fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
    
  3. Finally, you could just save a portion of the figure when you call savefig. You can modify this area using the bbox_inches kwarg, by using an actual Bbox rather than setting it to tight.

For example, lets consider this image from the matplotlib gallery. Note that I've change the axis and figure background colours, so they show up clearly on the page below.

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(10,8))
# I added a pink axis background, just so its easy to see against the white page
ax = fig.add_subplot(111, projection='3d', facecolor='#FFAAAA')

u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)

x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')

ax.axis('off')

# Save the original figure (using a grey background for the figure for clarity)
plt.savefig('3d_whitespace0.png', facecolor='#AAAAAA')

enter image description here

# Step 1 above: change the axes limits
ax.set_xlim(-8, 8)
ax.set_ylim(-8, 8)
ax.set_zlim(-8, 8)

plt.savefig('3d_whitespace1.png', facecolor='#AAAAAA')

enter image description here

# Step 2 above: change the subplot margins
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)

plt.savefig('3d_whitespace2.png', facecolor='#AAAAAA')

enter image description here

# Step 3 above: save only a portion of the figure. Here we will cut one inch
# off each side of the figure, to change the 10in x 8in figure to 8in x 6in
bbox = fig.bbox_inches.from_bounds(1, 1, 8, 6)

plt.savefig('3d_whitespace3.png', bbox_inches=bbox, facecolor='#AAAAAA')

enter image description here

tmdavison
  • 64,360
  • 12
  • 187
  • 165
3

The figure margins can be reduced by setting fig.subplots_adjust(top=1, bottom=0, left=0, right=1). This might or might not be sufficient, depending on the actual figure.

Also mind that if the axes aspect is set to equal, the figsize must be squared. (This is not the case here, but might be needed for other cases).

The last knob to turn would be to reduce the axis limits. Those can be set to smaller values to reduce the white space around the object. For example, plotting a sphere with radius 1 around the origin, one would be tempted to set the limits to [-1,1] to fit the whole sphere into the plot. However, this will leave a lot of white space around. Reducing the limits to [-0.57,0.57] will let the sphere fit nicely into the figure. To see this effect, I left the axes turned on in the example below.

import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

u = np.linspace(0, 2 * np.pi, 12)
v = np.linspace(0, np.pi,15)
x =  np.outer(np.cos(u), np.sin(v))
y =  np.outer(np.sin(u), np.sin(v))
z =  np.outer(np.ones(np.size(u)), np.cos(v))
F = np.sin(x)*y + z
F = (F-F.min())/(F-F.min()).max()

#Set colours and render
fig = plt.figure(figsize=(8,4))
fig.subplots_adjust(top=1, bottom=0, left=0, right=1, wspace=0)
ax = fig.add_subplot(121, projection='3d')
ax2 = fig.add_subplot(122, projection='3d')

# plotting a sphere with radius 1. 
# Naturally, setting the limits to 1 makes sense
ax.plot_surface(x,y,z,  rstride=1, cstride=1, facecolors=cm.jet(F), alpha=0.5)
ax.set_xlim(np.array([-1,1]))
ax.set_ylim(np.array([-1,1]))
ax.set_zlim(np.array([-1,1]))

# plotting a sphere with radius 1. 
# but now reducing the limits
ax2.plot_surface(x,y,z,  rstride=1, cstride=1, facecolors=cm.jet(1-F), alpha=0.5) 
ax2.set_xlim(np.array([-1,1])*.57)
ax2.set_ylim(np.array([-1,1])*.57)
ax2.set_zlim(np.array([-1,1])*.57)

#ax.axis('off') # turned on to see the effect. Turn off to have a nice image.
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712