1

This seems to be a very basic question, but after reading the help function and searching on the internet, I still cannot find a solution. Excuse me if I missed something obvious here.

Consider following MWE that is meant to plot a 3D figure in polar coordinates, using a colormap and without lines :

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

r=np.linspace(0,1,100)
theta=np.linspace(0,2*np.pi,10000)
R,Theta=np.meshgrid(r,theta)
X,Y=R*np.cos(Theta),R*np.sin(Theta)
Z=R*np.sin(Theta)*np.cos(Theta)

fig=plt.figure(1)
ax=fig.add_subplot(projection='3d')
ax.plot_surface(X,Y,Z,cmap=cm.inferno,linewidth=0)
plt.show()

As you can see in the produced figure, despite asking linewidth=0 and exaggerating the size of the theta vector, lines are visible on the surface, and the color resolution is bad :

enter image description here

How can I get rid of the white lines, and obtain a smooth surface with a continuously changing color?

Karlo
  • 1,630
  • 4
  • 33
  • 57
  • 1
    I hope you resolved your problem a long time ago, but anyway: Nowhere in the documentation is it described, but you can add the parameter `antialiased=False` to the function `plot_surface()` to remove the "white" lines. However, this does not lead to a smooth colored surface, but rather a very visible triangle soup – Marco Feb 21 '23 at 13:28

2 Answers2

4

According to this blog post by Adam Murphy, surface plots in matplotlib are generated by filling in a wireframe plot, so it is difficult to get rid of the line artifacts completely even if you set linewidth=0.

However, looking through matplotlib's documentation on surface plots, there are two parameters rstride and cstride that downsample the rows and columns of your X, Y, and Z arrays - the defaults for both of these parameters are 10 so only every 10th row and 10th column are being plotted. Therefore, if we lower rstride and cstride to 5, then your surface plot should have a higher resolution, although it will be slower to render.

To increase the rendering speed, the documentation suggests setting rstride and cstride to be multiples of the number of rows - 1 and the number of columns - 1 for your 2D arrays. Since X, Y, Z all have dimensions (10000, 100) in your original code, I changed the dimensions of these arrays to be (10001, 101) so that 5 evenly divides 101-1 and 10001-1

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

## to maximize rendering speed, we want the parameters rstride and cstride 
## to be multiples of the number of rows-1 and columns-1
r=np.linspace(0,1,101)
theta=np.linspace(0,2*np.pi,10001)
R,Theta=np.meshgrid(r,theta)
X,Y=R*np.cos(Theta),R*np.sin(Theta)
Z=R*np.sin(Theta)*np.cos(Theta)

fig=plt.figure(1)
ax=fig.add_subplot(projection='3d')
ax.plot_surface(X,Y,Z,rstride=5,cstride=5,cmap=cm.inferno,linewidth=0)
plt.show()

enter image description here

Derek O
  • 16,770
  • 4
  • 24
  • 43
3

I faced the same issue, and based on this answered question I got inspired to also color the wireframe. I tried implementing @Derek_O 's answer but takes a lot of time to render. Maybe coloring the wireframe also could solve the issue instead of linewidth=0.

I tried this:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

r = np.linspace(0, 1, 100)
theta = np.linspace(0, 2 * np.pi, 10000)
R, Theta = np.meshgrid(r, theta)
X, Y = R * np.cos(Theta), R * np.sin(Theta)
Z = R * np.sin(Theta) * np.cos(Theta)

norm = plt.Normalize(np.nanmin(Z), np.nanmax(Z))
colors = cm.inferno(norm(Z))

fig = plt.figure(1)
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap=cm.inferno, facecolors=colors)
plt.show()

Colored wireframe

Robert Chang
  • 87
  • 1
  • 1
  • 6