Source code for scitools.easyviz.common

import pickle, os, operator, pprint

from scitools.numpyutils import seq, iseq, asarray, ones, zeros, sqrt, shape, \
     ravel, meshgrid, rank, squeeze, reshape, ndgrid, size
from scitools.numpytools import arrmin, arrmax, NumPyArray
from scitools.globaldata import backend

from misc import _check_xyz, _check_xyuv, _check_xyzuvw, _check_xyzv, \
     _check_size, _check_type, _toggle_state, _update_from_config_file

from warnings import warn


[docs]def docadd(comment, *lists, **kwargs): """ Format a string, intended to be appended to or inserted in a doc string, containing a comment and a nicely formatted sequence of lists. Typically used for adding lists of options to a doc string, where the lists of options are available as static list data in a class. Example on usage: # add to the class doc string: __doc__ += docadd('Keywords for the setp method', _local_attrs.keys()) # add to a method (get) doc string: get.__doc__ += docadd('Keywords for the setp method', BaseClass._local_attrs.keys(), SomeSubClass._local_attrs.keys()) """ lst = [] for l in lists: lst.extend(l) lst.sort() s = '\n' + comment + ':\n' s += ' ' + pprint.pformat(lst)[1:-1] # strip off leading [ and trailing ] # add indent: indent = kwargs.get('indent', 4) indent = ' '*indent lines = s.split('\n') for i in range(2,len(lines)): # skip first 2 lines (heading) if lines[i].strip() != '': lines[i] = indent + '- ' + lines[i] # add - for list syntax in some markup languages s = '\n'.join(lines) return s
[docs]class MaterialProperties(object): """ Storage of various properties for a material on a PlotProperties object. """ _local_prop = { 'opacity': None, 'ambient': None, 'diffuse': None, 'specular': None, 'specularpower': None, } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys())
[docs] def __init__(self, **kwargs): self._prop = {} self._prop.update(self._local_prop) self.setp(**kwargs)
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def setp(self, **kwargs): for key in self._prop.keys(): if key in kwargs: _check_type(kwargs[key], key, (int,float)) self._prop[key] = float(kwargs[key])
[docs] def getp(self, name): try: return self._prop[name] except: raise KeyError('%s.getp: no parameter with name "%s"' % \ (self.__class__.__name__, name) )
[docs]class PlotProperties(object): """ Storage of various properties needed for plotting, such as line types, surface features, contour values, etc. Different subclasses (Line, Surface, Contours) are specialized for different kinds of plots. All properties are stored in the dictionary self._prop. """ _colors = "b g r m c y k w".split() _markers = "o + x * s d v ^ < > p h .".split() _linestyles = ": -. -- -".split() _sizes = "1 2 3 4 5 6 7 8 9".split() _styledoc = {'y': 'yellow', 'm': 'magenta', 'c': 'cyan', 'r': 'red', 'g': 'green', 'b': 'blue', 'w': 'white', 'k': 'black', '.': 'point', 'o': 'circle', 'x': 'x-mark', '+': 'plus', '*': 'star', 's': 'square', 'd': 'diamond', 'v': 'triangle (down)', '^': 'triangle (up)', '<': 'triangle (left)', '>': 'triangle (right)', 'p': 'pentagram', 'h': 'hexagram', '-': 'solid', ':': 'dotted', '-.':'dashdot', '--':'dashed', } __doc__ += 'Valid symbols::\n - Colors: %s\n - Markers: %s\n - Linestyles: %s\n - Sizes: %s\n - Styles:\n%s' % (_colors, _markers, _linestyles, _sizes, pprint.pformat(_styledoc)[1:-1]) _local_prop = { 'description': '', 'legend': '', 'xlim': (0,0), 'ylim': (0,0), 'zlim': (0,0), 'dims': (0,0,0), 'numberofpoints': 0, 'function': '', # the function that created this item 'linecolor': '', 'linewidth': '', 'linetype': '', 'linemarker': '', 'facecolor': '', 'edgecolor': '', 'pointsize': 1.0, 'material': None, 'memoryorder': 'yxz', # FIXME: this is deprecated and will be removed 'indexing': 'ij', # 'xy' is Cartesian indexing, 'ij' matrix indexing } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys())
[docs] def __init__(self, **kwargs): self._prop = {} self._prop.update(PlotProperties._local_prop) self._prop['material'] = MaterialProperties()
[docs] def __str__(self): props = {} for key in self._prop.keys(): prop = self._prop[key] if isinstance(prop, (list,tuple,NumPyArray)) and \ len(ravel(prop)) > 3: props[key] = '%s with shape %s' % (type(prop), shape(prop)) else: props[key] = self._prop[key] return pprint.pformat(props) # repr is maybe not smart since # >>> plot(...) # will then return Line, Surface, # etc which automatically gets printed. # Better to make a dump function # that one can call on the current figure f.ex. #def __repr__(self): # return self.__str__()
[docs] def dump(self): """Dump the parameters of this object.""" return str(self)
[docs] def setp(self, **kwargs): """ Set plot properties. The method adds the argument value to the self._prop (if the value is legal). """ if 'description' in kwargs: descr = kwargs['description'] self._prop['description'] = descr # descr is on the form 'mesh: 3D mesh' (say) self._prop['function'] = descr.split(':')[0] if 'legend' in kwargs: self._prop['legend'] = str(kwargs['legend']) if 'linewidth' in kwargs: _check_type(kwargs['linewidth'], 'linewidth', (float,int)) self._prop['linewidth'] = float(kwargs['linewidth']) if 'linecolor' in kwargs: color = kwargs['linecolor'] if isinstance(color, str) and color in self._colors: self._prop['linecolor'] = color elif isinstance(color, (list,tuple)) and len(color) == 3: self._prop['linecolor'] = color else: raise ValueError("linecolor must be '%s', not '%s'" % \ (self._colors, kwargs['linecolor'])) if 'linetype' in kwargs: if kwargs['linetype'] in self._linestyles: self._prop['linetype'] = kwargs['linetype'] else: raise ValueError("linetype must be '%s', not '%s'" % \ (self._linestyles, kwargs['linetype'])) if 'linemarker' in kwargs: if kwargs['linemarker'] in self._markers: self._prop['linemarker'] = kwargs['linemarker'] else: raise ValueError("linemarker must be '%s', not '%s'" % \ (self._markers, kwargs['linemarker'])) if 'facecolor' in kwargs: self._prop['facecolor'] = kwargs['facecolor'] if 'edgecolor' in kwargs: self._prop['edgecolor'] = kwargs['edgecolor'] if 'memoryorder' in kwargs: msg = "Keyword argument 'memoryorder' is deprecated and will be " \ "removed in the future. Please use the 'indexing' keyword " \ "argument instead." warn(msg, DeprecationWarning) if kwargs['memoryorder'] == 'xyz': self._prop['indexing'] = 'ij' self._prop['memoryorder'] = 'xyz' elif kwargs['memoryorder'] == 'yxz': self._prop['indexing'] = 'xy' self._prop['memoryorder'] = 'yxz' else: raise ValueError("memoryorder must be 'xyz' or 'yxz', not %s"\ % kwargs['memoryorder']) if 'indexing' in kwargs: if kwargs['indexing'] in ['xy', 'ij']: self._prop['indexing'] = kwargs['indexing'] else: raise ValueError("indexing must be 'xy' or 'ij', not '%s'" \ % kwargs['indexing']) # set material properties: self._prop['material'].setp(**kwargs)
[docs] def getp(self, prm_name=None): """ Return the value of the parameter with name prm_name. If the name is None, the dictionary with all parameters is returned. """ if prm_name == 'memoryorder': msg = "Keyword argument 'memoryorder' is deprecated and will be " \ "removed in the future. Please use the 'indexing' keyword " \ "argument instead." warn(msg, DeprecationWarning) if prm_name is None: return self._prop else: try: return self._prop[prm_name] except: raise KeyError('%s.getp: no parameter with name "%s"' % \ (self.__class__.__name__, prm_name))
[docs] def setformat(self, format): """ Extract the right values for color, linetype, marker, etc. given a Matlab-like format string for a curve (e.g., 'r-'). The extracted values are stored in self._prop (with keys like 'linecolor', 'linetype', etc.). Erroneous chars will be ignored. When there are multiple format characters for a property, the last one will count. """ if isinstance(format,str) and len(format) > 0: color = "" linetype = "" marker = "" linewidth = "" pointsize = "" # Notice that '--' and '-.' are before '-' in the _linestyles # alphabet. for item in self._linestyles: if item in format: linetype = item break for item in format: if item in self._colors: color = item elif item in self._markers: if item == '.': if ('.' in linetype) and (format.count('.') == 1): pass else: marker = item # same as '.' else: marker = item elif item in self._sizes: # this int describes pointsize or linewidth self._prop['pointsize'] = item self._prop['linewidth'] = item if color in self._colors or color == "": self._prop['linecolor'] = color else: print "Illegal line color choice, %s is not known" % color if linetype != "" or marker != "": if linetype in self._linestyles: self._prop['linetype'] = linetype elif linetype == "": self._prop['linetype'] = linetype # Since marker is known else: print "Illegal line style choice, %s is not known" % \ linetype if marker in self._markers: self._prop['linemarker'] = marker elif marker == "": self._prop['linemarker'] = marker # Since linetype is known else: print "Illegal line marker choice, %s is not known" % \ marker
[docs] def get_limits(self): """ Return limits on the x, y, and z axis: xmin, xmax, ymin, ymax, zmin, zmax. """ return self._prop['xlim']+self._prop['ylim']+self._prop['zlim']
def _set_lim(self, a, name, adj_step=0.03): try: amin = arrmin(a) amax = arrmax(a) except ValueError: amin = min(ravel(a)) amax = max(ravel(a)) if (amax - amin) == 0: #print 'empty %s-range [%g,%g], adjusting to [%g,%g]' % \ # (name[0], amin, amax, amin-adj_step, amax+adj_step) amin -= adj_step; amax += adj_step self._prop[name] = (amin,amax)
[docs]class Line(PlotProperties): """ Storage of information about lines in curve plots. """ _local_prop = { 'xdata': None, 'ydata': None, 'zdata': None, } __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Line._local_prop) self.setp(**kwargs)
[docs] def setp(self, **kwargs): """ Set line properties. Legal keyword arguments: x, y, format. The x and y arguments hold the x and y points of a curve. The format string is just passed on to setformat, which parses the contents and sets the format information. """ PlotProperties.setp(self, **kwargs) # Here x,y values can be any SequenceType # The proper casting should be in the backends plotroutine if 'z' in kwargs: if not operator.isSequenceType(kwargs['z']): raise TypeError("Can only plot sequence types, "\ "z is %s" % type(kwargs['z'])) z = kwargs['z'] if 'format' in kwargs: self.setformat(kwargs['format']) if 'y' in kwargs: # will only set y variable if z is set if isinstance(kwargs['y'], basestring) \ and kwargs['y'] == 'auto': # now y is the indicies of z y = range(len(z)) else: if not operator.isSequenceType(kwargs['y']): raise TypeError("Can only plot sequence types, "\ "y is %s" % type(kwargs['y'])) y = kwargs['y'] if 'x' in kwargs: # will only set x variable if y is set if isinstance(kwargs['x'], basestring) \ and kwargs['x'] == 'auto': # now x is the indicies of y x = range(len(y)) else: if not operator.isSequenceType(kwargs['x']): raise TypeError("Can only plot sequence types, "\ "x is %s" % type(kwargs['x'])) x = kwargs['x'] # Consitency check assert size(x) == size(y), \ 'Line.setp: x has size %d, expected y to have size %d, ' \ 'not %d' % (size(x),size(x),size(y)) assert size(x) == size(z), \ 'Line.setp: x has size %d, expected z to have size %d, ' \ 'not %d' % (size(x),size(x),size(z)) self._set_data(x, y, z) elif 'y' in kwargs: if not operator.isSequenceType(kwargs['y']): raise TypeError("Can only plot sequence types, "\ "y is %s" % type(kwargs['y'])) y = kwargs['y'] if 'format' in kwargs: self.setformat(kwargs['format']) if 'x' in kwargs: # will only set x variable if y is set if isinstance(kwargs['x'], basestring) \ and kwargs['x'] == 'auto': # now x is the indicies of y x = range(len(y)) else: if not operator.isSequenceType(kwargs['x']): raise TypeError("Can only plot sequence types, "\ "x is %s" % type(kwargs['x'])) x = kwargs['x'] # Consitency check assert size(x) == size(y), \ 'Line.setp: x has size %d, expected y to have size %d, ' \ 'not %d.' % (size(x),size(x),size(y)) self._set_data(x, y)
def _set_data(self, x, y, z=None): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['dims'] = (len(x), 1, 1) self._prop['numberofpoints'] = len(x) if z is not None: self._set_lim(z, 'zlim') self._prop['zdata'] = z self._prop['dims'] = (len(x), len(y), 1)
[docs]class Bars(PlotProperties): """ Properties of bars in bar graphs. """ _local_prop = { 'xdata': None, 'ydata': None, 'barwidth': 0.8, 'barstepsize': 1.0, 'barticks': None, 'rotated_barticks': False, } __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Bars._local_prop) self.setp(**kwargs) self._parseargs(*args)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) if 'barwidth' in kwargs: self._prop['barwidth'] = float(kwargs['barwidth']) if 'barstepsize' in kwargs: self._prop['barstepsize'] = float(kwargs['barstepsize']) if 'barticks' in kwargs: self._prop['barticks'] = kwargs['barticks'] if 'rotated_barticks' in kwargs: self._prop['rotated_barticks'] = bool(kwargs['rotated_barticks'])
def _parseargs(self, *args): # allow both bar(...,LineSpec,width) and bar(...,width,LineSpec): for i in range(2): arg = args[-1] if isinstance(arg, str) and arg in self._colors: self._prop['linecolor'] = arg args = args[:-1] elif isinstance(arg, (float,int)): self._prop['barwidth'] = float(arg) args = args[:-1] nargs = len(args) if nargs == 2: # bar(x,Y) x = args[0] y = args[1] self._prop['barticks'] = x elif nargs == 1: # bar(Y) y = args[0] if isinstance(y, dict): a = [] keys = y.keys() keys.sort() for key in keys: a.append(y[key].values()) self._prop['barticks'] = keys y = asarray(a) x = range(len(y)) else: raise TypeError("Bars._parseargs: wrong number of arguments") self._set_data(x, y) def _set_data(self, x, y): if x is None: pass self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') self._prop['xdata'] = asarray(x) self._prop['ydata'] = asarray(y) n = len(x) self._prop['dims'] = (n, 1, 1) self._prop['numberofpoints'] = n
[docs]class Surface(PlotProperties): """ Properties of surfaces in scalar field plots. """ _local_prop = { 'cdata': None, 'wireframe': True, 'contours': None, 'xdata': None, 'ydata': None, 'zdata': None, } __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Surface._local_prop) self.setp(**kwargs) self._parseargs(*args) if self._prop['function'] in ['meshc', 'surfc']: self._prop['contours'] = Contours(self._prop['xdata'], self._prop['ydata'], self._prop['zdata'], **kwargs)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) if 'wireframe' in kwargs: self._prop['wireframe'] = _toggle_state(kwargs['wireframe'])
def _parseargs(self, *args): kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 3 and nargs <= 4: # mesh(X,Y,Z) or mesh(x,y,Z) x, y, z = _check_xyz(*args[:3], **kwargs) elif nargs >= 1 and nargs <= 2: # mesh(Z) x, y, z = _check_xyz(args[0], indexing=kwargs['indexing']) else: raise TypeError("Surface._parseargs: wrong number of arguments") if nargs == 2 or nargs == 4: # mesh(...,C) self._prop['cdata'] = args[-1] self._set_data(x, y, z) def _set_data(self, x, y, z): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') self._set_lim(z, 'zlim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['zdata'] = z nx, ny = shape(z) self._prop['dims'] = (nx, ny, 1) self._prop['numberofpoints'] = nx*ny if not 'mesh' in self._prop['function']: self._prop['wireframe'] = False
[docs]class Contours(PlotProperties): """ Information about contours for plot of scalar fields. """ _local_prop = { 'cvector': None, # vector of contour heights 'clevels': 8, # default number of contour levels 'clabels': False, # display contour labels 'clocation': 'base', # location of cntr levels (surface or base) 'filled': False, # fill contours 'xdata': None, 'ydata': None, 'zdata': None, } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Contours._local_prop) self.setp(**kwargs) self._parseargs(*args)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) if 'cvector' in kwargs: _check_type(kwargs['cvector'], 'cvector', (tuple,list,NumPyArray)) self._prop['cvector'] = kwargs['cvector'] self._prop['clevels'] = len(kwargs['cvector']) if 'clevels' in kwargs: clevels = kwargs['clevels'] _check_type(clevels, 'clevels', int) cvector = self._prop['cvector'] if cvector is not None and clevels > len(cvector): clevels = len(cvector) self._prop['clevels'] = clevels if 'clabels' in kwargs: self._prop['clabels'] = _toggle_state(kwargs['clabels'])
def _parseargs(self, *args): if isinstance(args[-1], str): # contour(...,LineSpec) self.setformat(args[-1]); args = args[:-1] kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 3 and nargs <= 4: x, y, z = _check_xyz(*args[:3], **kwargs) elif nargs >= 1: x, y, z = _check_xyz(args[0], indexing=kwargs['indexing']) else: raise TypeError("Contours._parseargs: wrong number of arguments") if nargs == 2 or nargs == 4: tmp = args[-1] if operator.isSequenceType(tmp): self._prop['cvector'] = tmp self._prop['clevels'] = len(tmp) elif isinstance(tmp, int): self._prop['clevels'] = tmp else: raise TypeError( "Contours._parseargs: expected array or integer for " \ " argument %d, not %s" % (nargs, type(tmp))) self._set_data(x, y, z) def _set_data(self, x, y, z): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') self._set_lim(z, 'zlim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['zdata'] = z nx, ny = shape(z) self._prop['dims'] = (nx, ny, 1) self._prop['numberofpoints'] = len(ravel(z)) if self._prop['function'] == 'contour3': self._prop['clocation'] = 'surface' elif self._prop['function'] == 'contourf': self._prop['filled'] = True
[docs]class VelocityVectors(PlotProperties): """ Information about velocity vectors in a vector plot. """ _local_prop = { 'arrowscale': 1.0, 'filledarrows': False, 'xdata': None, 'ydata': None, 'zdata': None, # grid components 'udata': None, 'vdata': None, 'wdata': None, # vector components } __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(VelocityVectors._local_prop) self.setp(**kwargs) self._parseargs(*args)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) if 'arrowscale' in kwargs: _check_type(kwargs['arrowscale'], 'arrowscale', (int,float)) self._prop['arrowscale'] = float(kwargs['arrowscale']) if 'filledarrows' in kwargs: self._prop['filledarrows'] = _toggle_state(kwargs['filledarrows'])
def _parseargs(self, *args): # allow both quiver(...,LineSpec,'filled') and quiver(...,'filled',LS): for i in range(2): if isinstance(args[-1], str): if args[-1] == 'filled': self._prop['filledarrows'] = True; args = args[:-1] else: self.setformat(args[-1]); args = args[:-1] z, w = [None]*2 func = self._prop['function'] kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 6 and nargs <= 7: # quiver3(X,Y,Z,U,V,W) x, y, z, u, v, w = _check_xyzuvw(*args[:6], **kwargs) elif nargs >= 4 and nargs <= 5: # quiver(X,Y,U,V) or quiver3(Z,U,V,W) if func == 'quiver3': x, y, z, u, v, w = _check_xyzuvw(*args[:4], **kwargs) else: x, y, u, v = _check_xyuv(*args[:4], **kwargs) elif func == 'quiver' and nargs >= 2 and nargs <= 3: # quiver(U,V) x, y, u, v = _check_xyuv(*args[:2], **kwargs) else: raise TypeError( "VelocityVectors._parseargs: wrong number of arguments") if (func == 'quiver3' and nargs in (5,7)) or \ (func == 'quiver' and nargs in (3,5)): # quiver?(...,arrowscale) _check_type(args[-1], 'arrowscale', (float,int)) self._prop['arrowscale'] = float(args[-1]) self._set_data(x, y, z, u, v, w)
[docs] def scale_vectors(self): as_ = self._prop['arrowscale'] if as_: u = self._prop['udata'] v = self._prop['vdata'] w = self._prop['wdata'] dims = self._prop['dims'] xmin, xmax, ymin, ymax, zmin, zmax = self.get_limits() dx = (xmax - xmin)/dims[1] dy = (ymax - ymin)/dims[0] d = dx**2 + dy**2 if w is not None: dz = (zmax - zmin)/max(dims[0],dims[1]) d += dx**2 if d > 0: if w is not None: length = sqrt((u/d)**2 + (v/d)**2 + (w/d)**2) else: length = sqrt((u/d)**2 + (v/d)**2) maxlen = max(length.flat) else: maxlen = 0 if maxlen > 0: as_ = as_*0.9/maxlen else: as_ = as_*0.9 self._prop['udata'] = u*as_ self._prop['vdata'] = v*as_ if w is not None: self._prop['wdata'] = w*as_
def _set_data(self, x, y, z, u, v, w): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') if z is not None: self._set_lim(z, 'zlim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['zdata'] = z self._prop['udata'] = u self._prop['vdata'] = v self._prop['wdata'] = w if rank(u) == 1: self._prop['dims'] = (len(u), 1, 1) elif rank(u) == 2: nx, ny = shape(u) self._prop['dims'] = (nx, ny, 1) else: self._prop['dims'] = u.shape self._prop['numberofpoints'] = len(ravel(u))
[docs]class Streams(PlotProperties): """ Information about stream lines, stream tubes, and similar vector field visualization techniques. """ _local_prop = { 'stepsize': 0.1, 'numberofstreams': 0, 'tubes': False, 'tubescale': 1.0, 'n': 20, # number of points along the circumference of the tube 'ribbons': False, 'ribbonwidth': 0.5, 'xdata': None, 'ydata': None, 'zdata': None, # grid components 'udata': None, 'vdata': None, 'wdata': None, # vector components 'startx': None, 'starty': None, 'startz': None, # starting points } __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Streams._local_prop) self.setp(**kwargs) self._parseargs(*args)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) for key in 'stepsize tubescale ribbonwidth'.split(): if key in kwargs: _check_type(kwargs[key], key, (float,int)) self._prop[key] = float(kwargs[key]) # set whether we should use lines, tubes, or ribbons: func = self._prop['function'] self._prop['tubes'] = func == 'streamtube' self._prop['ribbons'] = func == 'streamribbon'
def _parseargs(self, *args): # TODO: do more error checking and add support for indexing='ij'. z, w, sz, option = [None]*4 #kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 9 and nargs <= 10: x, y, z, u, v, w, sx, sy, sz = [asarray(a) for a in args[:9]] #x, y, z, u, v, w = _check_xyzuvw(*args[:6]) #x, y, z = [asarray(a) for a in args[:3]] #_check_xyz(*args[:3]) #u, v, w = [asarray(a) for a in args[3:6]] #sx, sy, sz = [asarray(a) for a in args[6:9]] elif nargs >= 6 and nargs <= 7: u, v = [asarray(a) for a in args[:2]] if rank(u) == 3: # streamline(U,V,W,startx,starty,startz) nx, ny, nz = shape(u) x, y, z = ndgrid(seq(nx-1), seq(ny-1), seq(nz-1)) #w = asarray(args[2]) w, sx, sy, sz = [asarray(a) for a in args[2:6]] else: # streamline(X,Y,U,V,startx,starty) x = u; y = v #u, v = [asarray(a) for a in args[2:4]] u, v, sx, sy = [asarray(a) for a in args[2:6]] elif nargs >= 4 and nargs <= 5: # streamline(U,V,startx,starty) u, v = [asarray(a) for a in args[:2]] try: nx, ny = shape(u) except: raise ValueError("u must be 2D, not %dD" % rank(u)) x, y = ndgrid(seq(nx-1), seq(ny-1)) sx, sy = [asarray(a) for a in args[2:4]] elif nargs >= 1 and nargs <= 2: # streamline(XYZ) or streamline(XY) raise NotImplementedError('Streams._parseargs: not implemented') else: raise TypeError('wrong number of arguments') if nargs in (5,7,10): # streamline(...,options) func = self._prop['function'] options = args[-1] if isinstance(options, (tuple,list)) and len(options) in (1,2): if func == 'streamtube': self._prop['tubescale'] = float(options[0]) else: self._prop['stepsize'] = float(options[0]) if len(options) == 2: if func == 'streamtube': self._prop['n'] = int(options[1]) else: maxverts = float(options[1]) elif isinstance(options, (float,int)): if func == 'streamtube': self._prop['tubescale'] = float(options) elif func == 'streamribbon': self._prop['ribbonwidth'] = float(options) else: self._prop['stepsize'] = float(options) else: msg = "options must be [stepsize[,maxverts]], not '%s'" % \ options if func == 'streamtube': msg = "options must be [scale[,n]], not '%s'" % options elif func == 'streamribbon': msg = "options must be a [width], not %s" % options raise ValueError(msg) ## if len(u.shape) == 3: ## assert shape(x) == shape(y) == shape(z) == \ ## shape(u) == shape(v) == shape(w), \ ## "x, y, z, u, v, and z must be 3D arrays and of same shape" ## assert shape(sx) == shape(sy) == shape(sz), \ ## "startx, starty, and startz must all be of same shape" ## else: ## assert shape(x) == shape(y) == shape(u) == shape(v), \ ## "x, y, u, and v must be 2D arrays and of same shape" ## assert shape(sx) == shape(sy), \ ## "startx and starty must be of same shape" z = w = zeros(shape(u)) sz = zeros(shape(sx)) self._set_data(x, y, z, u, v, w, sx, sy, sz) def _set_data(self, x, y, z, u, v, w, sx, sy, sz): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') if z is not None: self._set_lim(z, 'zlim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['zdata'] = z self._prop['udata'] = u self._prop['vdata'] = v self._prop['wdata'] = w self._prop['startx'] = sx self._prop['starty'] = sy self._prop['startz'] = sz if rank(u) == 2: nx, ny = shape(u) self._prop['dims'] = (nx, ny, 1) else: self._prop['dims'] = shape(u) self._prop['numberofpoints'] = len(ravel(u)) self._prop['numberofstreams'] = len(ravel(sx))
[docs]class Volume(PlotProperties): """ Information about volume visualization techniques. """ _local_prop = { 'slices': None, 'isovalue': None, 'clevels': 5, # default number of contour lines per plane 'cvector': None, 'xdata': None, 'ydata': None, 'zdata': None, # grid components 'vdata': None, # data values at grid points 'cdata': None, # pseudocolor data } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', PlotProperties._local_prop.keys(), _local_prop.keys())
[docs] def __init__(self, *args, **kwargs): PlotProperties.__init__(self, **kwargs) self._prop.update(Volume._local_prop) self.setp(**kwargs) self._parseargs(*args)
[docs] def setp(self, **kwargs): PlotProperties.setp(self, **kwargs) if 'isovalue' in kwargs: _check_type(kwargs['isovalue'], 'isovalue', (float,int)) self._prop['isovalue'] = float(kwargs['isovalue']) if 'clevels' in kwargs: clevels = kwargs['clevels'] _check_type(kwargs['clevels'], 'clevels', int) cvector = self._prop['cvector'] if cvector is not None and clevels > len(cvector): clevels = len(cvector) self._prop['clevels'] = clevels if 'cvector' in kwargs: _check_type(kwargs['cvector'], 'cvector', (tuple,list)) self._prop['cvector'] = kwargs['cvector'] self._prop['clevels'] = len(kwargs['cvector'])
def _parseargs(self, *args): func = self._prop['function'] if func in ['slice_', 'contourslice']: self._parseargs_slice_(*args) elif func == 'isosurface': self._parseargs_isosurface(*args) def _parseargs_slice_(self, *args): # this method also works for contourslice kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 7 and nargs <= 8: # slice_(X,Y,Z,V,Sx,Sy,Sz) or slice_(X,Y,Z,V,XI,YI,ZI) x, y, z, v = _check_xyzv(*args[:4], **kwargs) slices = [asarray(a) for a in args[4:7]] elif nargs >= 4 and nargs <= 5: # slice_(V,Sx,Sy,Sz) or slice_(V,XI,YI,ZI) x, y, z, v = _check_xyzv(args[0], indexing=kwargs['indexing']) slices = [asarray(a) for a in args[1:4]] else: raise TypeError("Wrong number of arguments") if nargs == 5 or nargs == 8: func = self._prop['function'] tmparg = args[-1] if func == 'slice_': # slice_(...,'method') intrp_methods = 'linear cubic nearest'.split() method = str(tmparg) if not method in interp_methods: raise ValueError( 'interpolation method must be %s, not %s' % \ (interp_methods, method)) #self._prop['interpolationmethod'] = method elif func == 'contourslice': # contourslice(..., if isinstance(tmparg, int) and tmparg >= 0: self._prop['clevels'] = tmparg elif isinstance(tmparg, (list,tuple)): self._prop['cvector'] = tmparg self._prop['clevels'] = len(tmparg) self._set_data(x, y, z, v, slices=slices) def _parseargs_isosurface(self, *args): kwargs = {'indexing': self._prop['indexing']} nargs = len(args) if nargs >= 5 and nargs <= 6: # isosurface(X,Y,Z,V,isovalue) x, y, z, v = _check_xyzv(*args[:4], **kwargs) isovalue = float(args[4]) elif nargs >= 2 and nargs <= 3: # isosurface(V,isovalue) x, y, z, v = _check_xyzv(args[0], indexing=kwargs['indexing']) isovalue = float(args[1]) else: raise TypeError("Wrong number of arguments") if nargs in (3,6): # isosurface(...,COLORS) cdata = asarray(args[-1]) assert v.shape == cdata.shape, \ "COLORS must have shape %s (as V), not %s" % \ (v.shape, cdata.shape) self._prop['cdata'] = cdata self._set_data(x, y, z, v, isovalue=isovalue) def _set_data(self, x, y, z, v, slices=None, isovalue=None): self._set_lim(x, 'xlim') self._set_lim(y, 'ylim') self._set_lim(z, 'zlim') self._prop['xdata'] = x self._prop['ydata'] = y self._prop['zdata'] = z self._prop['vdata'] = v if slices: self._prop['slices'] = slices if isovalue is not None: self._prop['isovalue'] = isovalue self._prop['dims'] = v.shape self._prop['numberofpoints'] = len(ravel(v))
[docs]class Colorbar(object): """ Information about color bars in color plots. """ _local_prop = { 'cblocation': 'EastOutside', 'cbtitle': '', 'visible': False, } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys()) _locations = 'North South East West NorthOutside SouthOutside ' \ 'EastOutside WestOutside'.split() __doc__ += docadd('Legal values for color bar location', _locations)
[docs] def __init__(self, **kwargs): self._prop = {} self._prop.update(Colorbar._local_prop) self._defaults = self._prop.copy() self.setp(**kwargs)
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def setp(self, **kwargs): if 'cblocation' in kwargs: if kwargs['cblocation'] in self._locations: self._prop['cblocation'] = kwargs['cblocation'] else: print "colorbar location must be one of %s, not %s" % \ (self._locations, kwargs['cblocation']) if 'cbtitle' in kwargs: self._prop['cbtitle'] = str(kwargs['cbtitle']) if 'visible' in kwargs: self._prop['visible'] = _toggle_state(kwargs['visible'])
[docs] def getp(self, prm_name): try: return self._prop[prm_name] except: raise KeyError("%s.getp: no parameter with name '%s'" % \ (self.__class__.__name__, prm_name))
[docs] def reset(self): self._prop = self._defaults.copy()
[docs]class Light(object): """ Information about light in a visualization. """ _local_prop = { 'lightcolor': (1,1,1), 'lightpos': (1,0,1), 'lighttarget': (0,0,0), 'intensity': 1, 'visible': True, } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys())
[docs] def __init__(self, **kwargs): self._prop = {} self._prop.update(Light._local_prop) self._defaults = self._prop.copy() self.setp(**kwargs)
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def setp(self, **kwargs): if 'lightcolor' in kwargs: color = kwargs['lightcolor'] _check_type(color, 'lightcolor', (list,tuple)) _check_size(color, 'lightcolor', 3) for i in range(3): _check_type(color[i], 'lightcolor', (float,int)) self._prop['lightcolor'] = color if 'lightpos' in kwargs: self._prop['lightpos'] = kwargs['lightpos'] if 'lighttarget' in kwargs: self._prop['lighttarget'] = kwargs['lighttarget'] if 'itensity' in kwargs: self._prop['itensity'] = kwargs['itensity'] if 'visible' in kwargs: self._prop['visible'] = _toggle_state(kwargs['visible'])
[docs] def getp(self, name): try: return self._prop[name] except: raise KeyError("%s.getp: no parameter with name '%s'." % \ (self.__class__.__name__, name))
[docs] def reset(self): self._prop = self._defaults.copy()
[docs]class Camera(object): """ Information about the camera in a visualization. """ _local_prop = { 'cammode': 'auto', 'camtarget': (0,0,0), 'camva': None, # view angle 'azimuth': None, 'elevation': None, 'view': 2, 'camup': (0,1,0), 'camdolly': (0,0,0), 'camroll': None, # angle 'camzoom': 1, 'campos': (0,0,0), 'camproj': 'orthographic' } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys()) _modes = ['auto', 'manual'] _camprojs = ['orthographic', 'perspective'] __doc__ += docadd('Legal values for the mode keyword', _modes) __doc__ += docadd('Legal values for the camproj keyword', _camprojs)
[docs] def __init__(self, axis, **kwargs): self._prop = {} self._prop.update(Camera._local_prop) self._ax = axis self._defaults = self._prop.copy() self.setp(**kwargs)
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def setp(self, **kwargs): if 'cammode' in kwargs: if kwargs['cammode'] in self._modes: self._prop['cammode'] = kwargs['cammode'] else: raise ValueError("Camera.setp: cammode must be %s, not %s" % \ (self._modes, kwargs['cammode'])) if 'view' in kwargs: view = kwargs['view'] if view in (2,3): self._set_default_view(view) elif isinstance(view, (tuple,list)) and len(view) == 2: self._prop['azimuth'], self._prop['elevation'] = view else: raise ValueError( "Camera.setp: view must be either [az,el], 2, or 3.") if 'camproj' in kwargs: if kwargs['camproj'] in self._camprojs: self._prop['camproj'] = kwargs['camproj'] else: raise ValueError("projection must one of %s, not %s" % \ (self._camprojs, kwargs['camproj'])) for prop in 'camzoom camva camroll'.split(): if prop in kwargs: _check_type(kwargs[prop], prop, (int,float)) self._prop[prop] = float(kwargs[prop]) if 'azimuth' in kwargs: _check_type(kwargs['azimuth'], 'azimuth', (int,float)) self._prop['azimuth'] = float(kwargs['azimuth']) if self._prop['elevation'] is None: self._prop['elevation'] = 0 if 'elevation' in kwargs: _check_type(kwargs['elevation'], 'elevation', (int,float)) self._prop['elevation'] = float(kwargs['elevation']) if self._prop['azimuth'] is None: self._prop['azimuth'] = 0 if self._prop['azimuth'] is not None: # elevation is also != None self._prop['view'] = 3 self._prop['camup'] = (0,0,1) for prop in 'camtarget campos camup camdolly'.split(): if prop in kwargs: _check_type(kwargs[prop], prop, (list,tuple)) _check_size(kwargs[prop], prop, 3) self._prop[prop] = kwargs[prop]
[docs] def getp(self, name): try: return self._prop[name] except: raise KeyError("%s.getp: no parameter with name '%s'." % \ (self.__class__.__name__, name))
[docs] def reset(self): """Reset camera to defaults.""" self._prop = self._defaults.copy()
def _set_default_view(self, view): self.reset() self._prop['view'] = view self._prop['camtarget'] = self._ax.getp('center') if self._prop['view'] == 3: self._prop['camup'] = (0,0,1)
[docs]class Axis(object): """ Information about the axis in curve, surface, and volume plots. """ _local_prop = { 'plotitems': [], 'numberofitems': 0, 'mode': 'auto', 'method': 'normal', 'direction': 'xy', 'hold': False, 'hidden': True, 'box': False, 'grid': False, 'camera': None, 'cameramode': 'auto', 'lights': [], 'colorbar': None, 'colormap': None, 'caxis': [None]*2, 'caxismode': 'auto', 'axiscolor': (0,0,0), 'bgcolor': (1,1,1), # background color 'fgcolor': (0,0,0), # foreground color 'fontname': 'Helvetica', 'fontsize': 12, 'shading': 'faceted', 'scale': 'linear', 'xmin': None, 'xmax': None, 'ymin': None, 'ymax': None, 'zmin': None, 'zmax': None, 'xlim': [None]*2, 'ylim': [None]*2, 'zlim': [None]*2, 'title': '', 'xlabel': '', 'ylabel': '', 'zlabel': '', 'center': (0,0,0), 'daspect': [None]*3, 'daspectmode': 'auto', 'visible': True, 'viewport': None, # viewport coords (list with 4 elm, backend specific) 'ambientcolor': None, 'diffusecolor': None, 'speculartcolor': None, 'pth': None, # this is the p-th axis in subplot(m,n,p) 'colororder': 'b g r c m y k'.split(), 'curcolor': 0, } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys()) _directions = "ij xy".split() _methods = "equal image square normal vis3d".split() _modes = "auto manual tight fill".split() _ranges = "xmin xmax ymin ymax zmin zmax".split() _shadings = "flat interp faceted".split() _legend_locs = {'upper right': 1, 'upper left': 2, 'lower left': 3, 'lower right': 4, 'center left': 6, 'center right': 7, 'lower center': 8, 'upper center': 9, 'best': 0, 'right': 5, 'center': 10} __doc__ += docadd('Legal values for direction keyword', _directions) __doc__ += docadd('Legal values for method keyword', _methods) __doc__ += docadd('Legal values for mode keyword', _modes) __doc__ += docadd('Legal values for range keyword', _ranges) __doc__ += docadd('Legal values for shading keyword', _shadings) __doc__ += docadd('Legal values for legend locations', list(_legend_locs.keys()))
[docs] def __init__(self, *args, **kwargs): self._prop = {} self._prop.update(Axis._local_prop) self._defaults = self._prop.copy() # self was not available when _local_prop was defined: self._prop['camera'] = Camera(self) self._prop['colorbar'] = Colorbar()
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def dump(self): """Dump the parameters of this object.""" return str(self)
[docs] def setp(self, **kwargs): if 'mode' in kwargs: mode = kwargs['mode'] if mode in self._modes: self._prop['mode'] = mode if mode in ['auto', 'tight']: # clear the current axis ranges: for r in self._ranges: self._prop[r] = None else: raise ValueError("Axis.setp: mode must be %s, not %s" % \ (self._modes, mode)) if 'method' in kwargs: if kwargs['method'] in self._methods: self._prop['method'] = kwargs['method'] else: raise ValueError("Axis.setp: method must be %s, not %s" % \ (self._methods, kwargs['method'])) if 'direction' in kwargs: if kwargs['direction'] in self._directions: self._prop['direction'] = kwargs['direction'] else: raise ValueError("Axis.setp: direction must be %s, not %s" % \ (self._directions, kwargs['direction'])) for key in 'hold hidden box grid'.split(): if key in kwargs: self._toggle_state(key, kwargs[key]) del kwargs[key] if 'colorbar' in kwargs: if isinstance(kwargs['colorbar'], Colorbar): self._prop['colorbar'] = kwargs['colorbar'] else: self._prop['colorbar'].setp(visible=kwargs['colorbar']) if 'colormap' in kwargs: self._prop['colormap'] = kwargs['colormap'] # backend dependent if 'caxis' in kwargs: ca = kwargs['caxis'] if isinstance(ca, (tuple,list)) and len(ca) == 2: _check_type(ca[0], 'cmin', (int,float)) _check_type(ca[1], 'cmax', (int,float)) self._prop['caxis'] = ca self._prop['caxismode'] = 'manual' else: raise ValueError("%s.setp: caxis must be a two element vector" \ " [cmin,cmax], not '%s'." \ % (self.__class__.__name__,ca)) if 'caxismode' in kwargs: mode = kwargs['caxismode'] if kwargs['caxismode'] in self._modes: self._prop['caxismode'] = mode if mode == 'auto': self._prop['caxis'] = [None]*2 elif mode == 'manual': if None in self._prop['caxis']: zmin, zmax = self._prop['zlim'] if zmin is None or zmax is not None: self._prop['caxis'] = (zmin, zmax) else: self._prop['caxis'] = (0,1) else: raise ValueError("Axis.setp: caxismode must be %s, not %s" \ (self._modes, mode)) if 'shading' in kwargs: if kwargs['shading'] in self._shadings: self._prop['shading'] = kwargs['shading'] #self._update_shading() else: raise ValueError("Axis.setp: '%s' not a valid shading mode" \ % kwargs['shading']) if 'light' in kwargs: if isinstance(kwargs['light'], Light): self._prop['lights'].append(kwargs['light']) else: raise ValueError("Axis.setp: light must be %s, not %s" % \ (type(Light), type(self._prop['light']))) # Set scale if 'log' in kwargs: if kwargs['log'] == 'x': self._prop['scale'] = 'logx' elif kwargs['log'] == 'y': self._prop['scale'] = 'logy' elif kwargs['log'] == 'xy': self._prop['scale'] = 'loglog' # Note: The only way to reset scale to linear after log is to # use plot(args,log=None) elif kwargs['log'] == None: self._prop['scale'] = 'linear' for key in self._ranges: if key in kwargs and isinstance(kwargs[key], (float,int)): self._prop[key] = kwargs[key] for key in 'xlim ylim zlim'.split(): if key in kwargs and isinstance(kwargs[key], (tuple,list)): _check_size(kwargs[key], key, 2) self._prop[key[0]+'min'] = kwargs[key][0] self._prop[key[0]+'max'] = kwargs[key][1] if 'axis' in kwargs: axis = kwargs['axis'] if isinstance(axis, (tuple,list)): n = len(axis) if n in (4,6): for i in range(n): _check_type(axis[i], self._ranges[i], (float,int)) self._prop[self._ranges[i]] = axis[i] elif axis in ['on', 'off']: #, None, True, False]: self._prop['visible'] = _toggle_state(axis) elif axis in self._methods: self._prop['method'] = axis elif axis in self._modes: self._prop['mode'] = axis elif axis in self._directions: self._prop['direction'] = axis else: raise ValueError("not a valid axis specification") for key in 'title xlabel ylabel zlabel'.split(): if key in kwargs and isinstance(kwargs[key], str): self._prop[key] = kwargs[key] if 'daspect' in kwargs: daspect = kwargs['daspect'] if isinstance(daspect, (int,float)): daspect = [daspect]*3 _check_type(daspect, 'daspect', (tuple,list)) _check_size(daspect, 'daspect', 3) self._prop['daspect'] = [float(elm) for elm in daspect] self._prop['daspectmode'] = 'manual' if 'daspectmode' in kwargs: self._prop['daspectmode'] = kwargs['daspectmode'] if 'fgcolor' in kwargs: self._prop['fgcolor'] = kwargs['fgcolor'] if 'bgcolor' in kwargs: self._prop['bgcolor'] = kwargs['bgcolor'] if 'visible' in kwargs: self._toggle_state('visible', kwargs['visible']) del kwargs['visible'] if 'viewport' in kwargs: viewport = kwargs['viewport'] _check_type(viewport, 'viewport', (list,tuple)) _check_size(viewport, 'viewport', 4) #for i in range(4): # _check_type(viewport[i], 'viewport coor', (int,float)) self._prop['viewport'] = viewport if 'pth' in kwargs: pth = kwargs['pth'] _check_type(pth, 'pth', int) self._prop['pth'] = pth if 'legend_loc' in kwargs: self._prop['legend_loc'] = kwargs['legend_loc'] if 'legend_fancybox' in kwargs: self._prop['legend_fancybox'] = kwargs['legend_fancybox'] # set properties for camera and colorbar: self._prop['camera'].setp(**kwargs) self._prop['colorbar'].setp(**kwargs) # update the axis: self.update()
[docs] def getp(self, name): """Return parameter with name 'name'.""" try: return self._prop[name] except: raise KeyError("%s.getp: no parameter with name '%s'" % \ (self.__class__.__name__, name))
[docs] def get_next_color(self): """Return the next color defined in the 'colororder' property.""" colors = self._prop['colororder'] if self._prop['curcolor'] == len(colors): self._prop['curcolor'] = 0 curcolor = colors[self._prop['curcolor']] self._prop['curcolor'] += 1 return curcolor
[docs] def reset(self): """Reset axis attributes to default values.""" viewport = self._prop['viewport'] # don't reset viewport coords pth = self._prop['pth'] # don't reset p-th axis information self._prop = self._defaults.copy() self._prop['viewport'] = viewport self._prop['pth'] = pth self._prop['plotitems'] = [] #self._prop['camera'].reset() del self._prop['camera'] self._prop['camera'] = Camera(self) #for l in self._prop['lights']: # l.reset() self._prop['lights'] = [] #self._prop['colorbar'].reset() self._prop['colorbar'] = Colorbar()
[docs] def add(self, items): """Add all items in 'items' to this axis.""" if not self._prop['hold']: self.reset() if not isinstance(items, (tuple,list)): items = (items,) for item in items: if not isinstance(item, PlotProperties): raise ValueError("item must be %s (or a subclass), not %s" % \ (type(PlotProperties), type(item))) self._prop['plotitems'].append(item) self.update()
[docs] def get_limits(self): """Return axis limits.""" return self._prop['xlim']+self._prop['ylim']+self._prop['zlim']
[docs] def toggle(self, name): """Toggle axis parameter with name 'name'.""" if self._prop[name]: self._prop[name] = False else: self._prop[name] = True
[docs] def update(self): """Update axis.""" if len(self._prop['plotitems']) > self._prop['numberofitems']: self._prop['numberofitems'] = len(self._prop['plotitems']) for item in self._prop['plotitems']: self._update_limits(item) if len(self._prop['plotitems']) > 0: if None in self._prop['daspect'] or \ self._prop['daspectmode'] == 'auto': self._update_daspect() self._set_center() p = self._prop if (p['xmin'] is not None and p['xmax'] is not None) or \ (p['ymin'] is not None and p['ymax'] is not None) or \ (p['zmin'] is not None and p['zmax'] is not None): self._prop['mode'] = 'manual' method = self._prop['method'] if method == 'equal': self._prop['daspect'] = (1.,1.,1.) elif method == 'image': self._prop['daspect'] = (1.,1.,1.) self._prop['mode'] = 'tight' elif method == 'square': #print "axis method 'square' not implemented yet" pass elif method == 'normal': if None in self._prop['daspect'] or \ self._prop['daspectmode'] == 'auto': self._update_daspect()
def _toggle_state(self, name, state): self._prop[name] = _toggle_state(state) def _set_center(self): center = [None]*3 xmin, xmax, ymin, ymax, zmin, zmax = self.get_limits() center[0] = (xmax + xmin) / 2.0 center[1] = (ymax + ymin) / 2.0 center[2] = (zmax + zmin) / 2.0 self._prop['center'] = tuple(center) def _check_lim(self, l1, l2): """Return a tuple with the "larger" values of 'l1' and 'l2'.""" lim = [None]*2 if l1[0] > l2[0]: lim[0] = l2[0] else: lim[0] = l1[0] if l1[1] < l2[1]: lim[1] = l2[1] else: lim[1] = l1[1] return tuple(lim) def _update_limits(self, item): """ Update axis limits according to the PlotProperties given in 'item'. """ xlim = item.getp('xlim') ylim = item.getp('ylim') zlim = item.getp('zlim') if not None in self._prop['xlim']: xlim = self._check_lim(self._prop['xlim'], xlim) if not None in self._prop['ylim']: ylim = self._check_lim(self._prop['ylim'], ylim) if not None in self._prop['zlim']: zlim = self._check_lim(self._prop['zlim'], zlim) self._prop['xlim'] = xlim self._prop['ylim'] = ylim self._prop['zlim'] = zlim def _update_daspect(self): lim = list(self.get_limits()) xmin, xmax = self._prop['xmin'], self._prop['xmax'] ymin, ymax = self._prop['ymin'], self._prop['ymax'] zmin, zmax = self._prop['zmin'], self._prop['zmax'] if not None in [xmin, xmax]: lim[0] = xmin lim[1] = xmax if not None in [ymin, ymax]: lim[2] = ymin lim[3] = ymax if not None in [zmin, zmax]: lim[4] = zmin lim[5] = zmax if self._prop['method'] == 'normal': if not None in lim: # limits are set sx = float(lim[1] - lim[0]) sy = float(lim[3] - lim[2]) sz = float(lim[5] - lim[4]) scale = max([sx,sy,sz]) if not scale > 0: scale = 1 if sz == 0: daspect = (sx/scale, sy/scale, 1.0) else: daspect = (sx/scale, sy/scale, sz/scale) self._prop['daspect'] = daspect
[docs]class Figure(object): """Hold figure attributtes like axes, size, ....""" _local_prop = { 'axes': None, # dictionary of axis instances 'curax': 1, # current axis 'axshape': (1,1), # shape of axes 'size': [None]*2, # size of figure ([width, height]) 'number': 1, # this figures number } _update_from_config_file(_local_prop) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_prop.keys())
[docs] def __init__(self, **kwargs): self._prop = {} self._prop.update(Figure._local_prop) # store a copy of the default values for use when figure is reset: self._defaults = self._prop.copy() self._prop['axes'] = {1: Axis()} self.setp(**kwargs)
[docs] def __str__(self): return pprint.pformat(self._prop)
[docs] def dump(self): """Dump the contents of the figure (all axes).""" s = '\nFigure object:\n' if self._prop['size'] is not None: s += pprint.pformat(self._prop['size']) + '\n' for ax in self._prop['axes']: s += 'axis %d:\n' % ax #s += pprint.pformat(str(self._prop['axes'][ax])) s += pprint.pformat(self._prop['axes'][ax]._prop) return s
[docs] def gca(self): """Return current axis.""" return self._prop['axes'][self._prop['curax']]
[docs] def reset(self): """Reset figure attributes and backend to defaults.""" self._prop = self._defaults.copy() self._prop['axes'] = {1: Axis()} self._prop['axes'][1].reset()
[docs] def setp(self, **kwargs): if 'axshape' in kwargs: shape = kwargs['axshape'] _check_type(shape, 'axshape', (tuple,list)) _check_size(shape, 'axshape', 2) _check_type(shape[0], 'm', int) _check_type(shape[1], 'n', int) self._prop['axshape'] = shape dx = 1./shape[1]; dy = 1./shape[0] last_x = 0; last_y = 1-dy viewport_coords = [] for y in seq(dy,1,dy): for x in seq(dx,1,dx): if backend.startswith('vtk'): viewport_coords.append((last_x,last_y,x,last_y+dy)) else: viewport_coords.append((last_x,last_y,x,y)) last_x = x last_x = 0 last_y = last_y - dy self._prop['axes'] = {} for i in iseq(1,len(viewport_coords)): ax = Axis() ax.setp(viewport=viewport_coords[i-1]) self._prop['axes'][i] = ax ax.setp(pth=i) if 'curax' in kwargs: curax = kwargs['curax'] #_check_type(curax, 'curax', int) self._set_current_axis(curax) if 'size' in kwargs: size = kwargs['size'] # size should be a list/tuple with two elements [width, height] _check_type(size, 'size', (list,tuple)) _check_size(size, 'size', 2) self._prop['size'] = size if 'number' in kwargs: number = kwargs['number'] _check_type(number, 'number', int) self._prop['number'] = number
[docs] def getp(self, prm_name): try: return self._prop[prm_name] except: raise KeyError("%s.getp: no parameter with name '%s'" % \ (self.__class__.__name__, prm_name))
def _set_current_axis_old(self, ax): if isinstance(ax, int): # check if it is inside axshape self._prop['curax'] = ax # no good! elif isinstance(ax, Axis): self._prop['axes'] = {1:ax} self._prop['axshape'] = (1,1) self._prop['curax'] = 1 def _set_current_axis(self, ax): if isinstance(ax, int) and ax in self._prop['axes'].keys(): self._prop['curax'] = ax elif isinstance(ax, Axis): if ax in self._prop['axes'].values(): for i in self._prop['axes'].keys(): if ax == self._prop['axes'][i]: self._prop['curax'] = i break else: n = max(self._prop['axes'].keys()) + 1 self._prop['axes'][n] = ax self._prop['curax'] = n
[docs]class BaseClass(object): """ Subclasses implement different backends (GnuplotBackend for Gnuplot, for instance). This base class saves info about plotting to instances of class Figure, Line, and PlotItem. List of internal helper functions (for subclasses): ... """ _matlab_like_cmds = [ 'autumn', 'axes', 'axis', 'bone', 'box', 'brighten', 'camdolly', 'camlight', 'camlookat', 'campos', 'camproj', 'camroll', 'camtarget', 'camup', 'camva', 'camzoom', 'caxis', 'cla', 'clabel', 'clf', 'close', 'closefig', 'closefigs', 'coneplot', 'colorbar', 'colorcube', 'colormap', 'contour', 'contour3', 'contourf', 'contourslice', 'cool', 'copper', 'daspect', 'figure', 'fill', 'fill3', 'flag', 'gca', 'gcf', 'get', 'gray', 'grid', 'hardcopy', 'hidden', 'hold', 'hot', 'hsv', 'ishold', 'isocaps', 'isosurface', 'jet', 'legend', 'light', 'lines', 'loglog', 'material', 'mesh', 'meshc', 'openfig', 'savefig', 'pcolor', 'pink', 'plot', 'plot3', 'prism', 'quiver', 'quiver3', 'reducevolum', 'semilogx', 'semilogy', 'set', 'shading', 'show', 'slice_', 'spring', 'streamline', 'streamribbon', 'streamslice', 'streamtube', 'savefig', 'subplot', 'subvolume', 'summer', 'surf', 'surfc', 'surfl', 'title', 'vga', 'view', 'white', 'winter', 'xlabel', 'ylabel', 'zlabel'] __doc__ += docadd('List of "Matlab-like" interface functions (for ' + \ 'the user)', _matlab_like_cmds) _local_attrs = { 'curfig': 1, # current figure 'show': True, # screenplot after each plot command #'changed': False, # sync state 'interactive': True, # update backend after each change 'color': False, # hardcopy with color? } _update_from_config_file(_local_attrs) # get defaults from scitools.cfg __doc__ += docadd('Keywords for the setp method', _local_attrs.keys()) # Dictionary of functions testing legal types _attrs_type = {'curfig': lambda arg: isinstance(arg, (int)), 'show': lambda arg: isinstance(arg, (bool)), 'interactive': lambda arg: isinstance(arg,(bool)), #'changed': lambda arg: isinstance(arg, (bool)), 'color': lambda arg: isinstance(arg,(bool)) }
[docs] def __init__(self): BaseClass.init(self)
[docs] def init(self): """Initialize internal data structures.""" self._g = None # Pointer to the backend for manual labour. self._figs = {1: Figure()} # dictionary of figure instances self._attrs = {} self._attrs.update(BaseClass._local_attrs)
[docs] def __str__(self): return pprint.pformat(self._attrs)
[docs] def setp(self, *args, **kwargs): """ Set object properties or attributes in this backend instance. Calling:: setp([obj,] prop1=value1, prop2=value2, ...) will set the attributes as given in this backend instance. If the optional positional argument obj is a given object with a set method (like Figure, Axis, and PlotProperties objects), the (relevant) properties and values are also set in this object. This also works on a list with objects. """ nargs = len(args) if nargs > 0: arg = args[0] if not isinstance(arg, (tuple,list)): arg = (arg,) for obj in arg: if hasattr(obj, 'setp'): obj.setp(**kwargs) if 'savefig' in kwargs: # synonym: hardcopy kwargs['hardcopy'] = kwargs['savefig'] for key in kwargs: value = kwargs[key] if key in self._attrs: # legal key? if self._attrs_type[key](value): # legal type? self._attrs[key] = value else: raise TypeError( 'BaseClass.setp: keyword "%s" %s is illegal.' % \ (key, type(key))) if 'hardcopy' in kwargs: self.hardcopy(kwargs['hardcopy']) if 'material' in kwargs: self.material(kwargs['material']) # subclasses should extend the doc string like this: #set.__doc__ += docadd('Keywords for the setp method', # BaseClass._local_attrs.keys(), # SomeSubClass._local_attrs.keys())
[docs] def getp(self, *args): """ Get object properties or an attribute in this backend instance. Calling:: getp('name') returns the attribute with name 'name' in this backend instance. Calling:: getp(obj, 'name') returns the property with name 'name' of the object given in obj. This object must have a get method (like Figure, Axis, or PlotProperties objects). Calling:: getp(obj) displays all property names and values for the object given in obj. """ nargs = len(args) if nargs > 0: if hasattr(args[0], 'getp'): obj = args[0] if nargs == 1: print obj else: return obj.getp(args[1]) else: prm_name = args[0] try: return self._attrs[prm_name] except: raise KeyError('%s.getp: no parameter with name "%s"' % \ (self.__class__.__name__, prm_name)) else: raise TypeError("getp: wrong number of arguments") # subclasses should extend the doc string like this: #getp.__doc__ += docadd('Keywords for the getp method', # BaseClass._local_attrs.keys(), # SomeSubClass._local_attrs.keys()) #def __getitem__(self, name): self.getp(name) #def __setitem__(self, name, value): self.setp({name:value})
def _replot(self, *args, **kwargs): """ Update backend after change in data. This is a key routine and must be implemented in the backend. """ raise NotImplementedError('_replot not implemented in class %s' % \ self.__class__.__name__) def _check_args(self, *args): """Return the current axis, the argument list args, and the number of arguments in args. If the first argument in args is an Axis instance, this will be returned instead of the current axis. The Axis instancce is then removed from the argument list and the number of arguments is decremented by one.""" ax = self.gca() nargs = len(args) if nargs > 0 and isinstance(args[0], Axis): ax = args[0]; args = args[1:]; nargs -= 1 return ax, args, nargs def _cmpPlotProperties(self, a, b): """Sort cmp function for PlotProperties.""" plotorder = [Volume, Streams, Surface, Contours, VelocityVectors, Bars, Line] assert isinstance(a, PlotProperties) assert isinstance(b, PlotProperties) assert len(PlotProperties.__class__.__subclasses__(PlotProperties)) ==\ len(plotorder) # check all subclasses is in plotorder return cmp(plotorder.index(a.__class__),plotorder.index(b.__class__))
[docs] def gcf(self): """Return current figure.""" return self._figs[self._attrs['curfig']]
[docs] def gca(self): """Return the current axis in the current figure.""" return self.gcf().gca()
[docs] def axes(self, *args, **kwargs): """Create axes in specified positions. Calling:: axes() returns a default Axis instance. Calling:: axes(ax) sets the axis in the Axis instance ax as the current axis. Calling:: axes(viewport=RECT) returns a axis at the position given in RECT. RECT is normally a list [left,bottom,width,height], where the four parameters (values between 0 and 1) specifies the location and size of the axis box ((0,0) is the lower-left corner and (1,1) is the upper-right corner). However, this is backend-dependent. """ nargs = len(args) if nargs == 0: a = Axis() if len(kwargs) > 0: a.setp(**kwargs) self.gcf().setp(curax=a) return a elif nargs == 1: _check_type(args[0], 'ax', Axis) self.gcf().setp(curax=args[0]) #raise NotImplementedError('not yet implemented') elif nargs == 2: pass else: raise TypeError("axes: wrong number of arguments")
[docs] def subplot(self, *args, **kwargs): """Create axes in tiled positions. Calling:: subplot(m,n,p) splits the Figure window into m rows and n columns of small axes. The p-th axis is then selected for the current plot and the Axis object is returned. Calling:: subplot(mnp) is the same as calling subplot(m,n,p) as long as m<=n<=p<10. Example: >>> x = linspace(-5,5,101) >>> subplot(2,1,1) >>> plot(x,x-cos(x**2)) >>> x = logspace(-1,2) >>> subplot(2,1,2) >>> loglog(x,exp(x),'b-s') """ fig = self.gcf() nargs = len(args) if nargs == 1: sp = str(args[0]) if len(sp) != 3: raise TypeError("subplot: '%s' is not a valid subplot" % sp) args = [int(a) for a in sp] nargs = 3 if nargs == 3: m, n, p = args if fig.getp('axshape') == (m,n): fig.setp(curax=p) else: fig.setp(axshape=(m,n), curax=p) self.gca().setp(**kwargs) else: raise TypeError("subplot: wrong number of arguments") return self.gca()
[docs] def daspect(self, *args): """Change the data aspect ratio. Calling:: daspect([x,y,z]) sets the data aspect ratio for the x, y, and z axis (e.g., daspect([1,2,4]) means that one unit in x direction is equal in length to two units in y direction and four units in z direction). For example, daspect([r,1,1]) makes the physical size of the y axis in the plot r times the x axis. The size of the axes must be explicitly in order for the daspect command to work properly. Note that setting the aspect ratio is not supported by all backends. Calling:: daspect() returns the data aspect ratio for the current axis. Calling:: daspect(mode) sets the data aspect ratio mode, where mode can be 'auto', 'equal', or 'manual'. By specifying the mode to 'auto' (default), the data aspect ratio will be automatically computed so that each axis spans the available space in the figure window. In the 'manual' mode the user sets the aspect ratio, while 'equal' means that the the same units are used in both axes (for example, if the x axis goes from 0 to 10 and the y axis from 0 to 1, and we want the x axis ten times as long as the y axis, the manual aspect ratio er 0.1, and 'equal' will automatically set the ratio to 0.1). Calling:: daspect('mode') returns the data aspect ratio mode. Calling:: daspect(r) sets the data aspect ratio to r. This is for backends that do not support setting individual aspect ratios for the x, y, and z axis (like the Gnuplot backend). If the backend do support individual aspect ratios, the aspect ratio is set to r for all three axes. Calling:: daspect(ax, ...) uses the the Axis object ax instead of the current axis. >>> surf(peaks(21)) <scitools.easyviz.common.Surface object at 0xb7d7950c> >>> daspect() (1.0, 1.0, 0.71549553759291729) >>> figure() >>> surf(peaks(21)) <scitools.easyviz.common.Surface object at 0xb58ff70c> >>> daspect([1,1,1]) >>> """ ax, args, nargs = self._check_args(*args) if nargs == 0: return ax.getp('daspect') elif nargs == 1: arg = args[0] if isinstance(arg, str): if arg == 'mode': return ax.getp('daspectmode') else: ax.setp(daspcetmode=arg) else: ax.setp(daspect=arg) else: raise TypeError("daspect: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def openfig(self, filename='figspickle.txt'): """ Load figures saved in a file (with the given filename). The format of this file is currently standard Python pickle format. All figures in a previous session were dumped to the file, and all these figures are by this method reloaded and added to the current set of figures. """ # in savefig, self._figs was pickled as one object handle = open(filename, 'r') filefigs = pickle.load(handle) handle.close() # check that filefigs is a dict of Figure instances: fail = True if isinstance(filefigs, dict): fail = False for item in filefigs: if not isinstance(item, Figure): fail = True if fail: raise Exception("Import error. Cannot retrieve figures from filename %s ." % filename) self._figs.update(filefigs)
[docs] def savefig(self, filename='figspickle.txt'): """ Save all current figures to a file (with the given filename). The file has standard Python pickle format (dict of Figure instances). The figures can later be reloaded by the openfig method. """ handle = open(filename, 'w') pickle.dump(self._figs, handle) handle.close()
[docs] def hardcopy(self, filename, **kwargs): """ Save a hardcopy of the current figure to file (with the given filename). The file format (image type) is determined from the extension of the filename. If any changes have been made by working directly on the backend instance, set the keyword argument replot to False to stop Easyviz from replotting the current figure and destroying those changes. """ # must be implemented in subclass raise NotImplementedError('hardcopy not implemented in class %s' % \ self.__class__.__name__)
[docs] def hold(self, *args): """Change the hold state of the current axis. Calling:: hold('on') makes every subsequent plotting commands be added to the current plot. Calling:: hold('off') clears the previous plot before the new plot is drawn. This is the default behavior. Calling:: hold() toggles the hold state in the current axis. Calling:: hold(ax, ...) affects the Axis object ax instead of the current axis. Note that one can use hold(True) and hold(False) instead of hold('on') and hold('off'), respectively. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(hold=args[0]) elif nargs == 0: ax.toggle('hold') print "hold state is %s" % ax.getp('hold') else: raise TypeError('hold: wrong number of arguments')
[docs] def ishold(self): """ Return the hold state (True if hold is on, and False if it is off). """ return self.gca().getp('hold')
[docs] def figure(self, num=None, **kwargs): """ Create a new figure or switch between figures and return Figure object. num is the figure number of the new or existing figure. """ try: num = int(num) except: # print str(num),' is not an integer' if len(self._figs) == 0: # No figures left num = 1 else: num = max(self._figs.keys())+1 #print "Active figure is %d." % num if not num in self._figs: # Points to class Figure or other convenient function # In gnuplot backend this should instantiate a new pipe instead kwargs['number'] = num self._figs[num] = Figure(**kwargs) self._attrs['curfig'] = num return self._figs[num]
[docs] def clf(self): """Clear the current figure.""" #self.gcf().reset() del self._figs[self._attrs['curfig']] self.figure(self._attrs['curfig'])
[docs] def cla(self, *args): """Clear the current axis. Calling:: cla() clears the current axis. Calling:: cla(ax) clears the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) ax.reset()
[docs] def axis(self, *args, **kwargs): """Choose the axis limits and appearance. Calling:: axis([xmin, xmax, ymin, ymax[, zmin, zmax]]) sets the limits on the x-, y-, and z-axes in the current plot. Calling:: axis(xmin, xmax, ymin, ymax[, zmin, zmax]) gives the same result as above. Calling:: axis() returns the limits on the x-, y-, and z-axes for the current plot. If the view in the current plot is a 2D view, only the limits on the x- and y-axis are returned. Calling:: axis(mode) sets axis scaling to mode, where mode can be * 'auto' - autoscaling is used * 'manual' - freeze the scaling at the current limits * 'tight' - sets the axis limits to the range of the data * 'fill' - has currently no affect Calling:: axis(method) sets the appearance of the current axis as specified by method. %s Calling:: axis(direction) sets the direction of the increasing values on the axes. * 'ij' - reverse y-axis * 'xy' - restore y-axis Calling:: axis('off') turns off the visibility of the axis. Calling:: axis('on') turns the visibility of the axis back on. Calling:: axis(ax, ...) affects the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 0 and len(kwargs) == 0: xmin, xmax, ymin, ymax, zmin, zmax = ax.get_limits() def get_lim(amin, amax, n1, n2): if ax.getp(n1) is not None and ax.getp(n2) is not None: return ax.getp(n1), ax.getp(n2) else: return amin, amax xmin, xmax = get_lim(xmin, xmax, 'xmin', 'xmax') ymin, ymax = get_lim(ymin, ymax, 'ymin', 'ymax') zmin, zmax = get_lim(zmin, zmax, 'zmin', 'zmax') if ax.getp('camera').getp('view') == 2: return xmin, xmax, ymin, ymax return xmin, xmax, ymin, ymax, zmin, zmax limits = Axis._ranges # Allow both axis(xmin,xmax,ymin,ymax[,zmin,zmax]) and # axis([xmin,xmax,ymin,ymax[,zmin,zmax]]) if nargs == 1: if isinstance(args[0], (tuple,list)): args = args[0]; nargs = len(args) elif isinstance(args[0], str): if args[0] in Axis._modes: ax.setp(mode=args[0]) elif args[0] in ['on', 'off']: state = _toggle_state(args[0]) ax.setp(visible=state) elif args[0] in Axis._methods: ax.setp(method=args[0]) elif args[0] in Axis._directions: ax.setp(direction=args[0]) kwargs_ = {} # first treat positional arguments: if nargs in (4,6): for i in range(nargs): kwargs_[limits[i]] = args[i] # allow keyword arguments: for kw in limits: if kw in kwargs: kwargs_[kw] = kwargs[kw] ax.setp(**kwargs_) if self.getp('interactive') and self.getp('show'): self._replot()
axis.__doc__ = axis.__doc__ % docadd('Legal values for method are', Axis._methods, indent=10)
[docs] def xlim(self, *args): """Set or get limits on x axis. Calling:: xlim([xmin,xmax]) sets the x limits on the current axis. Calling:: xlim(xmin,xmax) gives the same results as above. Calling:: xmin, xmax = xlim() returns the x limits for the current axis. Calling:: xlim(ax, ...) affects the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 0: xmin = ax.getp('xmin') xmax = ax.getp('xmax') if xmin is None or xmax is None: xmin, xmax = ax.getp('xlim') if xmin is None or xmax is None: return [0,1] return xmin, xmax elif nargs == 1: arg = args[0] if isinstance(arg, (list,tuple,NumPyArray)) and len(arg) == 2: ax.setp(xmin=arg[0], xmax=arg[1]) elif isinstance(arg, str): raise NotImplementedError() elif nargs == 2: ax.setp(xmin=args[0], xmax=args[1]) else: raise TypeError('xlim: wrong number of arguments.') if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def ylim(self, *args): """Set or get limits on y axis. Calling:: ylim([ymin,ymax]) sets the y limits on the current axis. Calling:: ylim(ymin,ymax) gives the same results as above. Calling:: ymin, ymax = ylim() returns the y limits for the current axis. Calling:: ylim(ax, ...) affects the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 0: ymin = ax.getp('ymin') ymax = ax.getp('ymax') if ymin is None or ymax is None: ymin, ymax = ax.getp('ylim') if ymin is None or ymax is None: return [0,1] return ymin, ymax elif nargs == 1: arg = args[0] if isinstance(arg, (list,tuple,NumPyArray)) and len(arg) == 2: ax.setp(ymin=arg[0], ymax=arg[1]) elif isinstance(arg, str): raise NotImplementedError() elif nargs == 2: ax.setp(ymin=args[0], ymax=args[1]) else: raise TypeError('ylim: wrong number of arguments.') if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def zlim(self, *args): """Set or get limits on z axis. Calling:: zlim([zmin,zmax]) sets the z limits on the current axis. Calling:: zlim(zmin,zmax) gives the same results as above. Calling:: zmin, zmax = zlim() returns the z limits for the current axis. Calling:: zlim(ax, ...) affects the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 0: zmin = ax.getp('zmin') zmax = ax.getp('zmax') if zmin is None or zmax is None: zmin, zmax = ax.getp('zlim') if zmin is None or zmax is None: return [0,1] return zmin, zmax elif nargs == 1: arg = args[0] if isinstance(arg, (list,tuple,NumPyArray)) and len(arg) == 2: ax.setp(zmin=arg[0], zmax=arg[1]) elif isinstance(arg, str): raise NotImplementedError() elif nargs == 2: ax.setp(zmin=args[0], zmax=args[1]) else: raise TypeError('zlim: wrong number of arguments.') if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def close(self, *args): """Close figure. Calling:: close() closes the current figure. Calling:: close(num) closes the figure with number num. Calling:: close(fig) closes the Figure object fig. Calling:: close('all') closes all figures. """ nargs = len(args) if nargs == 0: self.closefig(self._attrs['curfig']) elif nargs == 1: if args[0] == 'all': self.closefigs() else: self.closefig(args[0]) else: raise TypeError("close: wrong number of arguments")
[docs] def closefig(self, arg): """Close figure window.""" raise NotImplementedError('closefig not implemented in class %s' % \ self.__class__.__name__)
[docs] def closefigs(self): """Close all figure windows.""" self._figs = {} self._figs[1] = Figure() self._attrs['curfig'] = 1 # the rest should be written in subclass
[docs] def grid(self, *args): """Toggle the display of grid lines. Calling:: grid('on') displays grid lines in the current axis. Calling:: grid('off') removes the grid lines from the current axis. Calling:: grid() toggles the display of grid lines in the current axis. Calling:: grid(ax, ...) uses Axis object ax instead of the current axis. Note that calling grid(True) and grid(False) is the same as calling grid('on') and grid('off'), respectively. """ ax, args, nargs = self._check_args(*args) if nargs == 0: ax.toggle('grid') elif nargs == 1: ax.setp(grid=args[0]) else: raise TypeError("grid: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def legend(self, *args, **kwargs): """Add legend(s) to the current plot. Calling:: legend(string1, string2, string3,...) or legend([string1, string2, string3,...]) adds legends to the current plot using the given strings as labels. Note that the number of strings should match the number of items in the current axis (i.e., getp(gca(), 'numberofitems')), but a less legends than curve items are allowed.xo Calling:: legend(string) adds the given string as a legend to the last item added to the current axis. Calling:: legend(ax, ...) affects the Axis object ax instead of the current axis. The keyword arguments (**kwargs) are ignored, but makes it possible to seemlessly switch between easyviz and matplotlib. """ if len(args) == 1 and isinstance(args[0], (list, tuple)): # unpack the list/tuple to individual arguments args = [e for e in args[0]] ax, args, nargs = self._check_args(*args) for key in kwargs: if key == 'loc': value = kwargs[key] if isinstance(value, int): if 0 <= value <= 10: for loc in Axis._legend_locs: if Axis._legend_locs[loc] == value: value = loc break else: raise ValueError( 'legend: wrong value of loc=%s, ' 'should be between 0 and 10' % value) elif isinstance(value, str): if not value in Axis._legend_locs: raise ValueError( 'legend: wrong value of loc=%s, ' 'should be\n%s' % \ (value, str([v for v in Axis._legend_locs])[1:-1])) else: raise ValueError('legend: wrong value of loc=%s' % value) ax.setp(legend_loc=value) elif key == 'fancybox': value = kwargs[key] if not value in (True, False, None): raise ValueError('legend: wrong value of fancybox=%s' % value) ax.setp(legend_fancybox=value) else: print 'legend: ignoring keyword argument "%s"' % key items = ax.getp('plotitems') if len(items) == 0: print 'plot is empty, cannot add legend' return if nargs > 1: # Consistency check of len(args) and number of items in axis if len(items) >= nargs: # Iterate over items and set legend for i in range(nargs): items[i].setp(legend=str(args[i])) elif nargs == 1: items[-1].setp(legend=str(args[0])) else: raise TypeError("legend: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def title(self, *args): """Add title to the current plot. Calling:: title(s) adds the text given in s at the top of the current axis. Calling:: title(ax, ...) adds a title to the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(title=str(args[0])) else: raise TypeError("title: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def xlabel(self, *args): """Add a label to the x-axis. Calling:: xlabel(s) adds the text given in s beside the x-axis on the current axis. Calling:: xlabel(ax, ...) adds the label to the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(xlabel=str(args[0])) else: raise TypeError("xlabel: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def ylabel(self, *args): """Add a label to the y-axis. Calling:: ylabel(s) adds the text given in s beside the y-axis on the current axis. Calling:: ylabel(ax, ...) adds the label to the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(ylabel=str(args[0])) else: raise TypeError("ylabel: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def zlabel(self, *args): """Add a label to the z-axis. Calling:: zlabel(s) adds the text given in s beside the z-axis on the current axis. Calling:: zlabel(ax, ...) adds the zlabel to the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(zlabel=str(args[0])) else: raise TypeError("zlabel: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot() # 2D Plotting
[docs] def plot(self, *args, **kwargs): """Draw line and scatter plots. Calling:: plot(x, y) plots y against x, i.e., if x and y are vectors of length n, then this will plot all the points (x[i], y[i]) for 0<=i<n. Calling:: plot(y) plots values in y on y-axis (same as plot(range(len(y)),y)). Calling:: plot(y, fmt) plots values in y on y-axis formated like fmt (see below). Calling:: plot(x1,y1,fmt1, x2,y2,fmt2, ...) same as hold('on') followed by multiple plot(x,y,fmt). Calling:: plot(x1,y1,x2,y2,...) like above, but automatically chooses different colors. Calling:: plot(y1,y2,...,x=x) uses x as the x values for all the supplied curves. x='auto' has the same effect as x=range(len(y1)). Calling:: plot(...,log=mode) uses logarithmic (base 10) scales on either the x- or y-axes (or both). mode can be * 'x' - logarithmic scale on x-axis * 'y' - logarithmic scale on y-axis * 'xy' - logarithmic scales on both x- and y-axes. Calling:: plot(ax, ...) plots into the Axis object ax instead of the current axis. The plot command returns a list a of all Line objects created. The following format specifiers exist: y yellow . point - solid m magenta o circle : dotted c cyan x x-mark -. dashdot r red + plus -- dashed g green * star b blue s square w white d diamond k black v triangle (down) ^ triangle (up) < triangle (left) > triangle (right) p pentagram h hexagram Examples: Draw a line from a Python list: >>> plot([1,2,3]) Draw three red crosses: >>> plot([1,2,3], 'rx') A somewhat more complex example: >>> x = linspace(0, 15, 76) # 0, 0.2, 0.4, ..., 15 >>> y1 = sin(x)*x >>> y2 = sin(x)*sqrt(x) >>> plot(x, y1, 'b-', x, y2, 'ro', ... legend=('x*sin(x)', 'sqrt(x)*sin(x)')) Note: loglog, semilogy, and semilogx are like plot(...,log='xy'), plot(...,log='y'), and plot(...,log='x'), respectively. """ if 'description' not in kwargs: kwargs['description'] = 'plot: 2D curve plot' ax, args, nargs = self._check_args(*args) if nargs == 0: raise TypeError("plot: not enough arguments given") lines = [] # store all Line instances here # If first argument is a format string this will be ignored # If two format strings are used only the first of them will be used if 'x' in kwargs: if nargs == 1 or (nargs == 2 and isinstance(args[1], str)): if nargs == 1: lines.append(Line(x=kwargs['x'], y=args[0], format='')) else: lines.append(Line(x=kwargs['x'], y=args[0], format=args[1])) else: for i in range(len(args)-1): if not isinstance(args[i], str): if isinstance(args[i+1], str): lines.append(Line(x=kwargs['x'], y=args[i], format=args[1+i])) else: lines.append(Line(x=kwargs['x'], y=args[i], format='')) if i == nargs-2: lines.append(Line(x=kwargs['x'], y=args[i+1], format='')) del kwargs['x'] # x in kwargs is no longer needed else: # Normal case # If an odd number, larger than 2, of non-strings in args are # between two string arguments, something is wrong. # If the odd number is one, the argument x='auto' is passed. i = 0 if nargs in (1,2): if not isinstance(args[0], str): if nargs == 1: lines.append(Line(x='auto', y=args[0], format='')) else: if not isinstance(args[1], str): lines.append(Line(x=args[0], y=args[1], format='')) else: lines.append(Line(x='auto', y=args[0], format=args[1])) i+100 #return else: raise ValueError("plot: cannot plot a formatstring") while i <= nargs-3: # This item is not string --> y-value, should never be string. if not isinstance(args[i], str): if not isinstance(args[i+1], str): if isinstance(args[i+2], str): lines.append(Line(x=args[i], y=args[i+1], format=args[i+2])) i = i+3 else: lines.append(Line(y=args[i+1], x=args[i], format='')) i = i+2 # Next element is str --> no x-value else: lines.append(Line(y=args[i], x='auto', format=args[i+1])) i = i+2 # These last cases could be run outside the while loop if i == nargs-2: # Either y and format or x and y value left if isinstance(args[i+1], str): lines.append(Line(y=args[i], x='auto', format=args[i+1])) else: lines.append(Line(x=args[i], y=args[i+1], format='')) elif i == nargs-1: # In this case we have only an y value left lines.append(Line(y=args[i], x='auto', format='')) # add the lines to the axes in ax: ax.add(lines) # Set legends if 'legend' in kwargs: no_lines = len(lines) # number of lines added legends = kwargs['legend'] if isinstance(legends, (tuple,list)): # legends is a sequence if len(legends) == no_lines: for i in range(no_lines): legend = legends[no_lines-i-1] if isinstance(legend, str): ax.getp('plotitems')[-1-i].setp(legend=legend) else: print "Legend "+legend+" is not a string" else: print 'Number of legend items (%d) is not equal to '\ 'number of lines in plotcommand (%d)' % \ (len(legends), no_lines) elif isinstance(legends,str): # only one legend ax.getp('plotitems')[-1].setp(legend=legends) del kwargs['legend'] if 'legend_loc' in kwargs: # No test on validity as in legend method... ax.setp(legend_loc=kwargs['legend_loc']) if 'legend_fancybox' in kwargs: ax.setp(legend_fancybox=kwargs['legend_fancybox']) if not ax.getp('hold') and not 'box' in kwargs: kwargs['box'] = True # set keyword arguments in all the added lines for line in lines: line.setp(**kwargs) # automatically add line colors if this is not specified: if not line.getp('linecolor'): line.setp(linecolor=ax.get_next_color()) ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return lines
[docs] def loglog(self, *args, **kwargs): """Draw a loglog plot with logarithmic scaling on x- and y-axis. Calling:: loglog(...) is the same as calling plot(...) with the exception that a logarithmic (base 10) scale is used for both x- and y-axes. """ kwargs['log'] = 'xy' return self.plot(*args, **kwargs)
[docs] def semilogx(self, *args, **kwargs): """Draw a semilog plot with logarithmic scaling on x-axis. Calling:: semilogx(...) is the same as calling plot(...) with the exception that a logarithmic (base 10) scale is used for the x-axis. """ kwargs['log'] = 'x' return self.plot(*args, **kwargs)
[docs] def semilogy(self, *args, **kwargs): """Draw a semilog plot with logarithmic scaling on y-axis. Calling:: semilogy(...) is the same as calling plot(...) with the exception that a logarithmic (base 10) scale is used for the y-axis. """ kwargs['log'] = 'y' return self.plot(*args, **kwargs)
[docs] def plot3(self, *args, **kwargs): """Draw lines and points in 3D space. Calling:: plot3(x, y, z) plots z against x and y, i.e., if x, y, and z are vectors of length n, then this will plot all the points (x[i], y[i], z[i]) for 0<=i<n. Calling:: plot3(z) plots values in z on the z-axis (same as plot3(range(len(z)), range(len(z)), z)). Calling:: plot3(z, fmt) plots values in z on z-axis formated like fmt (see the plot command). Calling:: plot3(x1,y1,z1,fmt1,x2,y2,z2,fmt2,...) same as hold('on') followed by multiple plot3(x,y,z,fmt). Calling:: plot3(x1,y1,z1,x2,y2,z2,...) like above, but automatically chooses different colors. Calling:: plot3(z1,z2,...,x=x,y=y) uses x as the values on the x-axis and y as the values on the y-axis for all the supplied curves (assuming that all have the same length). By setting x='auto' and y='auto' has the same effect as x=range(len(z1)) and y=range(len(z1)), respectively. Calling:: plot3(ax, ...) plots into the Axis object ax instead of the current axis. The plot3 command returns a list containing all the created Line objects. Examples: >>> t = linspace(0,10*pi,301) >>> plot3(sin(t), cos(t), t, title='A helix', grid='on') """ if not 'description' in kwargs: kwargs['description'] = 'plot3: 3D line plot' if not 'hidden' in kwargs: kwargs['hidden'] = False ax, args, nargs = self._check_args(*args) if nargs == 0: raise TypeError("plot3: not enough arguments given") lines = [] # all Line instances are stored here # If first argument is a format string this will be ignored # If two format strings are used only the first of them will be used if 'x' in kwargs and 'y' in kwargs: if nargs == 1 or (nargs == 2 and isinstance(args[1], str)): if nargs == 1: lines.append(Line(x=kwargs['x'], y=kwargs['y'], z=args[0], format='')) else: lines.append(Line(x=kwargs['x'], y=kwargs['y'], z=args[0], format=args[1])) else: for i in range(len(args)-1): if not isinstance(args[i], str): if isinstance(args[i+1], str): lines.append(Line(x=kwargs['x'], y=kwargs['y'], z=args[i], format=args[1+i])) else: lines.append(Line(x=kwargs['x'], y=kwargs['y'], z=args[i], format='')) if i == nargs-2: lines.append(Line(x=kwargs['x'], y=kwargs['y'], z=args[i+1], format='')) # x and y in kwargs are no longer needed: del kwargs['x'] del kwargs['y'] else: # Normal case # If an odd number, larger than 2, of non-strings in args are # between two string arguments, something is wrong. # If the odd number is one, the argument x='auto' is passed. i = 0 if nargs in (1,2,3,4): if not isinstance(args[0], str): if nargs == 1: # plot3(z) lines.append(Line(x='auto', y='auto', z=args[0], format='')) elif nargs == 2: # plot3(z,fmt) if isinstance(args[1], str): lines.append(Line(x='auto', y='auto', z=args[0], format=args[1])) elif nargs == 3: # plot3(x,y,z) if not isinstance(args[2], str): lines.append(Line(x=args[0], y=args[1], z=args[2], format='')) else: # plot(x,y,z,fmt) or plot(z1,fmt1,z2,fmt2) if not isinstance(args[3], str): lines.append(Line(x='auto', y='auto', z=args[0], format=args[1])) lines.append(Line(x='auto', y='auto', z=args[2], format=args[3])) else: lines.append(Line(x=args[0], y=args[1], z=args[2], format=args[3])) i+100 #return else: raise ValueError("plot3: cannot plot a formatstring") while i <= nargs-5: if not isinstance(args[i], str): # should never be string # cases: # 1. plot3(x1,y1,z1,s1, x2,y2,z2,s2, ...) if not isinstance(args[i+1], str): if not isinstance(args[i+2], str): if isinstance(args[i+3], str): lines.append(Line(x=args[i], y=args[i+1], z=args[i+2], format=args[i+3])) i += 4 else: lines.append(Line(x=args[i], y=args[i+1], z=args[i+2], format='')) i += 3 else: # next element is str --> no x and y values lines.append(Line(x='auto', y='auto', z=args[i], format=args[i+1])) i += 2 # 2. plot3(x1,y1,z1, x2,y2,z2, ...) # 3. plot3(z1,s1, z2,s2, ...) if i == nargs-4: if not isinstance(args[i+1], str): lines.append(Line(x=args[i], y=args[i+1], z=args[i+2], format=args[i+3])) else: lines.append(Line(x='auto', y='auto', z=args[i], format=args[i+1])) lines.append(Line(x='auto', y='auto', z=args[i+2], format=args[i+3])) elif i == nargs-3: # x, y, and z left lines.append(Line(x=args[i], y=args[i+1], z=args[i+2], format='')) elif i == nargs-2: # only z and format string left if isinstance(args[i+1], str): lines.append(Line(x='auto', y='auto', z=args[i], format=args[i+1])) elif i == nargs-1: # only a z value left lines.append(Line(x='auto', y='auto', z=args[i], format='')) # add the lines to the axes in ax: ax.add(lines) # Set legends if 'legend' in kwargs: no_lines = len(lines) legends = kwargs['legend'] if isinstance(legends, (tuple,list)): # legends is a sequence if len(legends) == no_lines: for i in range(no_lines): legend = legends[no_lines-i-1] if isinstance(legend, str): ax.getp('plotitems')[-1-i].setp(legend=legend) else: print "Legend "+legend+" is not a string" else: print "Number of legend items (%d) is not equal to " \ "number of lines (%d) in plotcommand" % \ (len(legends), no_lines) elif isinstance(legends,str): # only one legend ax.getp('plotitems')[-1].setp(legend=legends) del kwargs['legend'] if not ax.getp('hold') and not 'view' in kwargs: kwargs['view'] = 3 # set keyword arguments in all the added lines: for line in lines: line.setp(**kwargs) # automatically add line colors if this is not specified: if not line.getp('linecolor'): line.setp(linecolor=ax.get_next_color()) ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return lines
[docs] def fill(self, *args, **kwargs): """Draw filled 2D polygons.""" kwargs['description'] = 'fill: filled 2D polygons' if not 'edgecolor' in kwargs: kwargs['edgecolor'] = 'k' return self.plot(*args, **kwargs)
[docs] def stem(self, *args, **kwargs): """Draw a stem plot.""" kwargs['description'] = 'stem: stem plot' return self.plot(*args, **kwargs)
[docs] def bar(self, *args, **kwargs): """Draw a bar graph. Calling:: bar(data) where data is a dictionary on the form data = {'method1': {'thing1': value, 'thing2': value}, 'method2': {'thing1': value, 'thing2': value}, 'method3': {'thing1': value, 'thing2': value},} will draw m bars for every name (key in data), one for each key in data[name], where the height indicates the value. The name is placed beneath each of the bar groups on the x axis. Calling:: bar(Y) will draw a bar for each of the elements in the vector/matrix Y. If Y is a matrix, a group of bars from the elements of each row of Y will be created. Calling:: bar(x,Y) is the same as above only that the values on the x axis is defined by the vector x. Calling:: bar(..., width) uses the specified width on the bars. The default width is 0.8, while a width of 1.0 should make the bars just touch each other (might vary in the different backends). Calling:: bar(..., color) uses the given color for all the bars. Calling:: bar(ax, ...) uses the Axis object ax instead of the current axis. @return: A Bars object. Examples: >>> from numpy.random import rand >>> bar(rand(4)) >>> figure() >>> bar(rand(4,3)) """ kwargs['description'] = 'bar: bar graph' ax, args, nargs = self._check_args(*args) h = Bars(*args, **kwargs) ax.add(h) if not ax.getp('hold'): if not 'box' in kwargs: kwargs['box'] = True ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def quiver(self, *args, **kwargs): """Draw arrows from a 2D vector field. Calling:: quiver(X, Y, U, V) displays vectors as arrows with components U and V at the grid defined by X and Y. The arrays U and V must both have the same shape and the grid components X and Y must either have the same shape as U or fulfill the requirement len(X)==n and len(y)==m, where m,n=shape(u). Calling:: quiver(U, V) is the same as calling quiver(range(n),range(m),U,V), where m,n=shape(u)). Calling:: quiver(..., 'filled') draw filled arrows. Calling:: quiver(..., fmt) sets the line specification as given in the format string fmt. See the plot command for further details on specifying the format string. Examples: Plot the gradient field of the function z = x**2 + y**2: >>> x = y = linspace(-2, 2, 21) >>> xv, yv = meshgrid(x,y) >>> values = xv**2 + yv**2 >>> contour(xv, yv, values, 10, hold='on') <scitools.easyviz.common.Contours object at 0xb45f374c> >>> uv, vv = gradient(values, 0.2) >>> quiver(xv, yv, uv, vv) <scitools.easyviz.common.VelocityVectors object at 0xb45435cc> >>> hold('off') Another example: >>> x = linspace(0,3,80) >>> y = sin(2*pi*x) >>> theta = 2*pi*x+pi/2 >>> u = sin(theta)/10 >>> v = cos(theta)/10 >>> quiver(x,y,u,v,0.04,'b',hold='on') <scitools.easyviz.common.VelocityVectors object at 0xb768e1cc> >>> plot(x,y,'r') [<scitools.easyviz.common.Line object at 0xb768e36c>] >>> hold('off') """ if not 'description' in kwargs: kwargs['description'] = 'quiver: 2D vector field' ax, args, nargs = self._check_args(*args) h = VelocityVectors(*args, **kwargs) ax.add(h) if not ax.getp('hold'): if 'quiver3' in kwargs['description']: if not 'grid' in kwargs: kwargs['grid'] = True if not 'view' in kwargs: kwargs['view'] = 3 else: if not 'box' in kwargs: kwargs['box'] = True ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def contour(self, *args, **kwargs): """Draw a 2D contour plot. Calling:: contour(X, Y, Z) displays a contour plot where the values in the scalar field Z are treated as heights above a plane. The grid components X and Y must either have the same shape as Z or fulfill the requirement len(X)==n and len(Y)==m, where m,n=shape(Z). Calling:: contour(Z) is the same as calling contour(range(n),range(m),Z), where m,n=shape(Z). Calling:: contour(..., n) displays a contour plot with n contour lines (default is 8 lines). Calling:: contour(..., v) displays contours at levels given in the array v. Calling:: contour(..., fmt) uses the color and line style as given in the format string fmt to draw the contour lines (see the plot command for further details on format strings). This overrides the default behavior of using the current colormap to color the contour lines. Calling:: contours(ax, ...) plots into the Axis object ax instead of the current axis. Calling:: contour(...,clabels='on') is the same as calling h=contour(...) followed by clabel(h,'on'). Examples: >>> # draw a contour plot of the peaks function: >>> x = y = linspace(-3, 3, 13) >>> xv, yv = meshgrid(x, y) >>> values = peaks(xv, yv) >>> contour(xv, yv, values) Draw 10 red contour lines with double line width: >>> contour(xv, yv, values, 10, 'r', linewidth=2) Draw contour lines at -2, 0, 2, and 5: >>> contour(xv, yv, values, [-2,0,2,5]) """ if not 'description' in kwargs: kwargs['description'] = 'contour: 2D contours at base' ax, args, nargs = self._check_args(*args) h = Contours(*args, **kwargs) ax.add(h) if not ax.getp('hold'): if 'contour3' in kwargs['description']: if not 'grid' in kwargs: kwargs['grid'] = True if not 'view' in kwargs: kwargs['view'] = 3 else: # contour or contourf if not 'box' in kwargs: kwargs['box'] = True ax.setp(**kwargs) if h.getp('function') == 'contour3': ax.getp('camera').setp(view=3) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def contourf(self, *args, **kwargs): """Draw filled contour plot. Calling:: contourf(...) is the same as calling contour(...) with the exception that the space between the contour lines is filled with colors. Examples: >>> contourf(peaks(), clabel='on', colorbar='on') """ kwargs['description'] = 'contourf: 2D filled contour plot' return self.contour(*args, **kwargs) # 3D plotting
[docs] def pcolor(self, *args, **kwargs): """Draw a 2D pseudocolor plot. Calling:: pcolor(C) draw a pseudocolor plot of the 2D array C. Calling:: pcolor(X,Y,C) same as above, only that the grid is specified by the X and Y arrays. These arrays must either have the same shape as C or fulfill the requirement len(X)==n and len(Y)==m, where m,n=shape(C). Calling:: pcolor(ax, ...) uses the Axis object ax instead of the current axis. Examples: Simple pseudocolor plot: >>> pcolor(peaks(31)) Draw a pseudocolor plot with interpolated shading: >>> pcolor(peaks(), shading='interp', ... colorbar='on', colormap=hot()) """ kwargs['description'] = 'pcolor: pseudocolor plot' ax, args, nargs = self._check_args(*args) h = Surface(*args, **kwargs) ax.add(h) if not ax.getp('hold') and not 'box' in kwargs: kwargs['box'] = True ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def fill3(self): """Draw filled 3D polygons.""" raise NotImplementedError("'fill3' is not implemented.")
[docs] def streamline(self, *args, **kwargs): """Draw streamlines from 2D or 3D vector data. Calling:: streamline(X,Y,Z,U,V,W,startx,starty,startz) will draw streamlines from the 3D vector field with components U,V,W defined on the grid given by X,Y,Z. The arrays U,V,W should all have the same shape and the grid components X,Y,Z must either have the same shape as U or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p=shape(U). The starting positions for the streamlines are defined in the arrays startx, starty, and startz. Calling:: streamline(U,V,W,startx,starty,startz) is the same as above, except that it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p=shape(U). Calling:: streamline(X,Y,U,V,startx,starty) will draw streamlines from the 2D vector field with components U,V defined on the grid given by X,Y. The arrays U,V should have the same shape and the grid componetns X,Y mist either have the same shape as U or fulfill the requirement len(X)==n and len(Y)==m, where m,n=shape(u). The starting positions for the streamlines are defined in the arrays startx and starty. Calling:: streamline(U,V,startx,starty) is the same as above, except that it is assumed that X,Y = meshgrid(range(n),range(m)), where m,n=shape(U). Calling:: streamline(..., stepsize) uses the given step size instead of the default step size of 0.1. Calling:: streamline(ax, ...) uses the Axis object ax instead of the current axis. The streamline command returns a Streams object. Examples: FIXME: Add streamline example. """ if not 'description' in kwargs: kwargs['description'] = "streamline: 2D or 3D streamline" ax, args, nargs = self._check_args(*args) h = Streams(*args, **kwargs) ax.add(h) ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def streamtube(self, *args, **kwargs): """Draw stream tubes from 3D vector data. Calling:: streamtube(...) is the same as calling streamlines(...), except that only 3D vector fields are supported by the streamtube command. The optional positional argument stepsize is not supported either. Calling:: streamtube(..., [scale, n]) will scale the width of the tubes according to scale (default is 1), while the variable n sets the number of points along the circumference of the tube (default is 20). Examples: FIXME: Add streamtube example. """ kwargs['description'] = "streamtube: 3D stream tube" return self.streamline(*args, **kwargs)
[docs] def streamribbon(self, *args, **kwargs): """Draw stream ribbons from 3D vector data. Calling:: streamribbon(...) is the same as calling streamlines(...), except that only 3D vector fields are supported by the streamribbon command. The optional positional argument stepsize is not supported either. Calling:: streamribbon(..., width) specifies the width of the ribbons. Examples: FIXME: Add streamribbon example. """ kwargs['description'] = "streamribbon: 3D stream ribbon" return self.streamline(*args, **kwargs)
[docs] def mesh(self, *args, **kwargs): """Draw a 3D mesh surface. Calling:: mesh(X, Y, Z[, C]) plots the colored mesh defined by scalar field Z defined on the grid given by X and Y. The grid components X and Y must either have the same shape as Z or fulfill the requirement len(X)==n and len(Y)==m, where m,n=shape(Z). The color is determined by the array C which must have the same shape as Z. If the color array C is not given, Z is used as the color array (i.e., C=Z). Calling:: mesh(Z[, C]) is the same as calling mesh(range(n), range(m), Z[, C]), where m,n = shape(Z). Calling:: mesh(ax, ...) plots into the Axis object ax instead of the current axis. @return: A Surface object. Examples: >>> x = y = linspace(-2, 2, 21) >>> xx, yy = meshgrid(x, y) >>> zz = exp(-xx**2)*exp(-yy**2) >>> mesh(xx, yy, zz) """ if not 'description' in kwargs: kwargs['description'] = 'mesh: 3D mesh' ax, args, nargs = self._check_args(*args) h = Surface(*args, **kwargs) ax.add(h) if not ax.getp('hold'): if not 'grid' in kwargs: kwargs['grid'] = True if not 'view' in kwargs: kwargs['view'] = 3 ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def meshc(self, *args, **kwargs): """Draw a mesh with a contour plot beneath. Calling:: meshc(...) is the same as calling mesh(...) only that a contour plot is drawn beneath the mesh. Examples: Draw a mesh with contour lines: >>> x = linspace(-2, 2, 21) >>> xx, yy = meshgrid(x) >>> zz = peaks(xx, yy) >>> meshc(xx, yy, zz) Draw a mesh with 20 contour lines: >>> meshc(xx, yy, zz, clevels=20) Draw a mesh with contour lines at height -0.2, -0.5, 0.2, 0.5: >>> meshc(xx, yy, zz, cvector=[-0.2,-0.5,0.2,0.5]) Draw a mesh with contours and label the contours: >>> meshc(xx, yy, zz, clabels='on') """ kwargs['description'] = 'meshc: 3D mesh with contours at base' return self.mesh(*args, **kwargs)
[docs] def surf(self, *args, **kwargs): """Draw a 3D solid surface. Calling:: surf(...) is the same as calling mesh(...), except that a solid colored surface is drawn instead of a wireframe mesh. Examples: >>> x = linspace(-2, 2, 21) >>> xx, yy = meshgrid(x) >>> zz = xx**2 + yy**2 >>> surf(xx, yy, zz) """ if not 'description' in kwargs: kwargs['description'] = 'surf: 3D surface' return self.mesh(*args, **kwargs)
[docs] def surfc(self, *args, **kwargs): """Draw a solid surface with contours beneath. Calling:: surfc(...) is the same as calling surf(...) only that a contour plot is drawn beneath the surface. """ kwargs['description'] = 'surfc: 3D surface with contours at base' return self.surf(*args, **kwargs)
[docs] def surfl(self, *args, **kwargs): """3D shaded surface with lighting.""" raise NotImplemetedError("'surfl' is not implemented")
[docs] def quiver3(self, *args, **kwargs): """Draw velocity vectors in 3D space. Calling:: quiver3(X, Y, Z, U, V, W) plots arrows from the 3D vector field with components U,V,W at the grid defined by X,Y,Z. The shape of the three vector components is assumed to be the same, while the grid components must either have the same shape as U or fulfill the requirements len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p=shape(U). Calling:: quiver3(Z,U,V,W) gives the same result as above, but it is assumed that X,Y = meshgrid(range(n),range(m)), where m,n=shape(Z). Calling:: quiver3(..., s) scales the arrows by the scale factor given in s. The default is s=1, while a value of s=0 turns off automatic scaling. Calling:: quiver3(..., 'filled') fills the arrows. Calling:: quiver3(..., fmt) sets the specification on the arrows as given in the format string fmt (see the plot command for further information on format strings). Calling:: quiver3(ax, ...) plots the vectors in the Axis object ax instead of the current axis. @return: A VelocityVectors object. Examples: Draw the "radius vector field" v = (x,y,z): >>> x = y = linspace(-3,3,4) >>> xv, yv, zv = meshgrid(x, y, sparse=False) >>> yv, vv, wv = xv, yv, zv >>> quiver3(xv, yv, zv, uv, uv, wv, 'filled', 'r', ... axis=[-7,7,-7,7,-7,7]) Draw the path of a projectile as a function of time: FIXME: This example is taken from ... >>> vz = 10 # Velocity >>> a = -32 # Acceleration >>> t = linspace(0,1,11) >>> z = vz*t + 1./2*a*t**2 >>> vx = 2 >>> x = vx*t >>> vy = 3 >>> y = vy*t >>> u = gradient(x) >>> v = gradient(y) >>> w = gradient(z) >>> scale = 0 >>> quiver3(x,y,z,u,v,w,0,view=[70,18],grid='on', ... axis=[0,3.5,0,3,-10,2]) """ kwargs['description'] = "quiver3: 3D vector field" return self.quiver(*args, **kwargs)
[docs] def contour3(self, *args, **kwargs): """Draw 3D contour plot. Calling:: contour3(...) is the same as calling contour(...), except that the contours are drawn at their coresponding height level. Examples: >>> contour3(peaks()) """ kwargs['description'] = "contour3: 3D contours at surface" return self.contour(*args, **kwargs) # Volume plotting
[docs] def slice_(self, *args, **kwargs): """Draw volumetric slice plot. Calling:: slice_(X,Y,Z,V,Sx,Sy,Sz) draws orthogonal slice planes through the volumetric data set V defined on the grid with components X, Y, and Z. The grid components must either have the same shape as V or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p=shape(V). The Sx, Sy, and Sz arrays defines the slice planes in the x, y, and z direction, respectively. Calling:: slice_(V,Sx,Sy,Sz) is the same as calling slice_(range(n),range(m),range(p),V,Sx,Sy,Sz), where m,n,p = shape(V). Calling:: slice_(X,Y,Z,V,XI,YI,ZI) draws slices through the volumetric data set V along the surface defined by the arrays XI,YI,ZI. Calling:: slice_(V,XI,YI,ZI) is the same as calling slice_(range(n),range(m),range(p)),V,XI,YI,ZI), where m,n,p = shape(V). Calling:: slice_(..., method) sets which interpolation method to be used, where method can be either 'linear' (default), 'cubic', or 'nearest'. Calling:: slice(ax, ...) plots into the Axis object ax instead of the current axis. @return: A Volume object. Examples: Visualize the function x*exp(-x**2-y**2-z**2) over the range -2 > x,y,z < 2: >>> xx, yy, zz = meshgrid(linspace(-2,2,21), linspace(-2,2,17), ... linspace(-2,2,25)) >>> vv = x*exp(-xx**2-yy**2-zz**2) >>> slice_(xx, yy, zz, vv, [-1.2,.8,2], 2, [-2,-.2]) """ if not 'description' in kwargs: kwargs['description'] = 'slice_: volumetric slices' ax, args, nargs = self._check_args(*args) h = Volume(*args, **kwargs) ax.add(h) if not ax.getp('hold'): if 'slice_' in kwargs['description']: if not 'grid' in kwargs: kwargs['grid'] = True if not 'view' in kwargs: kwargs['view'] = 3 ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def contourslice(self, *args, **kwargs): """Draw contour lines in slice planes. Calling:: contourslice(X,Y,Z,V,Sx,Sy,Sz) will draw contour lines in planes aligned with the coordinate axes at the points in the arrays Sx, Sy, and Sz. The arrays X, Y, and Z defines the grid coordinates for the volume V and they must either have the same shape as V or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p = shape(V). Calling:: contourslice(V,Sx,Sy,Sz) is the same as above, but it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p = shape(V). Calling:: contourslice(X,Y,Z,V,XI,YI,ZI) will draw contour lines through the volume V along the surface given in the arrays XI, YI, and ZI. Calling:: contourslice(V,XI,YI,ZI) is the same as above, but it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p = shape(V). Calling:: contourslice(..., n) will draw n contour lines per plane instead of the default of five contour lines. Calling:: contourslice(..., v) will draw contour lines at the levels given in the array v. Calling:: contourslice(ax, ...) uses the Axis object ax instead of the current axis. @return: A Volume object. Example: >>> xx, yy, zz = meshgrid(linspace(-2,2,21), linspace(-2,2,17), ... linspace(-2,2,25)) >>> vv = xx*exp(-xx**2-yy**2-zz**2) >>> contourslice(xx, yy, zz, vv, [-.7,.7], [], [0], view=3) """ kwargs['description'] = 'contourslice: contours in slice planes' return self.slice_(*args, **kwargs)
[docs] def coneplot(self, *args, **kwargs): """Draw a 3D cone plot. Calling:: coneplot(X,Y,Z,U,V,W,Cx,Cy,Cz) draws velocity vectors as cones from the 3D vector field defined by U, V, and W at the points given in the arrays Cx, Cy, and Cz. The arrays X, Y, and Z defines the grid coordinates for vector field. The shape of U, V, and W is assumed to be the same, while the grid components must either have the same shape as U or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p=shape(U). Calling:: coneplot(U,V,W,Cx,Cy,Cz) is the same as above, but it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p = shape(U). Calling:: coneplot(..., scale) will automatically scale the cones by the factor scale (default is 1). To turn of automatic scaling, use a scale value of 0. Calling:: coneplot(..., C) uses the colors in the array C to color the cones (C must have the same shape as U). Calling:: coneplot(..., 'quiver') will plot arrows instead of cones. Calling:: coneplot(ax, ...) uses the Axis object ax instead of the current axis. @return: A Streams object. Examples: FIXME: Add conplot example. """ kwargs['description'] = "coneplot: 3D cone plot" ax, args, nargs = self._check_args(*args) h = Streams(*args, **kwargs) ax.add(h) ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def streamslice(self, *args, **kwargs): """Draw streamlines in axis aligned slice planes. Calling:: streamslice(X,Y,Z,U,V,W,startx,starty,startz) will draw streamlines (with direction arrows) from the 3D vector field with components U, V, and W in planes aligned with the coordinate axes at the points given in the arrays startx, starty, and startz. The arrays U, V, and W must all have the same shape and the grid coordinates given in the arrays X, Y, and Z must either have the same shape as U or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p=shape(U). Calling:: streamslice(U,V,W,startx,starty,startz) is the same as above, except that it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p=shape(U). Calling:: streamslice(X,Y,U,V) will draw streamlines from the 2D vector field with components U and V. The vector components must have equal shape and the arrays X and Y should either have the same shape as U or fulfill the requirement len(X)==n and len(Y)==m, where m,n=shape(U). Calling:: streamslice(U,V) is the same as above, except that it is assumed that X,Y = meshgrid(range(n),range(m)), where m,n=shape(U). Calling:: streamslice(..., 'arrows') will display direction arrows (default). Calling:: streamslice(..., 'noarrows') will not display direction arrows. Calling:: streamslice(ax, ...) uses the Axis object ax instead of the current axis. @return: A ??? object. Examples: >>> import scipy >>> wind = scipy.io.loadmat('wind.mat') >>> x = wind['x'] >>> y = wind['y'] >>> z = wind['z'] >>> u = wind['u'] >>> v = wind['v'] >>> w = wind['w'] >>> streamslice(x,y,z,u,v,w,[],[],[4],daspect=[1,1,1]) """ raise NotImplementedError("'streamslice' is not implemented.")
[docs] def isocaps(self, *args, **kwargs): """Draw isosurface end caps.""" raise NotImplementedError("'isocaps' is not implemented.")
[docs] def isosurface(self, *args, **kwargs): """Draw isosurfaces from 3D scalar fields. Calling:: isosurface(X,Y,Z,V,isovalue) creates an isosurface for the volume V at the iso value given by isovalue. The arrays X, Y, and Z defines the grid for the volume V and they must either have the same shape as V or fulfill the requirement len(X)==n, len(Y)==m, and len(Z)==p, where m,n,p = shape(V). Calling:: isosurface(V,isovalue) is the same as above, but it is assumed that X,Y,Z = meshgrid(range(n),range(m),range(p)), where m,n,p = shape(V). Calling:: isosurface(..., C) uses the colors in the array C instead of the colors in the scalar field V (C and V must have the same shape). @return: A Volume object. Examples: >>> x, y, z, v = flow() >>> isosurface(x, y, z, v, -3, daspect=[1,1,1]) """ kwargs['description'] = 'isosurface: isosurface extractor' ax, args, nargs = self._check_args(*args) h = Volume(*args, **kwargs) ax.add(h) if not ax.getp('hold') and not 'view' in kwargs: kwargs['view'] = 3 ax.setp(**kwargs) self.gcf().setp(**kwargs) self.setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() return h
[docs] def show(self): """Redraw the current figure.""" self._replot()
[docs] def hidden(self, *args): """Toggle hidden line removal in the current axis. Calling:: hidden(state) turns hidden line removal on if state is 'on' (or True) and off if state is 'off' (or False). Hidden line removal is turned on by default. Calling:: hidden() toggles the hidden state. Note: Some backends has no support for hidden line removal (e.g., VTK). """ ax = self.gca() nargs = len(args) if nargs == 1: ax.setp(hidden=args[0]) elif nargs == 0: ax.toggle('hidden') else: raise TypeError("hidden: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def view(self, *args): """Specify viewpoint. Calling:: view(azimuth, elevation) sets the viewpoint according to azimuth (horizontal rotation) and elevation (vertical). Both azimuth and elevation should be given in degrees. Calling:: view([azimuth, elevation]) is the same as above. Calling:: view(2) changes the view to the default 2D view. Calling:: view(3) changes the view to the default 3D view. Calling:: view(ax, ...) uses the Axis object ax instead of the current axis. Examples: >>> surf(peaks()) >>> view(2) # the default 2D view >>> view(40,65) # azimuth=40 and elevation=65 >>> view(3) # back to the default 3D view >>> surf(peaks(),view=[35,75]) # as a keyword argument """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') # Allow both view(az,el) and view([az,el]) if nargs == 1: if isinstance(args[0], (tuple,list)): args = args[0]; nargs = len(args) elif isinstance(args[0], (int,float)) and args[0] in (2,3): cam.setp(view=args[0]) if nargs == 2: cam.setp(azimuth=args[0], elevation=args[1]) if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camdolly(self, *args): """Dolly camera. Calling:: camdolly(dx, dy, dz) moves the camera position along the direction of projection. Calling:: camdolly(ax, ...) uses the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 3: cam.setp(camdolly=args) else: raise TypeError("camdolly: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camlookat(self, *args): """Move camera and target to view specified objects. Calling:: camlookat(obj) views the PlotProperties object obj. Calling:: camlookat(ax) views the objects in the Axis object ax. Calling:: camlookat() views the objects in the current axes. """ ax, args, nargs = self._check_args(*args) if nargs == 0: self.gca().getp('camera').setp(camlookat=self.gca()) elif nargs == 1: tmparg = args[0] if isinstance(tmparg, Axis): tmparg.getp('camera').setp(camlookat=tmparg) elif isinstance(tmparg, PlotProperties): self.gca().getp('camera').setp(camlookat=tmparg) else: raise ValueError( "camlookat: object must be either %s or %s, not %s" % \ (type(Axis), type(PlotProperties), type(tmparg))) else: raise TypeError("camlookat: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camproj(self, *args): """Camera projection. Calling:: camproj() returns the camera projection of the current axis. Calling:: camproj(projeciton) sets the projection of the camera to projection, where projection can be either 'orthographic' (default) or 'perspective'. Calling:: camproj(ax, ...) sets or gets the camera projection of the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 0: return cam.getp('camproj') elif nargs == 1: cam.setp(camproj=args[0]) else: raise TypeError("camproj: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camup(self, *args): """Camera up vector. Calling:: camup() returns the up vector of the camera in the current axis. Calling:: camup([x, y, z]) sets the camera up vector. Calling:: camup(x, y, z) is the same as the above. Calling:: camup(ax, ...) sets or gets the up vector for the camera in the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 0: return cam.getp('camup') elif nargs == 1: cam.setp(camup=args[0]) elif nargs == 3: cam.setp(camup=args) else: raise TypeError("camup: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camroll(self, *args): """Roll camera. Calling:: camroll(angle) rotates the camera about the direction of projection. Calling:: camroll(ax, ...) rotates the camera in the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 1: cam.setp(camroll=args[0]) else: raise TypeError("camroll: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camva(self, *args): """Camera view angle. Calling:: camva() returns the camera view angle of the current axis. Calling:: camva(angle) sets the camera view angle. Calling:: camva(ax, ...) sets or gets the camera view angle in the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 0: return cam.getp('camva') elif nargs == 1: cam.setp(camva=args[0]) else: raise TypeError("camva: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camzoom(self, *args): """Zoom camera. Calling:: camzoom(factor) zooms the camera the specified factor. A value greater than 1 is a zoom-in, while a value less than 1 is a zoom-out. Calling:: camzoom(ax, ...) zooms the camera in the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 1: cam.setp(camzoom=args[0]) else: raise TypeError("camzoom: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def campos(self, *args): """Camera position. Calling:: campos() returns the position of the camera in the current axis. Calling:: campos([x,y,z]) sets the camera position. Calling:: campos(x,y,z) is the same as above. Calling:: campos(ax, ...) sets or gets the position of the camera in the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 0: return cam.getp('campos') elif nargs == 1: cam.setp(campos=args[0]) elif nargs == 3: cam.setp(campos=args) else: raise TypeError("campos: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camtarget(self, *args): """Camera target. Calling:: camtarget() returns the camera target of the current axis. Calling:: camtarget([x,y,z]) sets the target for the camera. Calling:: camtarget(x,y,z) is the same as above. Calling:: camtarget(ax, ...) sets or gets the camera target in the Axis object ax instead of the current axis.. """ ax, args, nargs = self._check_args(*args) cam = ax.getp('camera') if nargs == 0: return cam.getp('camtarget') elif nargs == 1: cam.setp(camtarget=args[0]) elif nargs == 3: cam.setp(camtarget=args) else: raise TypeError("camtarget: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def camlight(self, *args, **kwargs): """Create or set position of a light. Calling:: camlight('headlight') creates a light in the current axis at the position of the camera. Calling:: camlight('right') creates a light right and up from the camera in the current axis. Calling:: camlight('left') creates a light left and up from the camera. Calling:: camlight() is the same as camlight('right'). Calling:: camlight(azimuth, elevation) creates a light at azimuth, elevation (both given in degrees) from the camera. Calling:: camlight(..., style) sets the style of the light, where style can be either 'local' (default) or 'inifinite'. Calling:: camlight(l, ...) places Light object l at the specified position. @return: A Light object. """ # should be implemented in backend raise NotImplementedError("'camlight' not implemented in class %s" % \ self.__class__.__name__)
[docs] def light(self, **kwargs): """Add a light to the current axis. Calling: light() will add a light to the current axis with default values for all light properties. Calling:: light(prop1=value1, prop2=value2, ...) adds a light with properties as given in the keyword arguments. @return: A Light object. """ l = Light(**kwargs) self.gca().setp(light=l) if self.getp('interactive') and self.getp('show'): self._replot() return l
[docs] def colormap(self, *args): """Specify colormap. Calling:: colormap(map) uses the colormap in map as the current colormap (map is dependent on the current backend). Calling:: colormap('default') sets the colormap to the default colormap, i.e., jet. Calling:: map = colormap() returns the current colormap. Calling:: colormap(ax, ...) uses the figure corresponding to the Axis object ax instead of the current figure. """ ax, args, nargs = self._check_args(*args) if nargs == 0: return ax.getp('colormap') elif nargs == 1: if args[0] == 'default': ax.setp(colormap=self.jet()) else: ax.setp(colormap=args[0]) # backend dependent else: raise TypeError("colormap: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def caxis(self, *args): """Set the limits for the color axis. Calling:: caxis([cmin, cmax]) changes the limits for the color axis to range from cmin to cmax. Calling:: caxis(cmin, cmax) gives the same result as above. Calling:: caxis('manual') freezes the limits at the current range. Calling:: caxis('auto') uses autoranging for the color axis limits (default). Calling:: cmin, cmax = caxis() returns the current color axis limits. Calling:: caxis(ax, ...) uses the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 0: cmin, cmax = ax.getp('caxis') if cmin is None or cmax is None: cmin, cmax = ax.getp('zlim') return cmin, cmax elif nargs == 1: if isinstance(args[0], (tuple,list)): args = args[0]; nargs = len(args) elif isinstance(args[0], str) and args[0] in ['auto', 'manual']: ax.setp(caxismode=args[0]) else: raise TypeError("caxis: argument must be %s, not %s" % \ ((type(list),type(tuple),type(str)), type(args[0]))) if nargs == 2: ax.setp(caxis=args) if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def colorbar(self, *args): """Display a color bar. Calling:: colorbar() displays a colorbar in the current axis. Calling:: colorbar('off') removes the colorbar from the current axis. Calling:: colorbar(loc) displays a colorbar in the current axis at the location specified by loc, where loc may be any of the following: * 'North' - inside plot box near top * 'South' - inside bottom * 'East' - inside right * 'West' - inside left * 'NorthOutside' - outside plot box near top * 'SouthOutside' - outside bottom * 'EastOutside' - outside right * 'WestOutside' - outside left Calling:: colorbar(ax, ...) uses the Axis object ax instead of the current axis. @return: A Colorbar object. """ ax, args, nargs = self._check_args(*args) cbar = ax.getp('colorbar') if nargs == 0: cbar.setp(visible=True) elif nargs == 1: if args[0] == 'off' or not args[0]: cbar.setp(visible=False) else: cbar.setp(visible=True) cbar.setp(cblocation=args[0]) else: raise TypeError("colorbar: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot() return cbar
[docs] def shading(self, *args): """Control the color shading of surfaces. Calling:: shading(mode) changes the shading mode in the current axis to the one specified by by mode. Valid modes are 'flat', 'interp' (interpolated or Gouraud) and 'faceted' (default). Calling:: shading(ax, ...) uses the Axis object ax instead of the current axis. """ ax, args, nargs = self._check_args(*args) if nargs == 1: ax.setp(shading=str(args[0])) else: raise TypeError("shading: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def brighten(self, *args): """Brighten or darken the color map.""" raise NotImplementedError("'brighten' not implemented in class %s" % \ self.__class__.__name__)
[docs] def clabel(self, *args): """Control labeling of contours. Calling:: clabel(obj, 'on') adds height labels to a contour plot (obj must be a Contours object). Calling:: clabel(obj, 'off') removes the labeling of the contour lines (default). """ nargs = len(args) if nargs == 2: obj = args[0] state = args[1] obj.setp(clabels=state) else: raise TypeError("clabel: wrong number of arguments") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def box(self, *args): """Display a box around the boundaries of the current axis. Calling:: box('on') displays a box at the boundaries of the current axis. Calling:: box('off') turns off the box. Calling:: box() toggles the display of a box in the current axis. Calling:: box(ax, ...) uses the Axis object ax instead of the current axis. Note: box(True) and box(False) is the same as box('on') and box('off'), respectively. """ ax, args, nargs = self._check_args(*args) if nargs == 0: ax.toggle('box') elif nargs == 1: ax.setp(box=args[0]) else: raise TypeError("box: wrong number of argumnts") if self.getp('interactive') and self.getp('show'): self._replot()
[docs] def material(self, *args): """Set the material reflectance mode. Calling:: material([ka, kd, ks[, n[, sc]]]) changes the ambient/diffuse/specular strength, specular exponent, and specular color reflectance of objects. Calling:: material(ka, kd, ks[, n[, sc]]) is the same as above. Calling:: material(mode) sets the material mode, where mode can be one of the following strings: * 'shiny' - shiny objects * 'dull', - dull objects * 'metal' - metallic objects * 'default' - default material properties """ modes = {'shiny': (None, None, None, None, None), 'dull': (None, None, None, None, None), 'metal': (None, None, None, None, None), 'default': (None, None, None, None, None), } ka, kd, ks, n, sc = modes['default'] nargs = len(args) if nargs == 1: if isinstance(args[0], (tuple,list)): args = args[0] nargs = len(args) elif args[0] in modes.keys(): ka, kd, ks, n, sc = modes[args[0]] if nargs >= 3: ka, kd, ks = args[:3] if nargs >= 4: n = args[3] if nargs == 5: sc = args[4] if nargs < 1 or nargs > 5: raise ValueError("material: wrong nmumber of arguments") kwargs = {} if ka is not None: kwargs['ambient'] = ka if kd is not None: kwargs['diffuse'] = kd if ks is not None: kwargs['specular'] = ks if n is not None: kwargs['specularpower'] = n #if sc is not None: #kwargs['specularcolorreflectance'] = sc ax = self.gca() items = ax.getp('plotitems') for i in range(ax.getp('numberofitems')): items[i].getp('material').setp(**kwargs) if self.getp('interactive') and self.getp('show'): self._replot() # Colormap methods:
[docs] def hsv(self, m=None): """Hue-saturation-value color map.""" raise NotImplementedError('hsv not implemented in class %s' % \ self.__class__.__name__)
[docs] def hot(self, m=None): """Black-red-yellow-white color map.""" raise NotImplementedError('hot not implemented in class %s' % \ self.__class__.__name__)
[docs] def gray(self, m=None): """Linear gray-scale color map.""" raise NotImplementedError('gray not implemented in class %s' % \ self.__class__.__name__)
[docs] def bone(self, m=None): """Gray-scale with a tinge of blue color map.""" raise NotImplementedError('bone not implemented in class %s' % \ self.__class__.__name__)
[docs] def copper(self, m=None): """Linear copper-tone color map.""" raise NotImplementedError('copper not implemented in class %s' % \ self.__class__.__name__)
[docs] def pink(self, m=None): """Pastel shades of pink color map.""" raise NotImplementedError('pink not implemented in class %s' % \ self.__class__.__name__)
[docs] def white(self, m=None): """All white color map.""" raise NotImplementedError('white not implemented in class %s' % \ self.__class__.__name__)
[docs] def flag(self, m=None): """Alternating red, white, blue, and black color map.""" raise NotImplementedError('flag not implemented in class %s' % \ self.__class__.__name__)
[docs] def lines(self, m=None): """Color map with the line colors.""" raise NotImplementedError('lines not implemented in class %s' % \ self.__class__.__name__)
[docs] def colorcube(self, m=None): """Enhanced color-cube color map.""" raise NotImplementedError('colorcube not implemented in class %s' % \ self.__class__.__name__)
[docs] def vga(self, m=None): """Windows colormap for 16 colors.""" raise NotImplementedError('vga not implemented in class %s' % \ self.__class__.__name__)
[docs] def jet(self, m=None): """Variant of hsv.""" raise NotImplementedError('jet not implemented in class %s' % \ self.__class__.__name__)
[docs] def prism(self, m=None): """Prism color map.""" raise NotImplementedError('prism not implemented in class %s' % \ self.__class__.__name__)
[docs] def cool(self, m=None): """Shades of cyan and magenta color map.""" raise NotImplementedError('cool not implemented in class %s' % \ self.__class__.__name__)
[docs] def autumn(self, m=None): """Shades of red and yellow color map.""" raise NotImplementedError('autumn not implemented in class %s' % \ self.__class__.__name__)
[docs] def spring(self, m=None): """Shades of magenta and yellow color map.""" raise NotImplementedError('spring not implemented in class %s' % \ self.__class__.__name__)
[docs] def winter(self, m=None): """Shades of blue and green color map.""" raise NotImplementedError('winter not implemented in class %s' % \ self.__class__.__name__)
[docs] def summer(self, m=None): """Shades of green and yellow color map.""" raise NotImplementedError('summer not implemented in class %s' % \ self.__class__.__name__)
def turn_off_plotting(namespace=globals()): """Call turn_off_plotting(globals()) to turn off all plotting.""" use(namespace['plt'], namespace, True) def use(plt, namespace=globals(), neutralize=False): """ Export the namespace of backend instance to namespace. If neutralize is True, the plt object will be replaced by a scitools.misc.DoNothing object so that no plotting command will do anything. This can be used to efficiently turn off all plotting in a program. Just call turn_off_plotting(globals()) before the first plot command in your program. """ plt_dict = {} plt_orig = plt if neutralize: import scitools.misc plt = scitools.misc.DoNothing() plt_dict['plt'] = plt for item in plt_orig.__dict__: plt_dict[item] = eval('plt.'+item) for item in dir(plt_orig.__class__): if not '__' in item: plt_dict[item] = eval('plt.'+item) namespace.update(plt_dict) # Add to global namespace namespace['savefig'] = namespace['hardcopy'] # synonym def get_backend(): return plt._g namespace['get_backend'] = get_backend # desired global func # If this module is imported try: __all__ except: __all__ = ['plt'] try: for item in plt_dict.keys(): __all__.append(item) except: pass del(__all__)
[docs]def debug(plt, level=10): def print_(item, spaces=10): """Indent print""" pref = ' '*spaces print pref+('\n'+pref).join((str(item)).split('\n')) print "plt:" print plt if level > 0: for fignr in plt._figs: print "\nFig %d:" % fignr fig = plt._figs[fignr] print fig if level > 1: axes_ = fig.getp('axes') for axnr in axes_.keys(): print_("\nAx %d:" % axnr, 4) ax = axes_[axnr] print_(ax, 8) if level > 2: print_("\nCamera:", 4) print_(ax.getp('camera'), 8) print_("\nColorbar:", 4) print_(ax.getp('colorbar'), 8) print_("\nLights:", 4) for light_ in ax.getp('lights'): print_(light_, 8) print_("\nPlotitems:", 4) for i, item in enumerate(ax.getp('plotitems')): print_('item number %s %s:' %(i, repr(item)), 8) print_(item, 12) if level > 3: print_("Material:", 12) print_(item.getp('material'), 16) print ''