3D plot with Matplotlib
I agree with Ajean. I believe the problem arises because each matplotlib's artist (i.e. PolygonCollection
) is rendered separately. There is no way different faces from the same object to be rendered on different sides of another object in the scene.
Here is a useful piece of code :
from mpl_toolkits.mplot3d import axes3dimport matplotlib.pyplot as pltfrom matplotlib import cmimport numpy as npfile_path = "./3D_surface_and_contour.jpg"p = 0.05f = -0.01def get_data(p): x, y, z = axes3d.get_test_data(p) z = f * z return x, y, zdef plot_3d_contour(p, f): nrows = 4 ncols = 5 x, y, z = get_data(p) x_min, x_max = np.min(x), np.max(x) y_min, y_max = np.min(y), np.max(y) z_min, z_max = np.min(z), np.max(z) fig = plt.figure(figsize=(15, 10)) for n in range(nrows * ncols): i = n % ncols j = n / ncols k = n + 1 if j == 0: azim = -60 + (i - 2) * 15 elev = 30 elif j == 1: azim = -60 elev = 30 + (i - 2) * 5 elif j == 2: azim = 60 + (i - 2) * 10 elev = 30 elif j == 3: azim = 60 elev = 30 + (i - 2) * 5 ax = fig.add_subplot(nrows, ncols, k, projection='3d') ax.set_title("azim=" + str(azim) + " elev=" + str(elev)) ax.tick_params(labelsize=8) ax.view_init(azim=azim, elev=elev) ax.plot_surface(x, y, z, rstride=10, cstride=10, alpha=0.3) ax.contourf(x, y, z, zdir='z', offset=z_min, cmap=cm.coolwarm) ax.contourf(x, y, z, zdir='x', offset=x_min, cmap=cm.coolwarm) if j == 0 or j == 1: ax.contourf(x, y, z, zdir='y', offset=y_max, cmap=cm.coolwarm) elif j == 2 or j == 3: ax.contourf(x, y, z, zdir='y', offset=y_min, cmap=cm.coolwarm) ax.set_xlabel('X') ax.set_xlim(x_min, x_max) ax.set_ylabel('Y') ax.set_ylim(y_min, y_max) ax.set_zlabel('Z') ax.set_zlim(z_min, z_max) plt.savefig(file_path, dpi=80) plt.close()plot_3d_contour(p, f)
which gives the following image :
The first two rows are produced by a code similar to yours. You might notice that setting the elevation with view_init
to a higher value solve the problem. But it is not satisfactory. I have also determined the influence of the range of the z-values (not shown here), the bug seems to appear only when this range is small (you can use the f
parameter to test it) which explain why the example does not suffer from it.
The solution I propose is to replace :
ax.contourf(x, y, scalar_field, zdir='y', offset=y_dim/2+1, cmap=cm.coolwarm)
by :
ax.contourf(x, y, scalar_field, zdir='y', offset=-y_dim/2-1, cmap=cm.coolwarm)
in your code and add this additional line :
ax.view_init(azim=60, elev=30)
As shown in the last two rows of the previous image, this way you will be able to avoid the whims of matplotlib.