Revision: 8656 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8656&view=rev Author: heeres Date: 2010-08-25 07:15:15 +0000 (Wed, 25 Aug 2010)
Log Message: ----------- Fix ticks/limit setting, add tricontour(f), make it easier to set pane colors Modified Paths: -------------- trunk/matplotlib/examples/mplot3d/contour3d_demo2.py trunk/matplotlib/examples/mplot3d/surface3d_radial_demo.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py Modified: trunk/matplotlib/examples/mplot3d/contour3d_demo2.py =================================================================== --- trunk/matplotlib/examples/mplot3d/contour3d_demo2.py 2010-08-23 18:24:25 UTC (rev 8655) +++ trunk/matplotlib/examples/mplot3d/contour3d_demo2.py 2010-08-25 07:15:15 UTC (rev 8656) @@ -4,7 +4,7 @@ fig = plt.figure() ax = fig.gca(projection='3d') X, Y, Z = axes3d.get_test_data(0.05) -cset = ax.contour(X, Y, Z, 16, extend3d=True) +cset = ax.contour(X, Y, Z, extend3d=True) ax.clabel(cset, fontsize=9, inline=1) plt.show() Modified: trunk/matplotlib/examples/mplot3d/surface3d_radial_demo.py =================================================================== --- trunk/matplotlib/examples/mplot3d/surface3d_radial_demo.py 2010-08-23 18:24:25 UTC (rev 8655) +++ trunk/matplotlib/examples/mplot3d/surface3d_radial_demo.py 2010-08-25 07:15:15 UTC (rev 8656) @@ -23,5 +23,4 @@ ax.set_xlabel(r'$\phi_\mathrm{real}$') ax.set_ylabel(r'$\phi_\mathrm{im}$') ax.set_zlabel(r'$V(\phi)$') -ax.set_xticks([]) plt.show() Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2010-08-23 18:24:25 UTC (rev 8655) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2010-08-25 07:15:15 UTC (rev 8656) @@ -291,8 +291,17 @@ PatchCollection.__init__(self, *args, **kwargs) self._old_draw = lambda x: PatchCollection.draw(self, x) + def set_sort_zpos(self,val): + '''Set the position to use for z-sorting.''' + self._sort_zpos = val + def set_3d_properties(self, zs, zdir): - xs, ys = zip(*self.get_offsets()) + offsets = self.get_offsets() + if len(offsets) > 0: + xs, ys = zip(*self.get_offsets()) + else: + xs = [0] * len(zs) + ys = [0] * len(zs) self._offsets3d = juggle_axes(xs, ys, zs, zdir) self._facecolor3d = self.get_facecolor() self._edgecolor3d = self.get_edgecolor() Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2010-08-23 18:24:25 UTC (rev 8655) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2010-08-25 07:15:15 UTC (rev 8656) @@ -58,7 +58,6 @@ if rect is None: rect = [0.0, 0.0, 1.0, 1.0] - self.fig = fig self._cids = [] self.initial_azim = kwargs.pop('azim', -60) @@ -72,20 +71,28 @@ # they can't be defined until Axes.__init__ has been called self.view_init(self.initial_elev, self.initial_azim) self._ready = 0 - Axes.__init__(self, self.fig, rect, + + Axes.__init__(self, fig, rect, frameon=True, - xticks=[], yticks=[], *args, **kwargs) - + *args, **kwargs) + # Disable drawing of axes by base class + Axes.set_axis_off(self) + self._axis3don = True self.M = None self._ready = 1 self.mouse_init() - self.create_axes() self.set_top_view() self.axesPatch.set_linewidth(0) - self.fig.add_axes(self) + self.figure.add_axes(self) + def set_axis_off(self): + self._axis3don = False + + def set_axis_on(self): + self._axis3don = True + def set_top_view(self): # this happens to be the right view for the viewing coordinates # moved up and to the left slightly to fit labels and axes @@ -97,14 +104,24 @@ Axes.set_xlim(self, -xdwl, xdw, auto=None) Axes.set_ylim(self, -ydwl, ydw, auto=None) - def create_axes(self): + def _init_axis(self): + '''Init 3d axes; overrides creation of regular X/Y axes''' self.w_xaxis = axis3d.XAxis('x', self.xy_viewLim.intervalx, self.xy_dataLim.intervalx, self) + self.xaxis = self.w_xaxis self.w_yaxis = axis3d.YAxis('y', self.xy_viewLim.intervaly, self.xy_dataLim.intervaly, self) + self.yaxis = self.w_yaxis self.w_zaxis = axis3d.ZAxis('z', self.zz_viewLim.intervalx, self.zz_dataLim.intervalx, self) + self.zaxis = self.w_zaxis + for ax in self.xaxis, self.yaxis, self.zaxis: + ax.init3d() + + def get_children(self): + return [self.zaxis,] + Axes.get_children(self) + def unit_cube(self, vals=None): minx, maxx, miny, maxy, minz, maxz = vals or self.get_w_lims() xs, ys, zs = ([minx, maxx, maxx, minx, minx, maxx, maxx, minx], @@ -165,12 +182,16 @@ for i, (z, patch) in enumerate(zlist): patch.zorder = i - axes = (self.w_xaxis, self.w_yaxis, self.w_zaxis) - for ax in axes: - ax.draw_pane(renderer) - for ax in axes: - ax.draw(renderer) + if self._axis3don: + axes = (self.w_xaxis, self.w_yaxis, self.w_zaxis) + # Draw panes first + for ax in axes: + ax.draw_pane(renderer) + # Then axes + for ax in axes: + ax.draw(renderer) + # Then rest Axes.draw(self, renderer) def get_axis_position(self): @@ -240,18 +261,21 @@ lims = self._determine_lims(*args, **kwargs) self.xy_viewLim.intervalx = lims return lims + set_xlim = set_xlim3d def set_ylim3d(self, *args, **kwargs): '''Set 3D y limits.''' lims = self._determine_lims(*args, **kwargs) self.xy_viewLim.intervaly = lims return lims + set_ylim = set_ylim3d def set_zlim3d(self, *args, **kwargs): '''Set 3D z limits.''' lims = self._determine_lims(*args, **kwargs) self.zz_viewLim.intervalx = lims return lims + set_zlim = set_zlim3d def get_xlim3d(self): '''Get 3D x limits.''' @@ -264,7 +288,18 @@ def get_zlim3d(self): '''Get 3D z limits.''' return self.zz_viewLim.intervalx + get_zlim = get_zlim3d + def set_zticks(self,*args,**kwargs): + """ + Set 3d z tick locations and (optionally labels). + See set_xticks3d for more details. + """ + return self.w_zaxis.set_ticks(*args, **kwargs) + + def get_zticks(self): + return self.w_zaxis.get_ticks() + def clabel(self, *args, **kwargs): return None @@ -722,8 +757,9 @@ # 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]) + i1, i2, i3 = 0, int(len(ps2)/3), int(2*len(ps2)/3) + v1 = np.array(ps2[i1]) - np.array(ps2[i2]) + v2 = np.array(ps2[i2]) - np.array(ps2[i3]) normals.append(np.cross(v1, v2)) polyc = art3d.Poly3DCollection(polys, *args, **kwargs) @@ -896,6 +932,16 @@ for col in colls: self.collections.remove(col) + def add_contour_set(self, cset, extend3d=False, stride=5, zdir='z', offset=None): + zdir = '-' + zdir + if extend3d: + self._3d_extend_contour(cset, stride) + else: + for z, linec in zip(cset.levels, cset.collections): + if offset is not None: + z = offset + art3d.line_collection_2d_to_3d(linec, z, zdir=zdir) + def contour(self, X, Y, Z, *args, **kwargs): ''' Create a 3D contour plot. @@ -927,21 +973,49 @@ jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) cset = Axes.contour(self, jX, jY, jZ, *args, **kwargs) + self.add_contour_set(cset, extend3d, stride, zdir, offset) - zdir = '-' + zdir - if extend3d: - self._3d_extend_contour(cset, stride) - else: - for z, linec in zip(cset.levels, cset.collections): - if offset is not None: - z = offset - art3d.line_collection_2d_to_3d(linec, z, zdir=zdir) - self.auto_scale_xyz(X, Y, Z, had_data) return cset contour3D = contour + def tricontour(self, X, Y, Z, *args, **kwargs): + ''' + Create a 3D contour plot. + + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *extend3d* Whether to extend contour in 3D (default: False) + *stride* Stride (step size) for extending contour + *zdir* The direction to use: x, y or z (default) + *offset* If specified plot a projection of the contour + lines on this position in plane normal to zdir + ========== ================================================ + + Other keyword arguments are passed on to + :func:`~matplotlib.axes.Axes.tricontour` + + Returns a :class:`~matplotlib.axes.Axes.contour` + ''' + + extend3d = kwargs.pop('extend3d', False) + stride = kwargs.pop('stride', 5) + zdir = kwargs.pop('zdir', 'z') + offset = kwargs.pop('offset', None) + + had_data = self.has_data() + + jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) + cset = Axes.tricontour(self, jX, jY, jZ, *args, **kwargs) + self.add_contour_set(cset, extend3d, stride, zdir, offset) + + self.auto_scale_xyz(X, Y, Z, had_data) + return cset + def contourf(self, X, Y, Z, *args, **kwargs): ''' Plot filled 3D contours. @@ -968,6 +1042,43 @@ contourf3D = contourf + def tricontourf(self, X, Y, Z, offset=None, zdir='z', *args, **kwargs): + ''' + Create a 3D contourf plot. + + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *extend3d* Whether to extend contour in 3D (default: False) + *stride* Stride (step size) for extending contour + *zdir* The direction to use: x, y or z (default) + *offset* If specified plot a projection of the contour + lines on this position in plane normal to zdir + ========== ================================================ + + Other keyword arguments are passed on to + :func:`~matplotlib.axes.Axes.tricontour` + + Returns a :class:`~matplotlib.axes.Axes.contour` + ''' + + zdir = '-' + zdir + had_data = self.has_data() + + cset = Axes.tricontourf(self, X, Y, Z, *args, **kwargs) + levels = cset.levels + colls = cset.collections + for z1, linec in zip(levels, colls): + if offset is not None: + z1 = offset + art3d.poly_collection_2d_to_3d(linec, z1, zdir=zdir) + linec.set_sort_zpos(z1) + + self.auto_scale_xyz(X, Y, Z, had_data) + return cset + def add_collection3d(self, col, zs=0, zdir='z'): ''' Add a 3d collection object to the plot. @@ -993,21 +1104,32 @@ Axes.add_collection(self, col) - def scatter(self, xs, ys, zs=0, zdir='z', *args, **kwargs): + def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', *args, **kwargs): ''' Create a scatter plot. ========== ================================================ Argument Description - ========== ================================================ + ========== ========================================================== *xs*, *ys* Positions of data points. *zs* Either an array of the same length as *xs* and *ys* or a single value to place all points in the same plane. Default is 0. *zdir* Which direction to use as z ('x', 'y' or 'z') when plotting a 2d set. - ========== ================================================ + *s* size in points^2. It is a scalar or an array of the same + length as *x* and *y*. + *c* a color. *c* can be a single color format string, or a + sequence of color specifications of length *N*, or a + sequence of *N* numbers to be mapped to colors using the + *cmap* and *norm* specified via kwargs (see below). Note + that *c* should not be a single numeric RGB or RGBA + sequence because that is indistinguishable from an array + of values to be colormapped. *c* can be a 2-D array in + which the rows are RGB or RGBA, however. + ========== ========================================================== + Keyword arguments are passed on to :func:`~matplotlib.axes.Axes.scatter`. @@ -1016,7 +1138,25 @@ had_data = self.has_data() - patches = Axes.scatter(self, xs, ys, *args, **kwargs) + xs = np.ma.ravel(xs) + ys = np.ma.ravel(ys) + zs = np.ma.ravel(zs) + if xs.size != ys.size: + raise ValueError("x and y must be the same size") + if xs.size != zs.size and zs.size == 1: + zs = np.array(zs[0] * xs.size) + + s = np.ma.ravel(s) # This doesn't have to match x, y in size. + + cstr = cbook.is_string_like(c) or cbook.is_sequence_of_strings(c) + if not cstr: + c = np.asanyarray(c) + if c.size == xs.size: + c = np.ma.ravel(c) + + xs, ys, zs, s, c = cbook.delete_masked_points(xs, ys, zs, s, c) + + patches = Axes.scatter(self, xs, ys, s=s, c=c, *args, **kwargs) if not cbook.iterable(zs): is_2d = True zs = np.ones(len(xs)) * zs Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py 2010-08-23 18:24:25 UTC (rev 8655) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/axis3d.py 2010-08-25 07:15:15 UTC (rev 8656) @@ -73,6 +73,11 @@ self.v_interval = v_intervalx maxis.XAxis.__init__(self, axes, *args, **kwargs) + + self.set_rotate_label(kwargs.get('rotate_label', None)) + + + def init3d(self): self.line = mlines.Line2D(xdata=(0, 0), ydata=(0, 0), linewidth=0.75, color=(0, 0, 0, 1), @@ -80,11 +85,11 @@ ) # Store dummy data in Polygon object - self.has_pane = True self.pane = mpatches.Polygon(np.array([[0,0], [0,1], [1,0], [0,0]]), alpha=0.8, facecolor=(1,1,1,0), edgecolor=(1,1,1,0)) + self.set_pane_color(self._AXINFO[self.adir]['color']) self.axes._set_artist_props(self.line) self.axes._set_artist_props(self.pane) @@ -92,7 +97,6 @@ self.axes._set_artist_props(self.gridlines) self.axes._set_artist_props(self.label) self.label._transform = self.axes.transData - self.set_rotate_label(kwargs.get('rotate_label', None)) def get_tick_positions(self): majorLocs = self.major.locator() @@ -110,15 +114,17 @@ t.label2.set_transform(self.axes.transData) return ticks - def set_pane(self, xys, color): - if self.has_pane: - xys = np.asarray(xys) - xys = xys[:,:2] - self.pane.xy = xys - self.pane.set_edgecolor(color) - self.pane.set_facecolor(color) - self.pane.set_alpha(color[-1]) + def set_pane_pos(self, xys): + xys = np.asarray(xys) + xys = xys[:,:2] + self.pane.xy = xys + def set_pane_color(self, color): + '''Set pane color to a RGBA tuple''' + self.pane.set_edgecolor(color) + self.pane.set_facecolor(color) + self.pane.set_alpha(color[-1]) + def set_rotate_label(self, val): ''' Whether to rotate the axis label: True, False or None. @@ -161,7 +167,7 @@ else: plane = self._PLANES[2 * index + 1] xys = [tc[p] for p in plane] - self.set_pane(xys, info['color']) + self.set_pane_pos(xys) self.pane.draw(renderer) renderer.close_group('pane3d') @@ -225,25 +231,26 @@ self.label.set_va('center') self.label.draw(renderer) - # Grid points at end of one plane - xyz1 = copy.deepcopy(xyz0) - newindex = (index + 1) % 3 - newval = get_flip_min_max(xyz1[0], newindex, mins, maxs) - for i in range(len(majorLocs)): - xyz1[i][newindex] = newval + if len(xyz0) > 0: + # Grid points at end of one plane + xyz1 = copy.deepcopy(xyz0) + newindex = (index + 1) % 3 + newval = get_flip_min_max(xyz1[0], newindex, mins, maxs) + for i in range(len(majorLocs)): + xyz1[i][newindex] = newval - # Grid points at end of the other plane - xyz2 = copy.deepcopy(xyz0) - newindex = (index + 2) % 3 - newval = get_flip_min_max(xyz2[0], newindex, mins, maxs) - for i in range(len(majorLocs)): - xyz2[i][newindex] = newval + # Grid points at end of the other plane + xyz2 = copy.deepcopy(xyz0) + newindex = (index + 2) % 3 + newval = get_flip_min_max(xyz2[0], newindex, mins, maxs) + for i in range(len(majorLocs)): + xyz2[i][newindex] = newval - lines = zip(xyz1, xyz0, xyz2) - if self.axes._draw_grid: - self.gridlines.set_segments(lines) - self.gridlines.set_color([(0.9,0.9,0.9,1)] * len(lines)) - self.gridlines.draw(renderer, project=True) + lines = zip(xyz1, xyz0, xyz2) + if self.axes._draw_grid: + self.gridlines.set_segments(lines) + self.gridlines.set_color([(0.9,0.9,0.9,1)] * len(lines)) + self.gridlines.draw(renderer, project=True) # Draw ticks tickdir = info['tickdir'] @@ -284,19 +291,23 @@ renderer.close_group('axis3d') def get_view_interval(self): - """return the Interval instance for this axis view limits""" + """return the Interval instance for this 3d axis view limits""" return self.v_interval + + def set_view_interval(self, vmin, vmax, ignore=False): + if ignore: + self.v_interval = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.v_interval = min(vmin, Vmin), max(vmax, Vmax) -# Each type of axis should be looking in a different place for its -# current data limits so we do this with classes. I think there is -# a lot more that I can and should move down into these classes also. +# Use classes to look at different data limits class XAxis(Axis): def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.xy_dataLim.intervalx - class YAxis(Axis): def get_data_interval(self): 'return the Interval instance for this axis data limits' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ Sell apps to millions through the Intel(R) Atom(Tm) Developer Program Be part of this innovative community and reach millions of netbook users worldwide. Take advantage of special opportunities to increase revenue and speed time-to-market. Join now, and jumpstart your future. http://p.sf.net/sfu/intel-atom-d2d _______________________________________________ Matplotlib-checkins mailing list Matplotlib-checkins@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins