From: <ef...@us...> - 2007-11-18 19:06:54
|
Revision: 4377 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4377&view=rev Author: efiring Date: 2007-11-18 11:06:49 -0800 (Sun, 18 Nov 2007) Log Message: ----------- Add experimental "pcolorfast" for fast interactive pcolor plots This will need more discussion and work, but it illustrates the potential for very fast pcolor-type plotting with all three grid types: uniform, irregular but rectilinear, and general quadrilateral. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/image.py Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2007-11-18 19:02:55 UTC (rev 4376) +++ trunk/matplotlib/lib/matplotlib/axes.py 2007-11-18 19:06:49 UTC (rev 4377) @@ -3706,8 +3706,8 @@ xs = [thisx for thisx, b in zip(xs, mask) if b] ys = [thisy for thisy, b in zip(ys, mask) if b] return xs, ys - + if capsize > 0: plot_kw = { 'ms':2*capsize, @@ -3733,16 +3733,16 @@ # can't use numpy logical indexing since left and # y are lists leftlo, ylo = xywhere(left, y, xlolims) - + caplines.extend( self.plot(leftlo, ylo, ls='None', marker=mlines.CARETLEFT, **plot_kw) ) xlolims = ~xlolims - leftlo, ylo = xywhere(left, y, xlolims) + leftlo, ylo = xywhere(left, y, xlolims) caplines.extend( self.plot(leftlo, ylo, 'k|', **plot_kw) ) else: caplines.extend( self.plot(left, y, 'k|', **plot_kw) ) if xuplims.any(): - + rightup, yup = xywhere(right, y, xuplims) caplines.extend( self.plot(rightup, yup, ls='None', marker=mlines.CARETRIGHT, **plot_kw) ) xuplims = ~xuplims @@ -3775,7 +3775,7 @@ if uplims.any(): xup, upperup = xywhere(x, upper, uplims) - + caplines.extend( self.plot(xup, upperup, ls='None', marker=mlines.CARETUP, **plot_kw) ) uplims = ~uplims xup, upperup = xywhere(x, upper, uplims) @@ -4762,6 +4762,177 @@ return collection pcolormesh.__doc__ = cbook.dedent(pcolormesh.__doc__) % martist.kwdocd + def pcolorfast(self, *args, **kwargs): + """ + Experimental; this is a version of pcolor that + does not draw lines, that provides the fastest + possible rendering with the Agg backend, and that + can handle any quadrilateral grid. + + pcolor(*args, **kwargs): pseudocolor plot of a 2-D array + + Function signatures + + pcolor(C, **kwargs) + pcolor(xr, yr, C, **kwargs) + pcolor(x, y, C, **kwargs) + pcolor(X, Y, C, **kwargs) + + C is the 2D array of color values corresponding to quadrilateral + cells. Let (nr, nc) be its shape. C may be a masked array. + + pcolor(C, **kwargs) is equivalent to + pcolor([0,nc], [0,nr], C, **kwargs) + + xr, yr specify the ranges of x and y corresponding to the rectangular + region bounding C. If xr = [x0, x1] and yr = [y0,y1] then + x goes from x0 to x1 as the second index of C goes from 0 to nc, + etc. (x0, y0) is the outermost corner of cell (0,0), and (x1, y1) + is the outermost corner of cell (nr-1, nc-1). All cells are + rectangles of the same size. This is the fastest version. + + x, y are 1D arrays of length nc+1 and nr+1, respectively, giving + the x and y boundaries of the cells. Hence the cells are + rectangular but the grid may be nonuniform. The speed is + intermediate. (The grid is checked, and if found to be + uniform the fast version is used.) + + X and Y are 2D arrays with shape (nr+1, nc+1) that specify + the (x,y) coordinates of the corners of the colored + quadrilaterals; the quadrilateral for C[i,j] has corners at + (X[i,j],Y[i,j]), (X[i,j+1],Y[i,j+1]), (X[i+1,j],Y[i+1,j]), + (X[i+1,j+1],Y[i+1,j+1]). The cells need not be rectangular. + This is the most general, but the slowest to render. It may + produce faster and more compact output using ps, pdf, and + svg backends, however. + + Note that the the column index corresponds to the x-coordinate, + and the row index corresponds to y; for details, see + the "Grid Orientation" section below. + + Optional keyword args are shown with their defaults below (you must + use kwargs for these): + + * cmap = cm.jet : a cm Colormap instance from cm + + * norm = Normalize() : mcolors.Normalize instance + is used to scale luminance data to 0,1. + + * vmin=None and vmax=None : vmin and vmax are used in conjunction + with norm to normalize luminance data. If either are None, the + min and max of the color array C is used. If you pass a norm + instance, vmin and vmax will be None + + * alpha=1.0 : the alpha blending value + + Return value is an image if a regular or rectangular grid + is specified, and a QuadMesh collection in the general + quadrilateral case. + + """ + + if not self._hold: self.cla() + + alpha = kwargs.pop('alpha', 1.0) + norm = kwargs.pop('norm', None) + cmap = kwargs.pop('cmap', None) + vmin = kwargs.pop('vmin', None) + vmax = kwargs.pop('vmax', None) + if norm is not None: assert(isinstance(norm, mcolors.Normalize)) + if cmap is not None: assert(isinstance(cmap, mcolors.Colormap)) + + C = args[-1] + nr, nc = C.shape + if len(args) == 1: + style = "image" + x = [0, nc+1] + y = [0, nr+1] + elif len(args) == 3: + x, y = args[:2] + x = npy.asarray(x) + y = npy.asarray(y) + if x.ndim == 1 and y.ndim == 1: + if x.size == 2 and y.size == 2: + style = "image" + else: + dx = npy.diff(x) + dy = npy.diff(y) + if (npy.ptp(dx) < 0.01*npy.abs(dx.mean()) and + npy.ptp(dy) < 0.01*npy.abs(dy.mean())): + style = "image" + style = "pcolorimage" + elif x.ndim == 2 and y.ndim == 2: + style = "quadmesh" + else: + raise TypeError("arguments do not match valid signatures") + else: + raise TypeError("need 1 argument or 3 arguments") + + if style == "quadmesh": + + # convert to one dimensional arrays + # This should also be moved to the QuadMesh class + C = ma.ravel(C) # data point in each cell is value at lower left corner + X = x.ravel() + Y = y.ravel() + Nx = nc+1 + Ny = nr+1 + + # The following needs to be cleaned up; the renderer + # requires separate contiguous arrays for X and Y, + # but the QuadMesh class requires the 2D array. + coords = npy.empty(((Nx * Ny), 2), npy.float64) + coords[:, 0] = X + coords[:, 1] = Y + + # The QuadMesh class can also be changed to + # handle relevant superclass kwargs; the initializer + # should do much more than it does now. + collection = mcoll.QuadMesh(nc, nr, coords, 0) + collection.set_alpha(alpha) + collection.set_array(C) + collection.set_cmap(cmap) + collection.set_norm(norm) + self.add_collection(collection) + xl, xr, yb, yt = X.min(), X.max(), Y.min(), Y.max() + ret = collection + + else: + # One of the image styles: + xl, xr, yb, yt = x[0], x[-1], y[0], y[-1] + if style == "image": + + im = mimage.AxesImage(self, cmap, norm, + interpolation='nearest', + origin='lower', + extent=(xl, xr, yb, yt), + **kwargs) + im.set_data(C) + im.set_alpha(alpha) + self.images.append(im) + ret = im + + if style == "pcolorimage": + im = mimage.PcolorImage(self, x, y, C, + cmap=cmap, + norm=norm, + alpha=alpha, + **kwargs) + self.images.append(im) + ret = im + + self._set_artist_props(ret) + if vmin is not None or vmax is not None: + ret.set_clim(vmin, vmax) + else: + ret.autoscale_None() + self.update_datalim(npy.array([[xl, yb], [xr, yt]])) + self.autoscale_view(tight=True) + return ret + + + + def contour(self, *args, **kwargs): kwargs['filled'] = False return mcontour.ContourSet(self, *args, **kwargs) @@ -4822,14 +4993,14 @@ ticks on bottom and the returned axes will have ticks on the top """ - + ax2 = self.figure.add_axes(self.get_position(), sharey=self, frameon=False) ax2.xaxis.tick_top() ax2.xaxis.set_label_position('top') self.xaxis.tick_bottom() return ax2 - + #### Data analysis Modified: trunk/matplotlib/lib/matplotlib/image.py =================================================================== --- trunk/matplotlib/lib/matplotlib/image.py 2007-11-18 19:02:55 UTC (rev 4376) +++ trunk/matplotlib/lib/matplotlib/image.py 2007-11-18 19:06:49 UTC (rev 4377) @@ -410,9 +410,107 @@ raise RuntimeError('Cannot change colors after loading data') cm.ScalarMappable.set_cmap(self, norm) +class PcolorImage(martist.Artist, cm.ScalarMappable): + def __init__(self, ax, + x=None, + y=None, + A=None, + cmap = None, + norm = None, + **kwargs + ): + """ + cmap defaults to its rc setting + cmap is a colors.Colormap instance + norm is a colors.Normalize instance to map luminance to 0-1 + Additional kwargs are matplotlib.artist properties + """ + martist.Artist.__init__(self) + cm.ScalarMappable.__init__(self, norm, cmap) + self.axes = ax + self._rgbacache = None + self.update(kwargs) + self.set_data(x, y, A) + + def make_image(self, magnification=1.0): + if self._A is None: + raise RuntimeError('You must first set the image array') + fc = self.axes.get_frame().get_facecolor() + bg = mcolors.colorConverter.to_rgba(fc, 0) + bg = (npy.array(bg)*255).astype(npy.uint8) + x0, y0, v_width, v_height = self.axes.viewLim.get_bounds() + l, b, width, height = self.axes.bbox.get_bounds() + width *= magnification + height *= magnification + if self.check_update('array'): + A = self.to_rgba(self._A, alpha=self._alpha, bytes=True) + self._rgbacache = A + if self._A.ndim == 2: + self.is_grayscale = self.cmap.is_gray() + else: + A = self._rgbacache + im = _image.pcolor2(self._Ax, self._Ay, A, + height, width, + (x0, x0+v_width, y0, y0+v_height), + bg) + im.is_grayscale = self.is_grayscale + return im + + def draw(self, renderer, *args, **kwargs): + if not self.get_visible(): return + im = self.make_image(renderer.get_image_magnification()) + l, b, widthDisplay, heightDisplay = self.axes.bbox.get_bounds() + renderer.draw_image(l, b, im, self.axes.bbox) + + + def set_data(self, x, y, A): + A = ma.asarray(A) + if x is None: + x = npy.arange(0, A.shape[1]+1, dtype=npy.float64) + else: + x = npy.asarray(x, npy.float64).ravel() + if y is None: + y = npy.arange(0, A.shape[0]+1, dtype=npy.float64) + else: + y = npy.asarray(y, npy.float64).ravel() + + if A.shape[:2] != (y.size-1, x.size-1): + print A.shape + print y.size + print x.size + raise ValueError("Axes don't match array shape") + if A.ndim not in [2, 3]: + raise ValueError("A must be 2D or 3D") + if A.ndim == 3 and A.shape[2] == 1: + A.shape = A.shape[:2] + self.is_grayscale = False + if A.ndim == 3: + if A.shape[2] in [3, 4]: + if (A[:,:,0] == A[:,:,1]).all() and (A[:,:,0] == A[:,:,2]).all(): + self.is_grayscale = True + else: + raise ValueError("3D arrays must have RGB or RGBA as last dim") + self._A = A + self._Ax = x + self._Ay = y + self.update_dict['array'] = True + + def set_array(self, *args): + raise NotImplementedError('Method not supported') + + def set_alpha(self, alpha): + """ + Set the alpha value used for blending - not supported on + all backends + + ACCEPTS: float + """ + martist.Artist.set_alpha(self, alpha) + self.update_dict['array'] = True + class FigureImage(martist.Artist, cm.ScalarMappable): def __init__(self, fig, cmap = None, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |