That is why you have to work with the 3D objects from
mpl_toolkits.mplot3d.art3d directly. There isn't a lot of documentation, so
your best bet is to look at the source code of the module.
Indeed, I was able to extract the necessary pieces from the source
code. In fact I split the function `plot_surface` into two functions
`polyMeshFromMeshGrid` and `plot_surfaceMesh`. The first functions
takes the meshgrid arguments and produces a list of polygons that
should be drawn, the second functions finally displays them (the code
in plot_surface is already structured like this). With this approach I
can plot functions which are made up of several parts which each have
meshgrid structure. Also I can add raw polygons to the mesh.
Maybe this splitting makes sense in the online code as well?
-Holger
def polyMeshFromMeshGrid(self, X, Y, Z, *args, **kwargs):
rows, cols = Z.shape
tX, tY, tZ = np.transpose(X), np.transpose(Y), np.transpose(Z)
rstride = kwargs.pop('rstride', 10)
cstride = kwargs.pop('cstride', 10)
if 'facecolors' in kwargs:
fcolors = kwargs.pop('facecolors')
else:
color = np.array(colorConverter.to_rgba(kwargs.pop('color', 'b')))
fcolors = None
cmap = kwargs.get('cmap', None)
norm = kwargs.pop('norm', None)
vmin = kwargs.pop('vmin', None)
vmax = kwargs.pop('vmax', None)
shade = kwargs.pop('shade', cmap is None)
lightsource = kwargs.pop('lightsource', None)
# Shade the data
if shade and cmap is not None and fcolors is not None:
fcolors = self._shade_colors_lightsource(Z, cmap, lightsource)
polys = kwargs.pop('polys', )
normals = kwargs.pop('normals', )
#colset contains the data for coloring: either average z or the facecolor
colset = kwargs.pop('colset', )
for rs in np.arange(0, rows-1, rstride):
for cs in np.arange(0, cols-1, cstride):
ps =
corners =
for a, ta in [(X, tX), (Y, tY), (Z, tZ)]:
ztop = a[rs][cs:min(cols, cs+cstride+1)]
zleft = ta[min(cols-1, cs+cstride)][rs:min(rows, rs+rstride+1)]
zbase = a[min(rows-1, rs+rstride)][cs:min(cols, cs+cstride+1):]
zbase = zbase[::-1]
zright = ta[cs][rs:min(rows, rs+rstride+1):]
zright = zright[::-1]
corners.append([ztop[0], ztop[-1], zbase[0], zbase[-1]])
z = np.concatenate((ztop, zleft, zbase, zright))
ps.append(z)
# The construction leaves the array with duplicate points, which
# are removed here.
ps = zip(*ps)
lastp = np.array()
ps2 =
avgzsum = 0.0
for p in ps:
if p != lastp:
ps2.append(p)
lastp = p
avgzsum += p[2]
polys.append(ps2)
if fcolors is not None:
colset.append(fcolors[rs][cs])
else:
colset.append(avgzsum / len(ps2))
# Only need vectors to shade if no cmap
if cmap is None and shade:
v1 = np.array(ps2[0]) - np.array(ps2[1])
v2 = np.array(ps2[2]) - np.array(ps2[0])
normals.append(np.cross(v1, v2))
return polys, normals, colset
def plot_surfaceMesh(self, polys, normals, colset, *args, **kwargs):
had_data = self.has_data()
rows, cols = Z.shape
rstride = kwargs.pop('rstride', 10)
cstride = kwargs.pop('cstride', 10)
if 'facecolors' in kwargs:
fcolors = kwargs.pop('facecolors')
else:
color = np.array(colorConverter.to_rgba(kwargs.pop('color', 'b')))
fcolors = None
cmap = kwargs.get('cmap', None)
norm = kwargs.pop('norm', None)
vmin = kwargs.pop('vmin', None)
vmax = kwargs.pop('vmax', None)
linewidth = kwargs.get('linewidth', None)
shade = kwargs.pop('shade', cmap is None)
lightsource = kwargs.pop('lightsource', None)
polyc = art3d.Poly3DCollection(polys, *args, **kwargs)
if fcolors is not None:
if shade:
colset = self._shade_colors(colset, normals)
polyc.set_facecolors(colset)
polyc.set_edgecolors(colset)
elif cmap:
colset = np.array(colset)
polyc.set_array(colset)
if vmin is not None or vmax is not None:
polyc.set_clim(vmin, vmax)
if norm is not None:
polyc.set_norm(norm)
else:
if shade:
colset = self._shade_colors(color, normals)
else:
colset = color
polyc.set_facecolors(colset)
self.add_collection(polyc)
x, y, z = , , ;
for pol in polys:
for p in pol:
x.append(p[0])
y.append(p[1])
z.append(p[2])
self.auto_scale_xyz(x, y, z, had_data)
return polyc