From: <he...@us...> - 2009-06-09 22:41:32
|
Revision: 7206 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7206&view=rev Author: heeres Date: 2009-06-09 22:40:35 +0000 (Tue, 09 Jun 2009) Log Message: ----------- mplot3d: make Poly3DCollection ScalarMappable, support in Axes3D.plot_surface. Fix-up and extend contours. Add examples. Modified Paths: -------------- trunk/matplotlib/doc/mpl_toolkits/mplot3d/tutorial.rst trunk/matplotlib/examples/mplot3d/surface3d_demo.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py Added Paths: ----------- trunk/matplotlib/examples/mplot3d/contour3d_demo2.py trunk/matplotlib/examples/mplot3d/surface3d_demo2.py Modified: trunk/matplotlib/doc/mpl_toolkits/mplot3d/tutorial.rst =================================================================== --- trunk/matplotlib/doc/mpl_toolkits/mplot3d/tutorial.rst 2009-06-09 16:47:46 UTC (rev 7205) +++ trunk/matplotlib/doc/mpl_toolkits/mplot3d/tutorial.rst 2009-06-09 22:40:35 UTC (rev 7206) @@ -38,12 +38,14 @@ .. automethod:: Axes3D.plot_surface .. plot:: mpl_examples/mplot3d/surface3d_demo.py +.. plot:: mpl_examples/mplot3d/surface3d_demo2.py Contour plots ============= .. automethod:: Axes3D.contour .. plot:: mpl_examples/mplot3d/contour3d_demo.py +.. plot:: mpl_examples/mplot3d/contour3d_demo2.py Filled contour plots ==================== Added: trunk/matplotlib/examples/mplot3d/contour3d_demo2.py =================================================================== --- trunk/matplotlib/examples/mplot3d/contour3d_demo2.py (rev 0) +++ trunk/matplotlib/examples/mplot3d/contour3d_demo2.py 2009-06-09 22:40:35 UTC (rev 7206) @@ -0,0 +1,12 @@ +from mpl_toolkits.mplot3d import axes3d +import pylab +import random + +fig = pylab.figure() +ax = axes3d.Axes3D(fig) +X, Y, Z = axes3d.get_test_data(0.05) +cset = ax.contour(X, Y, Z, 16, extend3d=True) +ax.clabel(cset, fontsize=9, inline=1) + +pylab.show() + Modified: trunk/matplotlib/examples/mplot3d/surface3d_demo.py =================================================================== --- trunk/matplotlib/examples/mplot3d/surface3d_demo.py 2009-06-09 16:47:46 UTC (rev 7205) +++ trunk/matplotlib/examples/mplot3d/surface3d_demo.py 2009-06-09 22:40:35 UTC (rev 7206) @@ -1,16 +1,17 @@ from mpl_toolkits.mplot3d import Axes3D +from matplotlib import cm import pylab import random import numpy as np fig = pylab.figure() ax = Axes3D(fig) -X = np.arange(-5, 5, 0.5) -Y = np.arange(-5, 5, 0.5) +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) -ax.plot_surface(X, Y, Z, rstride=1, cstride=1, color='forestgreen') +ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet) pylab.show() Added: trunk/matplotlib/examples/mplot3d/surface3d_demo2.py =================================================================== --- trunk/matplotlib/examples/mplot3d/surface3d_demo2.py (rev 0) +++ trunk/matplotlib/examples/mplot3d/surface3d_demo2.py 2009-06-09 22:40:35 UTC (rev 7206) @@ -0,0 +1,18 @@ +from mpl_toolkits.mplot3d import Axes3D +import pylab +import random +import numpy as np + +fig = pylab.figure() +ax = Axes3D(fig) + +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') + +pylab.show() + Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2009-06-09 16:47:46 UTC (rev 7205) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2009-06-09 22:40:35 UTC (rev 7206) @@ -10,6 +10,7 @@ from matplotlib import lines, text as mtext, path as mpath, colors as mcolors from matplotlib.collections import Collection, LineCollection, \ PolyCollection, PatchCollection +from matplotlib.cm import ScalarMappable from matplotlib.patches import Patch from matplotlib.colors import Normalize from matplotlib.cbook import iterable @@ -111,26 +112,30 @@ line.__class__ = Line3D line.set_3d_properties(zs, zdir) -def path_to_3d_segment(path, z=0, zdir='z'): +def path_to_3d_segment(path, zs=0, zdir='z'): '''Convert a path to a 3D segment.''' + + if not iterable(zs): + zs = [zs] * len(path) + seg = [] - for (pathseg, code) in path.iter_segments(): - seg.append(pathseg) - seg3d = [juggle_axes(x, y, z, zdir) for (x, y) in seg] + pathsegs = path.iter_segments(simplify=False, curves=False) + for (((x, y), code), z) in zip(pathsegs, zs): + seg.append((x, y, z)) + seg3d = [juggle_axes(x, y, z, zdir) for (x, y, z) in seg] return seg3d def paths_to_3d_segments(paths, zs=0, zdir='z'): - '''Convert paths from a collection object to 3D segments.''' + ''' + Convert paths from a collection object to 3D segments. + ''' - try: - zs = float(zs) + if not iterable(zs): zs = [zs] * len(paths) - except: - pass segments = [] - for path, z in zip(paths, zs): - segments.append(path_to_3d_segment(path, z, zdir)) + for path, pathz in zip(paths, zs): + segments.append(path_to_3d_segment(path, pathz, zdir)) return segments class Line3DCollection(LineCollection): @@ -255,8 +260,17 @@ ''' def __init__(self, verts, *args, **kwargs): + ''' + Create a Poly3DCollection. + + *verts* should contain 3D coordinates. + + Note that this class does a bit of magic with the _facecolors + and _edgecolors properties. + ''' + PolyCollection.__init__(self, verts, *args, **kwargs) - self.set_3d_properties() + self._zsort = 1 def get_vector(self, segments3d): """Optimize points for projection""" @@ -276,6 +290,7 @@ self._sort_zpos = min(zs) def set_verts(self, verts, closed=True): + '''Set 3D vertices.''' self.get_vector(verts) # 2D verts will be updated at draw time PolyCollection.set_verts(self, [], closed) @@ -283,40 +298,73 @@ def set_3d_properties(self): self._zsort = 1 self._facecolors3d = PolyCollection.get_facecolors(self) - self._edgecolors3d = self.get_edgecolors() + self._edgecolors3d = PolyCollection.get_edgecolors(self) def do_3d_projection(self, renderer): + ''' + Perform the 3D projection for this object. + ''' + + if self._A is not None: + self.update_scalarmappable() + self._facecolors3d = self._facecolors + txs, tys, tzs = proj3d.proj_transform_vec(self._vec, renderer.M) xyzlist = [(txs[si:ei], tys[si:ei], tzs[si:ei]) \ for si, ei in self._segis] - colors = self._facecolors3d + # This extra fuss is to re-order face / edge colors + cface = self._facecolors3d + if len(self._edgecolors3d) != len(cface): + cedge = cface + else: + cedge = self._edgecolors3d + # if required sort by depth (furthest drawn first) if self._zsort: - z_segments_2d = [(min(zs), zip(xs, ys), c) for - (xs, ys, zs), c in zip(xyzlist, colors)] + z_segments_2d = [(min(zs), zip(xs, ys), fc, ec) for + (xs, ys, zs), fc, ec in zip(xyzlist, cface, cedge)] z_segments_2d.sort() z_segments_2d.reverse() else: raise ValueError, "whoops" - segments_2d = [s for z, s, c in z_segments_2d] - colors = [c for z, s, c in z_segments_2d] + + segments_2d = [s for z, s, fc, ec in z_segments_2d] PolyCollection.set_verts(self, segments_2d) - self._facecolors2d = colors + self._facecolors2d = [fc for z, s, fc, ec in z_segments_2d] + if len(self._edgecolors3d) == len(cface): + self._edgecolors2d = [ec for z, s, fc, ec in z_segments_2d] + else: + self._edgecolors2d = self._edgecolors3d + # Return zorder value zvec = np.array([[0], [0], [self._sort_zpos], [1]]) ztrans = proj3d.proj_transform_vec(zvec, renderer.M) return ztrans[2][0] + def set_facecolor(self, colors): + PolyCollection.set_facecolor(self, colors) + self._facecolors3d = PolyCollection.get_facecolor(self) + set_facecolors = set_facecolor + + def set_edgecolor(self, colors): + PolyCollection.set_edgecolor(self, colors) + self._edgecolors3d = PolyCollection.get_edgecolor(self) + set_edgecolors = set_edgecolor + def get_facecolors(self): return self._facecolors2d get_facecolor = get_facecolors + def get_edgecolors(self): + return self._edgecolors2d + get_edgecolor = get_edgecolors + def draw(self, renderer): return Collection.draw(self, renderer) -def poly_collection_2d_to_3d(col, zs=None, zdir='z'): +def poly_collection_2d_to_3d(col, zs=0, zdir='z'): """Convert a PolyCollection to a Poly3DCollection object.""" segments_3d = paths_to_3d_segments(col.get_paths(), zs, zdir) col.__class__ = Poly3DCollection Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2009-06-09 16:47:46 UTC (rev 7205) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2009-06-09 22:40:35 UTC (rev 7206) @@ -541,6 +541,10 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): ''' Create a surface plot. + + By default it will be colored in shades of a solid color, + but it also supports color mapping by supplying the *cmap* + argument. ========== ================================================ Argument Description @@ -550,6 +554,7 @@ *rstride* Array row stride (step size) *cstride* Array column stride (step size) *color* Color of the surface patches + *cmap* A colormap for the surface patches. ========== ================================================ ''' @@ -562,9 +567,11 @@ color = kwargs.pop('color', 'b') color = np.array(colorConverter.to_rgba(color)) + cmap = kwargs.get('cmap', None) polys = [] - boxes = [] + normals = [] + avgz = [] for rs in np.arange(0, rows-1, rstride): for cs in np.arange(0, cols-1, cstride): ps = [] @@ -579,31 +586,53 @@ corners.append([ztop[0], ztop[-1], zbase[0], zbase[-1]]) z = np.concatenate((ztop, zleft, zbase, zright)) ps.append(z) - boxes.append(map(np.array, zip(*corners))) - polys.append(zip(*ps)) - lines = [] + # 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) + avgz.append(avgzsum / len(ps2)) + + v1 = np.array(ps2[0]) - np.array(ps2[1]) + v2 = np.array(ps2[2]) - np.array(ps2[0]) + normals.append(np.cross(v1, v2)) + + polyc = art3d.Poly3DCollection(polys, *args, **kwargs) + if cmap is not None: + polyc.set_array(np.array(avgz)) + polyc.set_linewidth(0) + else: + colors = self._shade_colors(color, normals) + polyc.set_facecolors(colors) + + self.add_collection(polyc) + self.auto_scale_xyz(X, Y, Z, had_data) + + return polyc + + def _shade_colors(self, color, normals): shade = [] - for box in boxes: - n = proj3d.cross(box[0]-box[1], - box[0]-box[2]) - n = n/proj3d.mod(n)*5 + for n in normals: + n = n / proj3d.mod(n) * 5 shade.append(np.dot(n, [-1, -1, 0.5])) - lines.append((box[0], n+box[0])) shade = np.array(shade) mask = ~np.isnan(shade) norm = Normalize(min(shade[mask]), max(shade[mask])) - colors = [color * (0.5+norm(v)*0.5) for v in shade] - for c in colors: c[3] = 1 - polyc = art3d.Poly3DCollection(polys, facecolors=colors, \ - *args, **kwargs) - polyc._zsort = 1 - self.add_collection(polyc) + color = color.copy() + color[3] = 1 + colors = [color * (0.5 + norm(v) * 0.5) for v in shade] - self.auto_scale_xyz(X, Y, Z, had_data) - return polyc + return colors def plot_wireframe(self, X, Y, Z, *args, **kwargs): ''' @@ -653,21 +682,78 @@ return linec - def contour(self, X, Y, Z, *args, **kwargs): + def _3d_extend_contour(self, cset, stride=5): ''' + Extend a contour in 3D by creating + ''' + + levels = cset.levels + colls = cset.collections + dz = (levels[1] - levels[0]) / 2 + + for z, linec in zip(levels, colls): + topverts = art3d.paths_to_3d_segments(linec.get_paths(), z - dz) + botverts = art3d.paths_to_3d_segments(linec.get_paths(), z + dz) + + color = linec.get_color()[0] + + polyverts = [] + normals = [] + nsteps = round(len(topverts[0]) / stride) + stepsize = (len(topverts[0]) - 1) / (nsteps - 1) + for i in range(int(round(nsteps)) - 1): + i1 = int(round(i * stepsize)) + i2 = int(round((i + 1) * stepsize)) + polyverts.append([topverts[0][i1], + topverts[0][i2], + botverts[0][i2], + botverts[0][i1]]) + + v1 = np.array(topverts[0][i1]) - np.array(topverts[0][i2]) + v2 = np.array(topverts[0][i1]) - np.array(botverts[0][i1]) + normals.append(np.cross(v1, v2)) + + colors = self._shade_colors(color, normals) + colors2 = self._shade_colors(color, normals) + polycol = art3d.Poly3DCollection(polyverts, facecolors=colors, + edgecolors=colors2) + self.add_collection3d(polycol) + + for col in colls: + self.collections.remove(col) + + def contour(self, X, Y, Z, levels=10, **kwargs): + ''' Create a 3D contour plot. - *X*, *Y*, *Z*: data + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *levels* Number of levels to use, defaults to 10. Can + also be a tuple of specific levels. + *extend3d* Whether to extend contour in 3D (default: False) + *stride* Stride (step size) for extending contour + ========== ================================================ - Keyword arguments are passed on to + Other keyword arguments are passed on to :func:`~matplotlib.axes.Axes.contour` ''' + extend3d = kwargs.pop('extend3d', False) + stride = kwargs.pop('stride', 5) + nlevels = kwargs.pop('nlevels', 15) + had_data = self.has_data() - cset = Axes.contour(self, X, Y, Z, *args, **kwargs) - for z, linec in zip(cset.levels, cset.collections): - art3d.line_collection_2d_to_3d(linec, z) + cset = Axes.contour(self, X, Y, Z, levels, **kwargs) + if extend3d: + self._3d_extend_contour(cset, stride) + else: + for z, linec in zip(cset.levels, cset.collections): + art3d.line_collection_2d_to_3d(linec, z) + self.auto_scale_xyz(X, Y, Z, had_data) return cset @@ -688,11 +774,8 @@ cset = Axes.contourf(self, X, Y, Z, *args, **kwargs) levels = cset.levels colls = cset.collections - for z1, z2, linec in zip(levels, levels[1:], colls): - zs = [z1] * (len(linec.get_paths()[0]) / 2) - zs += [z2] * (len(linec.get_paths()[0]) / 2) - art3d.poly_collection_2d_to_3d(linec, zs) + art3d.poly_collection_2d_to_3d(linec, z1) self.auto_scale_xyz(X, Y, Z, had_data) return cset This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |