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

Skip to content

Commit c6bf0f7

Browse files
committed
Speed up quiver for large numbers of arrows; key tip given by Ray Speth.
svn path=/trunk/matplotlib/; revision=7303
1 parent 808b46b commit c6bf0f7

File tree

3 files changed

+51
-27
lines changed

3 files changed

+51
-27
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
2009-07-28 Quiver speed improved, thanks to tip by Ray Speth. -EF
2+
13
2009-07-27 Simplify argument handling code for plot method. -EF
24

35
2009-07-25 Allow "plot(1, 2, 'r*')" to work. - EF

lib/matplotlib/collections.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,9 @@ def __init__(self, verts, sizes = None, closed = True, **kwargs):
670670

671671
def set_verts(self, verts, closed=True):
672672
'''This allows one to delay initialization of the vertices.'''
673+
if np.ma.isMaskedArray(verts):
674+
verts = verts.astype(np.float_).filled(np.nan)
675+
# This is much faster than having Path do it one at a time.
673676
if closed:
674677
self._paths = []
675678
for xy in verts:

lib/matplotlib/quiver.py

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,12 @@ def _init(self):
253253
self._set_transform()
254254
_pivot = self.Q.pivot
255255
self.Q.pivot = self.pivot[self.labelpos]
256+
# Hack: save and restore the Umask
257+
_mask = self.Q.Umask
258+
self.Q.Umask = ma.nomask
256259
self.verts = self.Q._make_verts(np.array([self.U]),
257260
np.zeros((1,)))
261+
self.Q.Umask = _mask
258262
self.Q.pivot = _pivot
259263
kw = self.Q.polykw
260264
kw.update(self.kw)
@@ -388,9 +392,9 @@ def _parse_args(self, *args):
388392
X, Y, U, V, C = [None]*5
389393
args = list(args)
390394
if len(args) == 3 or len(args) == 5:
391-
C = ma.masked_invalid(args.pop(-1), copy=False)
392-
V = ma.masked_invalid(args.pop(-1), copy=False)
393-
U = ma.masked_invalid(args.pop(-1), copy=False)
395+
C = np.asanyarray(args.pop(-1))
396+
V = np.asanyarray(args.pop(-1))
397+
U = np.asanyarray(args.pop(-1))
394398
if U.ndim == 1:
395399
nr, nc = 1, U.shape[0]
396400
else:
@@ -430,10 +434,21 @@ def draw(self, renderer):
430434
collections.PolyCollection.draw(self, renderer)
431435

432436
def set_UVC(self, U, V, C=None):
433-
self.U = U.ravel()
434-
self.V = V.ravel()
437+
U = ma.masked_invalid(U, copy=False).ravel()
438+
V = ma.masked_invalid(V, copy=False).ravel()
439+
mask = ma.mask_or(U.mask, V.mask, copy=False, shrink=True)
440+
if C is not None:
441+
C = ma.masked_invalid(C, copy=False).ravel()
442+
mask = ma.mask_or(mask, C.mask, copy=False, shrink=True)
443+
if mask is ma.nomask:
444+
C = C.filled()
445+
else:
446+
C = ma.array(C, mask=mask, copy=False)
447+
self.U = U.filled(1)
448+
self.V = V.filled(1)
449+
self.Umask = mask
435450
if C is not None:
436-
self.set_array(C.ravel())
451+
self.set_array(C)
437452
self._new_UV = True
438453

439454
def _set_transform(self):
@@ -463,32 +478,33 @@ def _set_transform(self):
463478

464479
def _angles(self, U, V, eps=0.001):
465480
xy = self.ax.transData.transform(self.XY)
466-
uv = ma.hstack((U[:,np.newaxis], V[:,np.newaxis])).filled(0)
481+
uv = np.hstack((U[:,np.newaxis], V[:,np.newaxis]))
467482
xyp = self.ax.transData.transform(self.XY + eps * uv)
468483
dxy = xyp - xy
469-
ang = ma.arctan2(dxy[:,1], dxy[:,0])
484+
ang = np.arctan2(dxy[:,1], dxy[:,0])
470485
return ang
471486

472487
def _make_verts(self, U, V):
473-
uv = ma.asarray(U+V*1j)
474-
a = ma.absolute(uv)
488+
uv = (U+V*1j)
489+
a = np.absolute(uv)
475490
if self.scale is None:
476491
sn = max(10, math.sqrt(self.N))
477-
scale = 1.8 * a.mean() * sn / self.span # crude auto-scaling
492+
scale = 1.8 * a[~self.Umask].mean() * sn / self.span # crude auto-scaling
478493
self.scale = scale
479494
length = a/(self.scale*self.width)
480495
X, Y = self._h_arrows(length)
481496
if self.angles == 'xy':
482-
theta = self._angles(U, V).filled(0)
497+
theta = self._angles(U, V)
483498
elif self.angles == 'uv':
484-
theta = np.angle(uv.filled(0))
499+
theta = np.angle(uv)
485500
else:
486501
theta = ma.masked_invalid(self.angles, copy=False).filled(0)
487502
theta *= (np.pi/180.0)
488503
theta.shape = (theta.shape[0], 1) # for broadcasting
489504
xy = (X+Y*1j) * np.exp(1j*theta)*self.width
490505
xy = xy[:,:,np.newaxis]
491-
XY = ma.concatenate((xy.real, xy.imag), axis=2)
506+
XY = np.concatenate((xy.real, xy.imag), axis=2)
507+
492508
return XY
493509

494510

@@ -502,8 +518,8 @@ def _h_arrows(self, length):
502518
length = length.reshape(N, 1)
503519
# This number is chosen based on when pixel values overflow in Agg
504520
# causing rendering errors
505-
length = np.minimum(length, 2 ** 16)
506-
521+
#length = np.minimum(length, 2 ** 16)
522+
np.clip(length, 0, 2**16, out=length)
507523
# x, y: normal horizontal arrow
508524
x = np.array([0, -self.headaxislength,
509525
-self.headlength, 0], np.float64)
@@ -514,21 +530,20 @@ def _h_arrows(self, length):
514530
x0 = np.array([0, minsh-self.headaxislength,
515531
minsh-self.headlength, minsh], np.float64)
516532
y0 = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64)
517-
ii = [0,1,2,3,2,1,0]
533+
ii = [0,1,2,3,2,1,0,0]
518534
X = x.take(ii, 1)
519535
Y = y.take(ii, 1)
520-
Y[:, 3:] *= -1
536+
Y[:, 3:-1] *= -1
521537
X0 = x0.take(ii)
522538
Y0 = y0.take(ii)
523-
Y0[3:] *= -1
539+
Y0[3:-1] *= -1
524540
shrink = length/minsh
525541
X0 = shrink * X0[np.newaxis,:]
526542
Y0 = shrink * Y0[np.newaxis,:]
527-
short = np.repeat(length < minsh, 7, axis=1)
528-
#print 'short', length < minsh
543+
short = np.repeat(length < minsh, 8, axis=1)
529544
# Now select X0, Y0 if short, otherwise X, Y
530-
X = ma.where(short, X0, X)
531-
Y = ma.where(short, Y0, Y)
545+
np.putmask(X, short, X0)
546+
np.putmask(Y, short, Y0)
532547
if self.pivot[:3] == 'mid':
533548
X -= 0.5 * X[:,3, np.newaxis]
534549
elif self.pivot[:3] == 'tip':
@@ -538,14 +553,18 @@ def _h_arrows(self, length):
538553
tooshort = length < self.minlength
539554
if tooshort.any():
540555
# Use a heptagonal dot:
541-
th = np.arange(0,7,1, np.float64) * (np.pi/3.0)
556+
th = np.arange(0,8,1, np.float64) * (np.pi/3.0)
542557
x1 = np.cos(th) * self.minlength * 0.5
543558
y1 = np.sin(th) * self.minlength * 0.5
544559
X1 = np.repeat(x1[np.newaxis, :], N, axis=0)
545560
Y1 = np.repeat(y1[np.newaxis, :], N, axis=0)
546-
tooshort = ma.repeat(tooshort, 7, 1)
547-
X = ma.where(tooshort, X1, X)
548-
Y = ma.where(tooshort, Y1, Y)
561+
tooshort = np.repeat(tooshort, 8, 1)
562+
np.putmask(X, tooshort, X1)
563+
np.putmask(Y, tooshort, Y1)
564+
if self.Umask is not ma.nomask:
565+
mask = np.repeat(self.Umask[:,np.newaxis], 8, 1)
566+
X = ma.array(X, mask=mask, copy=False)
567+
Y = ma.array(Y, mask=mask, copy=False)
549568
return X, Y
550569

551570
quiver_doc = _quiver_doc

0 commit comments

Comments
 (0)