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

Skip to content

Commit d4e54a9

Browse files
committed
Reworked the handling of aspect ratio so that it follows redraw events - EF
svn path=/trunk/matplotlib/; revision=2168
1 parent fb10cd3 commit d4e54a9

File tree

1 file changed

+111
-59
lines changed

1 file changed

+111
-59
lines changed

lib/matplotlib/axes.py

Lines changed: 111 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from collections import RegularPolyCollection, PolyCollection, LineCollection, QuadMesh
1919
from colors import colorConverter, normalize, Colormap, LinearSegmentedColormap, looks_like_color
2020
import cm
21-
#from cm import ColormapJet, Grayscale, ScalarMappable
2221
from cm import ScalarMappable
2322
from contour import ContourSet
2423
import _image
@@ -370,8 +369,9 @@ def __init__(self, fig, rect,
370369
self._cachedRenderer = None
371370
self.set_navigate(True)
372371

373-
# aspect ration atribute, and original position
374-
self._aspect = 'normal'
372+
# aspect ratio atribute, and original position
373+
self.set_aspect('auto')
374+
self.set_aspect_adjusts('position')
375375
self._originalPosition = self.get_position()
376376

377377
if len(kwargs): setp(self, **kwargs)
@@ -816,8 +816,6 @@ def autoscale_view(self):
816816
locator = self.yaxis.get_major_locator()
817817
self.set_ylim(locator.autoscale())
818818

819-
if self._aspect == 'equal': self.set_aspect('equal')
820-
821819
def quiver(self, U, V, *args, **kwargs ):
822820
"""
823821
QUIVER( X, Y, U, V )
@@ -1406,6 +1404,7 @@ def draw(self, renderer=None, inframe=False):
14061404
if not self.get_visible(): return
14071405
renderer.open_group('axes')
14081406

1407+
self.apply_aspect()
14091408
try: self.transData.freeze() # eval the lazy objects
14101409
except ValueError:
14111410
print >> sys.stderr, 'data freeze value error', self.get_position(), self.dataLim.get_bounds(), self.viewLim.get_bounds()
@@ -2442,7 +2441,7 @@ def pcolormesh(self, *args, **kwargs):
24422441
showedges = 1
24432442
else:
24442443
showedges = 0
2445-
2444+
24462445
collection = QuadMesh(Ny - 1, Nx - 1, coords, showedges)
24472446
collection.set_alpha(alpha)
24482447
collection.set_array(C)
@@ -2969,7 +2968,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
29692968
faceted=True,
29702969
**kwargs):
29712970
"""
2972-
SCATTER(x, y, s=20, c='b', marker='o', cmap=None, norm=None,
2971+
SCATTER(x, y, s=20, c='b', marker='o', cmap=None, norm=None,
29732972
vmin=None, vmax=None, alpha=1.0, linewidths=None,
29742973
faceted=True, **kwargs)
29752974
Supported function signatures:
@@ -3344,7 +3343,7 @@ def set_xscale(self, value, basex = 10, subsx=None):
33443343
* subsx: the location of the minor ticks; None defaults to autosubs,
33453344
which depend on the number of decades in the plot
33463345
3347-
ACCEPTS: str
3346+
ACCEPTS: ['log' | 'linear' ]
33483347
"""
33493348

33503349
#if subsx is None: subsx = range(2, basex)
@@ -3466,7 +3465,7 @@ def set_yscale(self, value, basey=10, subsy=None):
34663465
* subsy: the location of the minor ticks; None are the default
34673466
is to autosub
34683467
3469-
ACCEPTS: str
3468+
ACCEPTS: ['log' | 'linear']
34703469
"""
34713470

34723471
#if subsy is None: subsy = range(2, basey)
@@ -3942,71 +3941,124 @@ def dist(a):
39423941
elif among is None:
39433942
pass
39443943
else:
3945-
raise ValueError('among mut be callable or iterable')
3944+
raise ValueError('among must be callable or iterable')
39463945
if not len(artists): return None
39473946
ds = [ (dist(a),a) for a in artists]
39483947
ds.sort()
39493948
return ds[0][1]
39503949

3951-
def set_aspect(self,aspect='normal',fixLimits=False):
3950+
def set_aspect(self, aspect='auto', fixLimits=None,
3951+
aspect_adjusts='position'):
3952+
"""
3953+
aspect:
3954+
'auto' - automatic; fill position rectangle with data
3955+
'normal' - same as 'auto'; deprecated
3956+
'equal' - same scaling from data to plot units for x and y
3957+
A - a circle will be stretched such that the height
3958+
is A times the width. aspect=1 is the same as
3959+
aspect='equal'.
3960+
3961+
aspect_adjusts:
3962+
'position' - change width or height of bounding rectangle;
3963+
keep it centered.
3964+
'box_size' - as above, but anchored to lower left
3965+
'datalim' - change xlim or ylim
3966+
3967+
fixLimits: deprecated; False is aspect_adjusts='datalim';
3968+
True is aspect_adjusts='position'
3969+
3970+
ACCEPTS: ['auto' | 'equal' | aspect_ratio]
39523971
"""
3953-
Set aspect to 'normal' or 'equal'
3954-
'normal' means matplotlib determines aspect ratio
3955-
'equal' means scale on x and y axes will be set equal such that circle looks like circle
3956-
In future we may want to add a number as input to have a certain aspect ratio,
3957-
such as vertical scale exagerrated by 2.
3972+
if aspect in ('normal', 'auto'):
3973+
self._aspect = 'auto'
3974+
elif aspect == 'equal':
3975+
self._aspect = 'equal'
3976+
else:
3977+
self._aspect = float(aspect) # raise ValueError if necessary
39583978

3959-
fixLimits: False means data limits will be changed, but height and widths of axes preserved.
3960-
True means height or width will be changed, but data limits preserved
3979+
if fixLimits is not None:
3980+
if not fixLimits: aspect_adjusts = 'datalim'
3981+
if aspect_adjusts in ('position', 'box_size', 'datalim'):
3982+
self._aspect_adjusts = aspect_adjusts
3983+
else:
3984+
raise ValueError(
3985+
'aspect_adjusts must be "position", "box_size", or "datalim"')
39613986

3962-
ACCEPTS: str, boolean
3987+
def set_aspect_adjusts(self, aspect_adjusts = 'position'):
39633988
"""
3989+
Must be called after set_aspect.
3990+
3991+
ACCEPTS: ['position' | 'box_size' | 'datalim']
3992+
"""
3993+
if aspect_adjusts in ('position', 'box_size', 'datalim'):
3994+
self._aspect_adjusts = aspect_adjusts
3995+
else:
3996+
raise ValueError(
3997+
'argument must be "position", "box_size", or "datalim"')
3998+
39643999

3965-
self._aspect = aspect
3966-
if self._aspect == 'normal':
4000+
def apply_aspect(self):
4001+
'''
4002+
Use self._aspect and self._aspect_adjusts to modify the
4003+
axes box or the view limits.
4004+
'''
4005+
4006+
if self._aspect == 'auto':
39674007
self.set_position( self._originalPosition )
3968-
elif self._aspect == 'equal' or self._aspect == 'scaled':
3969-
figW,figH = self.get_figure().get_size_inches()
3970-
xmin,xmax = self.get_xlim()
3971-
xsize = math.fabs(xmax-xmin)
3972-
ymin,ymax = self.get_ylim()
3973-
ysize = math.fabs(ymax-ymin)
3974-
if fixLimits: # Change size of axes
3975-
l,b,w,h = self._originalPosition # Always start from original position
3976-
axW = w * figW; axH = h * figH
3977-
if xsize / axW > ysize / axH: # y axis too long
3978-
axH = axW * ysize / xsize
3979-
if self._aspect == 'equal':
3980-
axc = b + 0.5 * h
3981-
h = axH / figH; b = axc - 0.5 * h
3982-
elif self._aspect == 'scaled':
3983-
h = axH / figH
3984-
else: # x axis too long
3985-
axW = axH * xsize / ysize
3986-
if self._aspect == 'equal':
3987-
axc = l + 0.5 * w
3988-
w = axW / figW; l = axc - 0.5 * w
3989-
elif self._aspect == 'scaled':
3990-
w = axW / figW
3991-
self.set_position( (l,b,w,h) )
3992-
else: # Change limits on axes
3993-
l,b,w,h = self.get_position() # Keep size of subplot
3994-
axW = w * figW; axH = h * figH
3995-
if xsize / axW > ysize / axH: # y limits too narrow
3996-
dely = axH * xsize / axW
3997-
yc = 0.5 * ( ymin + ymax )
3998-
ymin = yc - 0.5*dely; ymax = yc + 0.5*dely
3999-
self.set_ylim( ymin, ymax )
4000-
else:
4001-
delx = axW * ysize / axH
4002-
xc = 0.5 * ( xmin + xmax )
4003-
xmin = xc - 0.5*delx; xmax = xc + 0.5*delx
4004-
self.set_xlim( xmin, xmax )
4008+
return
4009+
4010+
if self._aspect == 'equal':
4011+
A = 1
4012+
else:
4013+
A = self._aspect
4014+
4015+
figW,figH = self.get_figure().get_size_inches()
4016+
fig_aspect = figH/figW
4017+
xmin,xmax = self.get_xlim()
4018+
xsize = math.fabs(xmax-xmin)
4019+
ymin,ymax = self.get_ylim()
4020+
ysize = math.fabs(ymax-ymin)
4021+
l,b,w,h = self._originalPosition
4022+
if self._aspect_adjusts in ('position', 'box_size'):
4023+
data_ratio = ysize/xsize
4024+
box_aspect = A * data_ratio
4025+
H = w * box_aspect/fig_aspect
4026+
if H <= h:
4027+
W = w
4028+
else:
4029+
W = h * fig_aspect/box_aspect
4030+
H = h
4031+
if self._aspect_adjusts == 'position':
4032+
L = l + 0.5 * (w-W)
4033+
B = b + 0.5 * (h-H)
4034+
else:
4035+
L,B = l,b
4036+
self.set_position((L,B,W,H))
4037+
return
4038+
4039+
self.autoscale_view()
4040+
xmin,xmax = self.get_xlim()
4041+
xsize = math.fabs(xmax-xmin)
4042+
ymin,ymax = self.get_ylim()
4043+
ysize = math.fabs(ymax-ymin)
4044+
box_aspect = fig_aspect * (h/w)
4045+
data_ratio = box_aspect / A
4046+
Ysize = data_ratio * xsize
4047+
if Ysize > ysize:
4048+
dy = 0.5 * (Ysize - ysize)
4049+
self.set_ylim((ymin-dy, ymax+dy))
4050+
return
4051+
4052+
Xsize = ysize / data_ratio
4053+
if Xsize > xsize:
4054+
dx = 0.5 * (Xsize - xsize)
4055+
self.set_xlim((xmin-dx, xmax+dx))
40054056

40064057
def get_aspect(self):
4007-
"""Get the aspect 'normal' or 'equal' """
40084058
return self._aspect
40094059

4060+
def get_aspect_adjusts(self):
4061+
return self._aspect_adjusts
40104062

40114063
class SubplotBase:
40124064
"""

0 commit comments

Comments
 (0)