Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e2df232

Browse files
committed
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. svn path=/trunk/matplotlib/; revision=4377
1 parent 0237456 commit e2df232

2 files changed

Lines changed: 276 additions & 7 deletions

File tree

lib/matplotlib/axes.py

Lines changed: 178 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3706,7 +3706,7 @@ def xywhere(xs, ys, mask):
37063706
xs = [thisx for thisx, b in zip(xs, mask) if b]
37073707
ys = [thisy for thisy, b in zip(ys, mask) if b]
37083708
return xs, ys
3709-
3709+
37103710

37113711
if capsize > 0:
37123712
plot_kw = {
@@ -3733,16 +3733,16 @@ def xywhere(xs, ys, mask):
37333733
# can't use numpy logical indexing since left and
37343734
# y are lists
37353735
leftlo, ylo = xywhere(left, y, xlolims)
3736-
3736+
37373737
caplines.extend( self.plot(leftlo, ylo, ls='None', marker=mlines.CARETLEFT, **plot_kw) )
37383738
xlolims = ~xlolims
3739-
leftlo, ylo = xywhere(left, y, xlolims)
3739+
leftlo, ylo = xywhere(left, y, xlolims)
37403740
caplines.extend( self.plot(leftlo, ylo, 'k|', **plot_kw) )
37413741
else:
37423742
caplines.extend( self.plot(left, y, 'k|', **plot_kw) )
37433743

37443744
if xuplims.any():
3745-
3745+
37463746
rightup, yup = xywhere(right, y, xuplims)
37473747
caplines.extend( self.plot(rightup, yup, ls='None', marker=mlines.CARETRIGHT, **plot_kw) )
37483748
xuplims = ~xuplims
@@ -3775,7 +3775,7 @@ def xywhere(xs, ys, mask):
37753775

37763776
if uplims.any():
37773777
xup, upperup = xywhere(x, upper, uplims)
3778-
3778+
37793779
caplines.extend( self.plot(xup, upperup, ls='None', marker=mlines.CARETUP, **plot_kw) )
37803780
uplims = ~uplims
37813781
xup, upperup = xywhere(x, upper, uplims)
@@ -4762,6 +4762,177 @@ def pcolormesh(self, *args, **kwargs):
47624762
return collection
47634763
pcolormesh.__doc__ = cbook.dedent(pcolormesh.__doc__) % martist.kwdocd
47644764

4765+
def pcolorfast(self, *args, **kwargs):
4766+
"""
4767+
Experimental; this is a version of pcolor that
4768+
does not draw lines, that provides the fastest
4769+
possible rendering with the Agg backend, and that
4770+
can handle any quadrilateral grid.
4771+
4772+
pcolor(*args, **kwargs): pseudocolor plot of a 2-D array
4773+
4774+
Function signatures
4775+
4776+
pcolor(C, **kwargs)
4777+
pcolor(xr, yr, C, **kwargs)
4778+
pcolor(x, y, C, **kwargs)
4779+
pcolor(X, Y, C, **kwargs)
4780+
4781+
C is the 2D array of color values corresponding to quadrilateral
4782+
cells. Let (nr, nc) be its shape. C may be a masked array.
4783+
4784+
pcolor(C, **kwargs) is equivalent to
4785+
pcolor([0,nc], [0,nr], C, **kwargs)
4786+
4787+
xr, yr specify the ranges of x and y corresponding to the rectangular
4788+
region bounding C. If xr = [x0, x1] and yr = [y0,y1] then
4789+
x goes from x0 to x1 as the second index of C goes from 0 to nc,
4790+
etc. (x0, y0) is the outermost corner of cell (0,0), and (x1, y1)
4791+
is the outermost corner of cell (nr-1, nc-1). All cells are
4792+
rectangles of the same size. This is the fastest version.
4793+
4794+
x, y are 1D arrays of length nc+1 and nr+1, respectively, giving
4795+
the x and y boundaries of the cells. Hence the cells are
4796+
rectangular but the grid may be nonuniform. The speed is
4797+
intermediate. (The grid is checked, and if found to be
4798+
uniform the fast version is used.)
4799+
4800+
X and Y are 2D arrays with shape (nr+1, nc+1) that specify
4801+
the (x,y) coordinates of the corners of the colored
4802+
quadrilaterals; the quadrilateral for C[i,j] has corners at
4803+
(X[i,j],Y[i,j]), (X[i,j+1],Y[i,j+1]), (X[i+1,j],Y[i+1,j]),
4804+
(X[i+1,j+1],Y[i+1,j+1]). The cells need not be rectangular.
4805+
This is the most general, but the slowest to render. It may
4806+
produce faster and more compact output using ps, pdf, and
4807+
svg backends, however.
4808+
4809+
Note that the the column index corresponds to the x-coordinate,
4810+
and the row index corresponds to y; for details, see
4811+
the "Grid Orientation" section below.
4812+
4813+
Optional keyword args are shown with their defaults below (you must
4814+
use kwargs for these):
4815+
4816+
* cmap = cm.jet : a cm Colormap instance from cm
4817+
4818+
* norm = Normalize() : mcolors.Normalize instance
4819+
is used to scale luminance data to 0,1.
4820+
4821+
* vmin=None and vmax=None : vmin and vmax are used in conjunction
4822+
with norm to normalize luminance data. If either are None, the
4823+
min and max of the color array C is used. If you pass a norm
4824+
instance, vmin and vmax will be None
4825+
4826+
* alpha=1.0 : the alpha blending value
4827+
4828+
Return value is an image if a regular or rectangular grid
4829+
is specified, and a QuadMesh collection in the general
4830+
quadrilateral case.
4831+
4832+
"""
4833+
4834+
if not self._hold: self.cla()
4835+
4836+
alpha = kwargs.pop('alpha', 1.0)
4837+
norm = kwargs.pop('norm', None)
4838+
cmap = kwargs.pop('cmap', None)
4839+
vmin = kwargs.pop('vmin', None)
4840+
vmax = kwargs.pop('vmax', None)
4841+
if norm is not None: assert(isinstance(norm, mcolors.Normalize))
4842+
if cmap is not None: assert(isinstance(cmap, mcolors.Colormap))
4843+
4844+
C = args[-1]
4845+
nr, nc = C.shape
4846+
if len(args) == 1:
4847+
style = "image"
4848+
x = [0, nc+1]
4849+
y = [0, nr+1]
4850+
elif len(args) == 3:
4851+
x, y = args[:2]
4852+
x = npy.asarray(x)
4853+
y = npy.asarray(y)
4854+
if x.ndim == 1 and y.ndim == 1:
4855+
if x.size == 2 and y.size == 2:
4856+
style = "image"
4857+
else:
4858+
dx = npy.diff(x)
4859+
dy = npy.diff(y)
4860+
if (npy.ptp(dx) < 0.01*npy.abs(dx.mean()) and
4861+
npy.ptp(dy) < 0.01*npy.abs(dy.mean())):
4862+
style = "image"
4863+
style = "pcolorimage"
4864+
elif x.ndim == 2 and y.ndim == 2:
4865+
style = "quadmesh"
4866+
else:
4867+
raise TypeError("arguments do not match valid signatures")
4868+
else:
4869+
raise TypeError("need 1 argument or 3 arguments")
4870+
4871+
if style == "quadmesh":
4872+
4873+
# convert to one dimensional arrays
4874+
# This should also be moved to the QuadMesh class
4875+
C = ma.ravel(C) # data point in each cell is value at lower left corner
4876+
X = x.ravel()
4877+
Y = y.ravel()
4878+
Nx = nc+1
4879+
Ny = nr+1
4880+
4881+
# The following needs to be cleaned up; the renderer
4882+
# requires separate contiguous arrays for X and Y,
4883+
# but the QuadMesh class requires the 2D array.
4884+
coords = npy.empty(((Nx * Ny), 2), npy.float64)
4885+
coords[:, 0] = X
4886+
coords[:, 1] = Y
4887+
4888+
# The QuadMesh class can also be changed to
4889+
# handle relevant superclass kwargs; the initializer
4890+
# should do much more than it does now.
4891+
collection = mcoll.QuadMesh(nc, nr, coords, 0)
4892+
collection.set_alpha(alpha)
4893+
collection.set_array(C)
4894+
collection.set_cmap(cmap)
4895+
collection.set_norm(norm)
4896+
self.add_collection(collection)
4897+
xl, xr, yb, yt = X.min(), X.max(), Y.min(), Y.max()
4898+
ret = collection
4899+
4900+
else:
4901+
# One of the image styles:
4902+
xl, xr, yb, yt = x[0], x[-1], y[0], y[-1]
4903+
if style == "image":
4904+
4905+
im = mimage.AxesImage(self, cmap, norm,
4906+
interpolation='nearest',
4907+
origin='lower',
4908+
extent=(xl, xr, yb, yt),
4909+
**kwargs)
4910+
im.set_data(C)
4911+
im.set_alpha(alpha)
4912+
self.images.append(im)
4913+
ret = im
4914+
4915+
if style == "pcolorimage":
4916+
im = mimage.PcolorImage(self, x, y, C,
4917+
cmap=cmap,
4918+
norm=norm,
4919+
alpha=alpha,
4920+
**kwargs)
4921+
self.images.append(im)
4922+
ret = im
4923+
4924+
self._set_artist_props(ret)
4925+
if vmin is not None or vmax is not None:
4926+
ret.set_clim(vmin, vmax)
4927+
else:
4928+
ret.autoscale_None()
4929+
self.update_datalim(npy.array([[xl, yb], [xr, yt]]))
4930+
self.autoscale_view(tight=True)
4931+
return ret
4932+
4933+
4934+
4935+
47654936
def contour(self, *args, **kwargs):
47664937
kwargs['filled'] = False
47674938
return mcontour.ContourSet(self, *args, **kwargs)
@@ -4822,13 +4993,13 @@ def twiny(self):
48224993
ticks on bottom and the returned axes will have ticks on the
48234994
top
48244995
"""
4825-
4996+
48264997
ax2 = self.figure.add_axes(self.get_position(), sharey=self, frameon=False)
48274998
ax2.xaxis.tick_top()
48284999
ax2.xaxis.set_label_position('top')
48295000
self.xaxis.tick_bottom()
48305001
return ax2
4831-
5002+
48325003

48335004
#### Data analysis
48345005

lib/matplotlib/image.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,106 @@ def set_cmap(self, cmap):
410410
raise RuntimeError('Cannot change colors after loading data')
411411
cm.ScalarMappable.set_cmap(self, norm)
412412

413+
class PcolorImage(martist.Artist, cm.ScalarMappable):
414+
def __init__(self, ax,
415+
x=None,
416+
y=None,
417+
A=None,
418+
cmap = None,
419+
norm = None,
420+
**kwargs
421+
):
422+
"""
423+
cmap defaults to its rc setting
424+
425+
cmap is a colors.Colormap instance
426+
norm is a colors.Normalize instance to map luminance to 0-1
427+
428+
Additional kwargs are matplotlib.artist properties
413429
430+
"""
431+
martist.Artist.__init__(self)
432+
cm.ScalarMappable.__init__(self, norm, cmap)
433+
self.axes = ax
434+
self._rgbacache = None
435+
self.update(kwargs)
436+
self.set_data(x, y, A)
414437

438+
def make_image(self, magnification=1.0):
439+
if self._A is None:
440+
raise RuntimeError('You must first set the image array')
441+
fc = self.axes.get_frame().get_facecolor()
442+
bg = mcolors.colorConverter.to_rgba(fc, 0)
443+
bg = (npy.array(bg)*255).astype(npy.uint8)
444+
x0, y0, v_width, v_height = self.axes.viewLim.get_bounds()
445+
l, b, width, height = self.axes.bbox.get_bounds()
446+
width *= magnification
447+
height *= magnification
448+
if self.check_update('array'):
449+
A = self.to_rgba(self._A, alpha=self._alpha, bytes=True)
450+
self._rgbacache = A
451+
if self._A.ndim == 2:
452+
self.is_grayscale = self.cmap.is_gray()
453+
else:
454+
A = self._rgbacache
455+
im = _image.pcolor2(self._Ax, self._Ay, A,
456+
height, width,
457+
(x0, x0+v_width, y0, y0+v_height),
458+
bg)
459+
im.is_grayscale = self.is_grayscale
460+
return im
461+
462+
def draw(self, renderer, *args, **kwargs):
463+
if not self.get_visible(): return
464+
im = self.make_image(renderer.get_image_magnification())
465+
l, b, widthDisplay, heightDisplay = self.axes.bbox.get_bounds()
466+
renderer.draw_image(l, b, im, self.axes.bbox)
467+
468+
469+
def set_data(self, x, y, A):
470+
A = ma.asarray(A)
471+
if x is None:
472+
x = npy.arange(0, A.shape[1]+1, dtype=npy.float64)
473+
else:
474+
x = npy.asarray(x, npy.float64).ravel()
475+
if y is None:
476+
y = npy.arange(0, A.shape[0]+1, dtype=npy.float64)
477+
else:
478+
y = npy.asarray(y, npy.float64).ravel()
479+
480+
if A.shape[:2] != (y.size-1, x.size-1):
481+
print A.shape
482+
print y.size
483+
print x.size
484+
raise ValueError("Axes don't match array shape")
485+
if A.ndim not in [2, 3]:
486+
raise ValueError("A must be 2D or 3D")
487+
if A.ndim == 3 and A.shape[2] == 1:
488+
A.shape = A.shape[:2]
489+
self.is_grayscale = False
490+
if A.ndim == 3:
491+
if A.shape[2] in [3, 4]:
492+
if (A[:,:,0] == A[:,:,1]).all() and (A[:,:,0] == A[:,:,2]).all():
493+
self.is_grayscale = True
494+
else:
495+
raise ValueError("3D arrays must have RGB or RGBA as last dim")
496+
self._A = A
497+
self._Ax = x
498+
self._Ay = y
499+
self.update_dict['array'] = True
500+
501+
def set_array(self, *args):
502+
raise NotImplementedError('Method not supported')
503+
504+
def set_alpha(self, alpha):
505+
"""
506+
Set the alpha value used for blending - not supported on
507+
all backends
508+
509+
ACCEPTS: float
510+
"""
511+
martist.Artist.set_alpha(self, alpha)
512+
self.update_dict['array'] = True
415513

416514
class FigureImage(martist.Artist, cm.ScalarMappable):
417515
def __init__(self, fig,

0 commit comments

Comments
 (0)