From: <he...@us...> - 2010-03-19 17:12:49
|
Revision: 8199 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8199&view=rev Author: heeres Date: 2010-03-19 17:12:41 +0000 (Fri, 19 Mar 2010) Log Message: ----------- Some fixes Modified Paths: -------------- trunk/matplotlib/examples/mplot3d/hist3d_demo.py trunk/matplotlib/examples/mplot3d/text3d_demo.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py Modified: trunk/matplotlib/examples/mplot3d/hist3d_demo.py =================================================================== --- trunk/matplotlib/examples/mplot3d/hist3d_demo.py 2010-03-19 16:50:37 UTC (rev 8198) +++ trunk/matplotlib/examples/mplot3d/hist3d_demo.py 2010-03-19 17:12:41 UTC (rev 8199) @@ -17,7 +17,7 @@ dy = dx.copy() dz = hist.flatten() -ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b') +ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color='b', zsort='average') plt.show() Modified: trunk/matplotlib/examples/mplot3d/text3d_demo.py =================================================================== --- trunk/matplotlib/examples/mplot3d/text3d_demo.py 2010-03-19 16:50:37 UTC (rev 8198) +++ trunk/matplotlib/examples/mplot3d/text3d_demo.py 2010-03-19 17:12:41 UTC (rev 8199) @@ -13,6 +13,9 @@ label = '(%d, %d, %d), dir=%s' % (x, y, z, zdir) ax.text(x, y, z, label, zdir) +ax.text(1, 1, 1, "red", color='red') +ax.text2D(0.05, 0.95, "2D Text", transform=ax.transAxes) + ax.set_xlim3d(0, 10) ax.set_ylim3d(0, 10) ax.set_zlim3d(0, 10) Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2010-03-19 16:50:37 UTC (rev 8198) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/art3d.py 2010-03-19 17:12:41 UTC (rev 8199) @@ -1,6 +1,7 @@ #!/usr/bin/python # art3d.py, original mplot3d version by John Porter # Parts rewritten by Reinier Heeres <re...@he...> +# Minor additions by Ben Axelrod <bax...@co...> ''' Module containing 3D artist code and functions to convert 2D @@ -15,6 +16,7 @@ from matplotlib.colors import Normalize from matplotlib.cbook import iterable +import warnings import numpy as np import math import proj3d @@ -52,8 +54,15 @@ Text object with 3D position and (in the future) direction. ''' - def __init__(self, x=0, y=0, z=0, text='', zdir='z'): - mtext.Text.__init__(self, x, y, text) + def __init__(self, x=0, y=0, z=0, text='', zdir='z', **kwargs): + ''' + *x*, *y*, *z* Position of text + *text* Text string to display + *zdir* Direction of text + + Keyword arguments are passed onto :func:`~matplotlib.text.Text`. + ''' + mtext.Text.__init__(self, x, y, text, **kwargs) self.set_3d_properties(z, zdir) def set_3d_properties(self, z=0, zdir='z'): @@ -86,6 +95,9 @@ ''' def __init__(self, xs, ys, zs, *args, **kwargs): + ''' + Keyword arguments are passed onto :func:`~matplotlib.lines.Line2D`. + ''' lines.Line2D.__init__(self, [], [], *args, **kwargs) self._verts3d = xs, ys, zs @@ -145,6 +157,9 @@ ''' def __init__(self, segments, *args, **kwargs): + ''' + Keyword arguments are passed onto :func:`~matplotlib.collections.LineCollection`. + ''' LineCollection.__init__(self, segments, *args, **kwargs) def set_segments(self, segments): @@ -317,13 +332,44 @@ *verts* should contain 3D coordinates. + Keyword arguments: + zsort, see set_zsort for options. + Note that this class does a bit of magic with the _facecolors and _edgecolors properties. ''' + self.set_zsort(kwargs.pop('zsort', True)) + PolyCollection.__init__(self, verts, *args, **kwargs) - self._zsort = 1 + + _zsort_functions = { + 'average': np.average, + 'min': np.min, + 'max': np.max, + } + + def set_zsort(self, zsort): + ''' + Set z-sorting behaviour: + boolean: if True use default 'average' + string: 'average', 'min' or 'max' + ''' + + if zsort is True: + zsort = 'average' + + if zsort is not False: + if zsort in self._zsort_functions: + zsortfunc = self._zsort_functions[zsort] + else: + return False + else: + zsortfunc = None + + self._zsort = zsort self._sort_zpos = None + self._zsortfunc = zsortfunc def get_vector(self, segments3d): """Optimize points for projection""" @@ -348,12 +394,13 @@ PolyCollection.set_verts(self, [], closed) def set_3d_properties(self): - self._zsort = 1 self._sort_zpos = None + self.set_zsort(True) self._facecolors3d = PolyCollection.get_facecolors(self) self._edgecolors3d = PolyCollection.get_edgecolors(self) - def set_sort_zpos(self, val): + def set_sort_zpos(self,val): + '''Set the position to use for z-sorting.''' self._sort_zpos = val def do_3d_projection(self, renderer): @@ -381,7 +428,7 @@ # if required sort by depth (furthest drawn first) if self._zsort: - z_segments_2d = [(np.average(zs), zip(xs, ys), fc, ec) for + z_segments_2d = [(self._zsortfunc(zs), zip(xs, ys), fc, ec) for (xs, ys, zs), fc, ec in zip(xyzlist, cface, cedge)] z_segments_2d.sort(cmp=lambda x, y: cmp(y[0], x[0])) else: @@ -468,9 +515,14 @@ def iscolor(c): try: - return (len(c) == 4 or len(c) == 3) and hasattr(c[0], '__float__') - except (IndexError): + if len(c) == 4 or len(c) == 3: + if iterable(c[0]): + return False + if hasattr(c[0], '__float__'): + return True + except: return False + return False def get_colors(c, num): """Stretch the color argument to provide the required number num""" @@ -484,6 +536,8 @@ return c elif iscolor(c): return [c] * num + elif len(c) == 0: #if edgecolor or facecolor is specified as 'none' + return [[0,0,0,0]] * num elif iscolor(c[0]): return [c[0]] * num else: Modified: trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2010-03-19 16:50:37 UTC (rev 8198) +++ trunk/matplotlib/lib/mpl_toolkits/mplot3d/axes3d.py 2010-03-19 17:12:41 UTC (rev 8199) @@ -2,12 +2,14 @@ # axes3d.py, original mplot3d version by John Porter # Created: 23 Sep 2005 # Parts fixed by Reinier Heeres <re...@he...> +# Minor additions by Ben Axelrod <bax...@co...> """ Module containing Axes3D, an object which can plot 3D objects on a 2D matplotlib figure. """ +import warnings from matplotlib.axes import Axes, rcParams from matplotlib import cbook from matplotlib.transforms import Bbox @@ -55,7 +57,7 @@ if rect is None: rect = [0.0, 0.0, 1.0, 1.0] self.fig = fig - self.cids = [] + self._cids = [] azim = kwargs.pop('azim', -60) elev = kwargs.pop('elev', 30) @@ -147,7 +149,7 @@ # Calculate projection of collections and zorder them zlist = [(col.do_3d_projection(renderer), col) \ - for col in self.collections] + for col in self.collections] zlist.sort() zlist.reverse() for i, (z, col) in enumerate(zlist): @@ -322,23 +324,52 @@ M = np.dot(perspM, M0) return M - def mouse_init(self): + def mouse_init(self, rotate_btn=1, zoom_btn=3): + """Initializes mouse button callbacks to enable 3D rotation of + the axes. Also optionally sets the mouse buttons for 3D rotation + and zooming. + + ============ ================================================ + Argument Description + ============ ================================================ + *rotate_btn* The integer or list of integers specifying which mouse + button or buttons to use for 3D rotation of the axes. + Default = 1. + + *zoom_btn* The integer or list of integers specifying which mouse + button or buttons to use to zoom the 3D axes. + Default = 3. + ============ ================================================ + """ self.button_pressed = None canv = self.figure.canvas if canv != None: c1 = canv.mpl_connect('motion_notify_event', self._on_move) c2 = canv.mpl_connect('button_press_event', self._button_press) c3 = canv.mpl_connect('button_release_event', self._button_release) - self.cids = [c1, c2, c3] + self._cids = [c1, c2, c3] + else: + warnings.warn('Axes3D.figure.canvas is \'None\', mouse rotation disabled. Set canvas then call Axes3D.mouse_init().') + self._rotate_btn = np.atleast_1d(rotate_btn) + self._zoom_btn = np.atleast_1d(zoom_btn) + def cla(self): - # Disconnect the various events we set. - for cid in self.cids: - self.figure.canvas.mpl_disconnect(cid) - self.cids = [] + """Clear axes and disable mouse button callbacks. + """ + self.disable_mouse_rotation() Axes.cla(self) self.grid(rcParams['axes3d.grid']) + def disable_mouse_rotation(self): + """Disable mouse button callbacks. + """ + # Disconnect the various events we set. + for cid in self._cids: + self.figure.canvas.mpl_disconnect(cid) + + self._cids = [] + def _button_press(self, event): if event.inaxes == self: self.button_pressed = event.button @@ -426,9 +457,10 @@ def _on_move(self, event): """Mouse moving - button-1 rotates - button-3 zooms + button-1 rotates by default. Can be set explicitly in mouse_init(). + button-3 zooms by default. Can be set explicitly in mouse_init(). """ + if not self.button_pressed: return @@ -447,7 +479,8 @@ h = (y1-y0) self.sx, self.sy = x, y - if self.button_pressed == 1: + # Rotation + if self.button_pressed in self._rotate_btn: # rotate viewing point # get the x and y pixel coords if dx == 0 and dy == 0: @@ -456,12 +489,15 @@ self.azim = art3d.norm_angle(self.azim - (dx/w)*180) self.get_proj() self.figure.canvas.draw() - elif self.button_pressed == 2: + +# elif self.button_pressed == 2: # pan view # project xv,yv,zv -> xw,yw,zw # pan - pass - elif self.button_pressed == 3: +# pass + + # Zoom + elif self.button_pressed in self._zoom_btn: # zoom view # hmmm..this needs some help from clipping.... minx, maxx, miny, maxy, minz, maxz = self.get_w_lims() @@ -476,7 +512,7 @@ self.figure.canvas.draw() def set_xlabel(self, xlabel, fontdict=None, **kwargs): - '''Set xlabel. ''' + '''Set xlabel.''' label = self.w_xaxis.get_label() label.set_text(xlabel) @@ -511,13 +547,18 @@ ''' self._draw_grid = on - def text(self, x, y, z, s, zdir=None): - '''Add text to the plot.''' - text = Axes.text(self, x, y, s) + def text(self, x, y, z, s, zdir=None, **kwargs): + ''' + Add text to the plot. kwargs will be passed on to Axes.text, + except for the `zdir` keyword, which sets the direction to be + used as the z direction. + ''' + text = Axes.text(self, x, y, s, **kwargs) art3d.text_2d_to_3d(text, z, zdir) return text text3D = text + text2D = Axes.text def plot(self, xs, ys, *args, **kwargs): ''' @@ -591,6 +632,9 @@ *shade* Whether to shade the facecolors, default: false when cmap specified, true otherwise ========== ================================================ + + Other arguments are passed on to + :func:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection.__init__` ''' had_data = self.has_data() @@ -822,8 +866,9 @@ colors = self._shade_colors(color, normals) colors2 = self._shade_colors(color, normals) - polycol = art3d.Poly3DCollection(polyverts, facecolors=colors, - edgecolors=colors2) + polycol = art3d.Poly3DCollection(polyverts, + facecolors=colors, + edgecolors=colors2) polycol.set_sort_zpos(z) self.add_collection3d(polycol) @@ -848,6 +893,8 @@ Other keyword arguments are passed on to :func:`~matplotlib.axes.Axes.contour` + + Returns a :class:`~matplotlib.axes.Axes.contour` ''' extend3d = kwargs.pop('extend3d', False) @@ -881,7 +928,9 @@ *X*, *Y*, *Z*: data points. Keyword arguments are passed on to - :func:`~matplotlib.axes.Axes.contour` + :func:`~matplotlib.axes.Axes.contourf` + + Returns a :class:`~matplotlib.axes.Axes.contourf` ''' had_data = self.has_data() @@ -1005,25 +1054,61 @@ return patches - def bar3d(self, x, y, z, dx, dy, dz, color='b'): + def bar3d(self, x, y, z, dx, dy, dz, color='b', + zsort='average', *args, **kwargs): ''' Generate a 3D bar, or multiple bars. When generating multiple bars, x, y, z have to be arrays. - dx, dy, dz can still be scalars. + dx, dy, dz can be arrays or scalars. + + *color* can be: + - A single color value, to color all bars the same color. + - An array of colors of length N bars, to color each bar + independently. + - An array of colors of length 6, to color the faces of the bars + similarly. + - An array of colors of length 6 * N bars, to color each face + independently. + + When coloring the faces of the boxes specifically, this is the order + of the coloring: + 1. -Z (bottom of box) + 2. +Z (top of box) + 3. -Y + 4. +Y + 5. -X + 6. +X + + Keyword arguments are passed onto + :func:`~mpl_toolkits.mplot3d.art3d.Poly3DCollection` ''' - had_data = self.has_data() if not cbook.iterable(x): - x, y, z = [x], [y], [z] + x = [x] + if not cbook.iterable(y): + y = [y] + if not cbook.iterable(z): + z = [z] + if not cbook.iterable(dx): - dx, dy, dz = [dx], [dy], [dz] + dx = [dx] + if not cbook.iterable(dy): + dy = [dy] + if not cbook.iterable(dz): + dz = [dz] + if len(dx) == 1: dx = dx * len(x) - dy = dy * len(x) - dz = dz * len(x) + if len(dy) == 1: + dy = dy * len(y) + if len(dz) == 1: + dz = dz * len(z) + if len(x) != len(y) or len(x) != len(z): + warnings.warn('x, y, and z must be the same length.') + minx, miny, minz = 1e20, 1e20, 1e20 maxx, maxy, maxz = -1e20, -1e20, -1e20 @@ -1053,15 +1138,35 @@ (xi + dxi, yi + dyi, zi + dzi), (xi + dxi, yi, zi + dzi)), ]) - color = np.array(colorConverter.to_rgba(color)) + facecolors = [] + if color is None: + # no color specified + facecolors = [None] * len(x) + elif len(color) == len(x): + # bar colors specified, need to expand to number of faces + for c in color: + facecolors.extend([c] * 6) + else: + # a single color specified, or face colors specified explicitly + facecolors = list(colorConverter.to_rgba_array(color)) + if len(facecolors) < len(x): + facecolors *= (6 * len(x)) + normals = self._generate_normals(polys) - colors = self._shade_colors(color, normals) - - col = art3d.Poly3DCollection(polys, facecolor=colors) + sfacecolors = self._shade_colors(facecolors, normals) + col = art3d.Poly3DCollection(polys, + zsort=zsort, + facecolor=sfacecolors, + *args, **kwargs) self.add_collection(col) self.auto_scale_xyz((minx, maxx), (miny, maxy), (minz, maxz), had_data) + def set_title(self, label, fontdict=None, **kwargs): + Axes.set_title(self, label, fontdict, **kwargs) + (x, y) = self.title.get_position() + self.title.set_y(0.92 * y) + def get_test_data(delta=0.05): ''' Return a tuple X, Y, Z with a test data set. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |