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

Skip to content

Commit 2313791

Browse files
committed
Add scale_units kwarg, additional 'xy' units option.
svn path=/trunk/matplotlib/; revision=7836
1 parent 71f62f1 commit 2313791

1 file changed

Lines changed: 59 additions & 16 deletions

File tree

lib/matplotlib/quiver.py

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
7373
* 'dots' or 'inches': pixels or inches, based on the figure dpi
7474
75-
* 'x' or 'y': *X* or *Y* data units
75+
* 'x', 'y', or 'xy': *X*, *Y*, or sqrt(X^2+Y^2) data units
7676
7777
The arrows scale differently depending on the units. For
7878
'x' or 'y', the arrows get larger as one zooms in; for other
@@ -81,6 +81,7 @@
8181
height of the axes, respectively, when the the window is resized;
8282
for 'dots' or 'inches', resizing does not change the arrows.
8383
84+
8485
*angles*: ['uv' | 'xy' | array]
8586
With the default 'uv', the arrow aspect ratio is 1, so that
8687
if *U*==*V* the angle of the arrow on the plot is 45 degrees
@@ -90,10 +91,20 @@
9091
of values in degrees, CCW from the *x*-axis.
9192
9293
*scale*: [ None | float ]
93-
data units per arrow unit, e.g. m/s per plot width; a smaller
94+
data units per arrow length unit, e.g. m/s per plot width; a smaller
9495
scale parameter makes the arrow longer. If *None*, a simple
9596
autoscaling algorithm is used, based on the average vector length
96-
and the number of vectors.
97+
and the number of vectors. The arrow length unit is given by
98+
the *scale_units* parameter
99+
100+
*scale_units*: None, or any of the *units* options. For example,
101+
if *scale_units* is 'inches', *scale* is 2.0, and (u,v) = (1,0),
102+
then the vector will be 0.5 inches long. If *scale_units* is
103+
'width', then the vector will be half the width of the axes.
104+
If *scale_units* is 'x' then the vector will be 0.5 x-axis
105+
units. To plot vectors in the x-y plane, with u and v having
106+
the same units as x and y, use
107+
"angles='xy', scale_units='xy', scale=1".
97108
98109
*width*:
99110
shaft width in arrow units; default depends on choice of units,
@@ -390,6 +401,7 @@ def __init__(self, ax, *args, **kw):
390401
self.minshaft = kw.pop('minshaft', 1)
391402
self.minlength = kw.pop('minlength', 1)
392403
self.units = kw.pop('units', 'width')
404+
self.scale_units = kw.pop('scale_units', None)
393405
self.angles = kw.pop('angles', 'uv')
394406
self.width = kw.pop('width', None)
395407
self.color = kw.pop('color', 'k')
@@ -418,7 +430,8 @@ def on_dpi_change(fig):
418430

419431

420432
def _init(self):
421-
"""initialization delayed until first draw;
433+
"""
434+
Initialization delayed until first draw;
422435
allow time for axes setup.
423436
"""
424437
# It seems that there are not enough event notifications
@@ -429,14 +442,15 @@ def _init(self):
429442
sx, sy = trans.inverted().transform_point(
430443
(ax.bbox.width, ax.bbox.height))
431444
self.span = sx
432-
sn = max(8, min(25, math.sqrt(self.N)))
433445
if self.width is None:
446+
sn = max(8, min(25, math.sqrt(self.N)))
434447
self.width = 0.06 * self.span / sn
435448

436449
@allow_rasterization
437450
def draw(self, renderer):
438451
self._init()
439-
if self._new_UV or self.angles == 'xy':
452+
if (self._new_UV or self.angles == 'xy'
453+
or self.scale_units in ['x','y', 'xy']):
440454
verts = self._make_verts(self.U, self.V)
441455
self.set_verts(verts, closed=False)
442456
self._new_UV = False
@@ -460,27 +474,46 @@ def set_UVC(self, U, V, C=None):
460474
self.set_array(C)
461475
self._new_UV = True
462476

463-
def _set_transform(self):
477+
def _dots_per_unit(self, units):
478+
"""
479+
Return a scale factor for converting from units to pixels
480+
"""
464481
ax = self.ax
465-
if self.units in ('x', 'y'):
466-
if self.units == 'x':
482+
if units in ('x', 'y', 'xy'):
483+
if units == 'x':
467484
dx0 = ax.viewLim.width
468485
dx1 = ax.bbox.width
469-
else:
486+
elif units == 'y':
470487
dx0 = ax.viewLim.height
471488
dx1 = ax.bbox.height
489+
else: # 'xy' is assumed
490+
dxx0 = ax.viewLim.width
491+
dxx1 = ax.bbox.width
492+
dyy0 = ax.viewLim.height
493+
dyy1 = ax.bbox.height
494+
dx1 = np.sqrt(dxx1*dxx1+dyy1*dyy1)
495+
dx0 = np.sqrt(dxx0*dxx0+dyy0*dyy0)
472496
dx = dx1/dx0
473497
else:
474-
if self.units == 'width':
498+
if units == 'width':
475499
dx = ax.bbox.width
476-
elif self.units == 'height':
500+
elif units == 'height':
477501
dx = ax.bbox.height
478-
elif self.units == 'dots':
502+
elif units == 'dots':
479503
dx = 1.0
480-
elif self.units == 'inches':
504+
elif units == 'inches':
481505
dx = ax.figure.dpi
482506
else:
483507
raise ValueError('unrecognized units')
508+
return dx
509+
510+
def _set_transform(self):
511+
"""
512+
Sets the PolygonCollection transform to go
513+
from arrow width units to pixels.
514+
"""
515+
dx = self._dots_per_unit(self.units)
516+
self._trans_scale = dx # pixels per arrow width unit
484517
trans = transforms.Affine2D().scale(dx)
485518
self.set_transform(trans)
486519
return trans
@@ -503,8 +536,18 @@ def _make_verts(self, U, V):
503536
else:
504537
amean = a.mean()
505538
scale = 1.8 * amean * sn / self.span # crude auto-scaling
506-
self.scale = scale
507-
length = a/(self.scale*self.width)
539+
# scale is typical arrow length as a multiple
540+
# of the arrow width
541+
if self.scale_units is None:
542+
if self.scale is None:
543+
self.scale = scale
544+
widthu_per_lenu = 1.0
545+
else:
546+
dx = self._dots_per_unit(self.scale_units)
547+
widthu_per_lenu = dx/self._trans_scale
548+
if self.scale is None:
549+
self.scale = scale * widthu_per_lenu
550+
length = a * (widthu_per_lenu / (self.scale * self.width))
508551
X, Y = self._h_arrows(length)
509552
if self.angles == 'xy':
510553
theta = self._angles(U, V)

0 commit comments

Comments
 (0)