From: <he...@us...> - 2010-08-25 07:15:22
|
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. |