|
18 | 18 | from collections import RegularPolyCollection, PolyCollection, LineCollection, QuadMesh |
19 | 19 | from colors import colorConverter, normalize, Colormap, LinearSegmentedColormap, looks_like_color |
20 | 20 | import cm |
21 | | -#from cm import ColormapJet, Grayscale, ScalarMappable |
22 | 21 | from cm import ScalarMappable |
23 | 22 | from contour import ContourSet |
24 | 23 | import _image |
@@ -370,8 +369,9 @@ def __init__(self, fig, rect, |
370 | 369 | self._cachedRenderer = None |
371 | 370 | self.set_navigate(True) |
372 | 371 |
|
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') |
375 | 375 | self._originalPosition = self.get_position() |
376 | 376 |
|
377 | 377 | if len(kwargs): setp(self, **kwargs) |
@@ -816,8 +816,6 @@ def autoscale_view(self): |
816 | 816 | locator = self.yaxis.get_major_locator() |
817 | 817 | self.set_ylim(locator.autoscale()) |
818 | 818 |
|
819 | | - if self._aspect == 'equal': self.set_aspect('equal') |
820 | | - |
821 | 819 | def quiver(self, U, V, *args, **kwargs ): |
822 | 820 | """ |
823 | 821 | QUIVER( X, Y, U, V ) |
@@ -1406,6 +1404,7 @@ def draw(self, renderer=None, inframe=False): |
1406 | 1404 | if not self.get_visible(): return |
1407 | 1405 | renderer.open_group('axes') |
1408 | 1406 |
|
| 1407 | + self.apply_aspect() |
1409 | 1408 | try: self.transData.freeze() # eval the lazy objects |
1410 | 1409 | except ValueError: |
1411 | 1410 | 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): |
2442 | 2441 | showedges = 1 |
2443 | 2442 | else: |
2444 | 2443 | showedges = 0 |
2445 | | - |
| 2444 | + |
2446 | 2445 | collection = QuadMesh(Ny - 1, Nx - 1, coords, showedges) |
2447 | 2446 | collection.set_alpha(alpha) |
2448 | 2447 | collection.set_array(C) |
@@ -2969,7 +2968,7 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, |
2969 | 2968 | faceted=True, |
2970 | 2969 | **kwargs): |
2971 | 2970 | """ |
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, |
2973 | 2972 | vmin=None, vmax=None, alpha=1.0, linewidths=None, |
2974 | 2973 | faceted=True, **kwargs) |
2975 | 2974 | Supported function signatures: |
@@ -3344,7 +3343,7 @@ def set_xscale(self, value, basex = 10, subsx=None): |
3344 | 3343 | * subsx: the location of the minor ticks; None defaults to autosubs, |
3345 | 3344 | which depend on the number of decades in the plot |
3346 | 3345 |
|
3347 | | - ACCEPTS: str |
| 3346 | + ACCEPTS: ['log' | 'linear' ] |
3348 | 3347 | """ |
3349 | 3348 |
|
3350 | 3349 | #if subsx is None: subsx = range(2, basex) |
@@ -3466,7 +3465,7 @@ def set_yscale(self, value, basey=10, subsy=None): |
3466 | 3465 | * subsy: the location of the minor ticks; None are the default |
3467 | 3466 | is to autosub |
3468 | 3467 |
|
3469 | | - ACCEPTS: str |
| 3468 | + ACCEPTS: ['log' | 'linear'] |
3470 | 3469 | """ |
3471 | 3470 |
|
3472 | 3471 | #if subsy is None: subsy = range(2, basey) |
@@ -3942,71 +3941,124 @@ def dist(a): |
3942 | 3941 | elif among is None: |
3943 | 3942 | pass |
3944 | 3943 | else: |
3945 | | - raise ValueError('among mut be callable or iterable') |
| 3944 | + raise ValueError('among must be callable or iterable') |
3946 | 3945 | if not len(artists): return None |
3947 | 3946 | ds = [ (dist(a),a) for a in artists] |
3948 | 3947 | ds.sort() |
3949 | 3948 | return ds[0][1] |
3950 | 3949 |
|
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] |
3952 | 3971 | """ |
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 |
3958 | 3978 |
|
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"') |
3961 | 3986 |
|
3962 | | - ACCEPTS: str, boolean |
| 3987 | + def set_aspect_adjusts(self, aspect_adjusts = 'position'): |
3963 | 3988 | """ |
| 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 | + |
3964 | 3999 |
|
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': |
3967 | 4007 | 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)) |
4005 | 4056 |
|
4006 | 4057 | def get_aspect(self): |
4007 | | - """Get the aspect 'normal' or 'equal' """ |
4008 | 4058 | return self._aspect |
4009 | 4059 |
|
| 4060 | + def get_aspect_adjusts(self): |
| 4061 | + return self._aspect_adjusts |
4010 | 4062 |
|
4011 | 4063 | class SubplotBase: |
4012 | 4064 | """ |
|
0 commit comments