diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 4462cd620ce6..674f56639e5a 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -438,7 +438,7 @@ def __init__(self, fig, rect, ================ ========================================= Keyword Description ================ ========================================= - *adjustable* [ 'box' | 'datalim' | 'box-forced'] + *adjustable* [ 'box' | 'datalim' ] *alpha* float: the alpha transparency (can be None) *anchor* [ 'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W' ] @@ -488,25 +488,15 @@ def __init__(self, fig, rect, self._originalPosition = self._position.frozen() # self.set_axes(self) self.axes = self - self.set_aspect('auto') + self._aspect = 'auto' self._adjustable = 'box' - self.set_anchor('C') + self._anchor = 'C' self._sharex = sharex self._sharey = sharey if sharex is not None: self._shared_x_axes.join(self, sharex) - if sharex._adjustable == 'box': - sharex._adjustable = 'datalim' - # warnings.warn( - # 'shared axes: "adjustable" is being changed to "datalim"') - self._adjustable = 'datalim' if sharey is not None: self._shared_y_axes.join(self, sharey) - if sharey._adjustable == 'box': - sharey._adjustable = 'datalim' - # warnings.warn( - # 'shared axes: "adjustable" is being changed to "datalim"') - self._adjustable = 'datalim' self.set_label(label) self.set_figure(fig) @@ -537,7 +527,11 @@ def __init__(self, fig, rect, self._hold = True self._connected = {} # a dict from events to (id, func) - self.cla() + try: + self.cla(clear_axis=False) # new xaxis, yaxis are already cleared + except TypeError: + self.cla() # For Axes subclasses lacking clear_axis argument. + # funcs used to format x and y - fall back on major formatters self.fmt_xdata = None self.fmt_ydata = None @@ -607,11 +601,11 @@ def get_window_extent(self, *args, **kwargs): def _init_axis(self): "move this out of __init__ because non-separable axes don't use it" self.xaxis = maxis.XAxis(self) - self.spines['bottom'].register_axis(self.xaxis) - self.spines['top'].register_axis(self.xaxis) + self.spines['bottom'].register_axis(self.xaxis, clear_axis=False) + self.spines['top'].register_axis(self.xaxis, clear_axis=False) self.yaxis = maxis.YAxis(self) - self.spines['left'].register_axis(self.yaxis) - self.spines['right'].register_axis(self.yaxis) + self.spines['left'].register_axis(self.yaxis, clear_axis=False) + self.spines['right'].register_axis(self.yaxis, clear_axis=False) self._update_transScale() def set_figure(self, fig): @@ -634,8 +628,7 @@ def set_figure(self, fig): def _set_lim_and_transforms(self): """ - set the *dataLim* and *viewLim* - :class:`~matplotlib.transforms.Bbox` attributes and the + set the *_xaxis_transform*, *_yaxis_transform*, *transScale*, *transData*, *transLimits* and *transAxes* transformations. @@ -952,7 +945,7 @@ def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'): ('bottom', mspines.Spine.linear_spine(self, 'bottom')), ('top', mspines.Spine.linear_spine(self, 'top'))]) - def cla(self): + def cla(self, clear_axis=True): """Clear the current axes.""" # Note: this is called by Axes.__init__() @@ -965,51 +958,46 @@ def cla(self): xaxis_visible = self.xaxis.get_visible() yaxis_visible = self.yaxis.get_visible() - self.xaxis.cla() - self.yaxis.cla() + # The axis cla() sets the scale and default locators and + # formatters. It needs to know if the axis is shared + # so that it can preserve the shared scale. + shared_x = self._sharex.xaxis if self._sharex else None + shared_y = self._sharey.yaxis if self._sharey else None + + if clear_axis: + self.xaxis.cla(shared_x) + self.yaxis.cla(shared_y) + for name, spine in six.iteritems(self.spines): - spine.cla() + spine.cla(clear_axis=False) # Clears only the position. self.ignore_existing_data_limits = True self.callbacks = cbook.CallbackRegistry() - if self._sharex is not None: - # major and minor are class instances with + if shared_x is not None: + # major and minor are axis.Ticker class instances with # locator and formatter attributes - self.xaxis.major = self._sharex.xaxis.major - self.xaxis.minor = self._sharex.xaxis.minor + self.xaxis.major = shared_x.major + self.xaxis.minor = shared_x.minor x0, x1 = self._sharex.get_xlim() self.set_xlim(x0, x1, emit=False, auto=None) - self.xaxis._scale = mscale.scale_factory( - self._sharex.xaxis.get_scale(), self.xaxis) else: - self.xaxis._set_scale('linear') try: self.set_xlim(0, 1) except TypeError: pass - if self._sharey is not None: - self.yaxis.major = self._sharey.yaxis.major - self.yaxis.minor = self._sharey.yaxis.minor + if shared_y is not None: + self.yaxis.major = shared_y.major + self.yaxis.minor = shared_y.minor y0, y1 = self._sharey.get_ylim() self.set_ylim(y0, y1, emit=False, auto=None) - self.yaxis._scale = mscale.scale_factory( - self._sharey.yaxis.get_scale(), self.yaxis) else: - self.yaxis._set_scale('linear') try: self.set_ylim(0, 1) except TypeError: pass - # update the minor locator for x and y axis based on rcParams - if (rcParams['xtick.minor.visible']): - self.xaxis.set_minor_locator(mticker.AutoMinorLocator()) - - if (rcParams['ytick.minor.visible']): - self.yaxis.set_minor_locator(mticker.AutoMinorLocator()) - self._autoscaleXon = True self._autoscaleYon = True self._xmargin = rcParams['axes.xmargin'] @@ -1093,6 +1081,13 @@ def cla(self): self.yaxis.set_visible(yaxis_visible) self.patch.set_visible(patch_visible) + # It is not clear to me (EF) why this reset is needed here, but + # it does seem to be needed somewhere in this vicinity. Otherwise, + # setting tick rotation via set_params doesn't work until the + # first draw has occurred. + self.xaxis.reset_ticks() + self.yaxis.reset_ticks() + self.stale = True @cbook.deprecated("2.1", alternative="Axes.patch") @@ -1224,7 +1219,7 @@ def hold(self, b=None): def get_aspect(self): return self._aspect - def set_aspect(self, aspect, adjustable=None, anchor=None): + def set_aspect(self, aspect, adjustable=None, anchor=None, share=False): """ *aspect* @@ -1245,12 +1240,9 @@ def set_aspect(self, aspect, adjustable=None, anchor=None): ============ ===================================== 'box' change physical size of axes 'datalim' change xlim or ylim - 'box-forced' same as 'box', but axes can be shared ============ ===================================== - 'box' does not allow axes sharing, as this can cause - unintended side effect. For cases when sharing axes is - fine, use 'box-forced'. + When both axes are shared, only 'box' is allowable. *anchor* @@ -1270,8 +1262,17 @@ def set_aspect(self, aspect, adjustable=None, anchor=None): else: self._aspect = float(aspect) # raise ValueError if necessary - if adjustable is not None: - self.set_adjustable(adjustable) + if share and self in self._shared_x_axes: + for ax in self._shared_x_axes.get_siblings(self): + ax._aspect = aspect + if share and self in self._shared_y_axes: + for ax in self._shared_y_axes.get_siblings(self): + ax._aspect = aspect + + if adjustable is None: + adjustable = self._adjustable + self.set_adjustable(adjustable, share=share) # Handle sharing. + if anchor is not None: self.set_anchor(anchor) self.stale = True @@ -1279,15 +1280,18 @@ def set_aspect(self, aspect, adjustable=None, anchor=None): def get_adjustable(self): return self._adjustable - def set_adjustable(self, adjustable): + def set_adjustable(self, adjustable, share=False): """ - ACCEPTS: [ 'box' | 'datalim' | 'box-forced'] + ACCEPTS: [ 'box' | 'datalim'] """ + # FIXME: add box-forced deprecation if adjustable in ('box', 'datalim', 'box-forced'): - if self in self._shared_x_axes or self in self._shared_y_axes: - if adjustable == 'box': - raise ValueError( - 'adjustable must be "datalim" for shared axes') + if share and self in self._shared_x_axes: + for ax in self._shared_x_axes.get_siblings(self): + ax._adjustable = adjustable + if share and self in self._shared_y_axes: + for ax in self._shared_y_axes.get_siblings(self): + ax._adjustable = adjustable self._adjustable = adjustable else: raise ValueError('argument must be "box", or "datalim"') @@ -1387,14 +1391,6 @@ def apply_aspect(self, position=None): else: A = aspect - # Ensure at drawing time that any Axes involved in axis-sharing - # does not have its position changed. - if self in self._shared_x_axes or self in self._shared_y_axes: - if self._adjustable == 'box': - self._adjustable = 'datalim' - warnings.warn( - 'shared axes: "adjustable" is being changed to "datalim"') - figW, figH = self.get_figure().get_size_inches() fig_aspect = figH / figW if self._adjustable in ['box', 'box-forced']: @@ -1452,15 +1448,15 @@ def apply_aspect(self, position=None): xm = 0 ym = 0 - changex = (self in self._shared_y_axes and - self not in self._shared_x_axes) - changey = (self in self._shared_x_axes and - self not in self._shared_y_axes) - if changex and changey: - warnings.warn("adjustable='datalim' cannot work with shared " - "x and y axes") - return - if changex: + shared_x = self in self._shared_x_axes + shared_y = self in self._shared_y_axes + # Not sure whether we need this check: + if shared_x and shared_y: + raise RuntimeError("adjustable='datalim' is not allowed when both" + " axes are shared.") + + # If y is shared, then we are only allowed to change x, etc. + if shared_y: adjust_y = False else: if xmarg > xm and ymarg > ym: @@ -1468,7 +1464,8 @@ def apply_aspect(self, position=None): (Xmarg < 0 and y_expander > 0)) else: adjy = y_expander > 0 - adjust_y = changey or adjy # (Ymarg > xmarg) + adjust_y = shared_x or adjy # (Ymarg > xmarg) + if adjust_y: yc = 0.5 * (ymin + ymax) y0 = yc - Ysize / 2.0 @@ -3915,6 +3912,9 @@ def _make_twin_axes(self, *kl, **kwargs): make a twinx axes of self. This is used for twinx and twiny. """ ax2 = self.figure.add_axes(self.get_position(True), *kl, **kwargs) + # do not touch every thing shared, just this and it's twin. + self.set_adjustable('datalim') + ax2.set_adjustable('datalim') return ax2 def twinx(self): @@ -3977,9 +3977,9 @@ def twiny(self): return ax2 def get_shared_x_axes(self): - 'Return a copy of the shared axes Grouper object for x axes' + 'Return a reference to the shared axes Grouper object for x axes' return self._shared_x_axes def get_shared_y_axes(self): - 'Return a copy of the shared axes Grouper object for y axes' + 'Return a reference to the shared axes Grouper object for y axes' return self._shared_y_axes diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 870c5e8f6f72..91930dd9611a 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -24,6 +24,14 @@ GRIDLINE_INTERPOLATION_STEPS = 180 +# This list is being used for compatibility with Axes.grid, which +# allows all Line2D kwargs. +_line_AI = artist.ArtistInspector(mlines.Line2D) +_line_param_names = _line_AI.get_setters() +_line_param_aliases = [list(d.keys())[0] for d in _line_AI.aliasd.values()] +_gridline_param_names = ['grid_' + name + for name in _line_param_names + _line_param_aliases] + class Tick(artist.Artist): """ @@ -82,6 +90,11 @@ def __init__(self, axes, loc, label, label2On=False, major=True, labelrotation=0, + grid_color=None, + grid_linestyle=None, + grid_linewidth=None, + grid_alpha=None, + **kw # Other Line2D kwargs applied to gridlines. ): """ bbox is the Bound2D bounding box in display coords of the Axes @@ -149,6 +162,17 @@ def __init__(self, axes, loc, label, zorder = mlines.Line2D.zorder self._zorder = zorder + self._grid_color = (rcParams['grid.color'] + if grid_color is None else grid_color) + self._grid_linestyle = (rcParams['grid.linestyle'] + if grid_linestyle is None else grid_linestyle) + self._grid_linewidth = (rcParams['grid.linewidth'] + if grid_linewidth is None else grid_linewidth) + self._grid_alpha = (rcParams['grid.alpha'] + if grid_alpha is None else grid_alpha) + + self._grid_kw = {k[5:]: v for k, v in kw.items()} + self.apply_tickdir(tickdir) self.tick1line = self._get_tick1line() @@ -344,6 +368,14 @@ def _apply_params(self, **kw): v = getattr(self.label1, 'get_' + k)() setattr(self, '_label' + k, v) + grid_list = [k for k in six.iteritems(kw) + if k[0] in _gridline_param_names] + if grid_list: + grid_kw = {k[5:]: v for k, v in grid_list} + self.gridline.set(**grid_kw) + for k, v in six.iteritems(grid_kw): + setattr(self, '_grid_' + k, v) + def update_position(self, loc): 'Set the location of tick in data coords with scalar *loc*' raise NotImplementedError('Derived must override') @@ -445,11 +477,12 @@ def _get_gridline(self): 'Get the default line2D instance' # x in data coords, y in axes coords l = mlines.Line2D(xdata=(0.0, 0.0), ydata=(0, 1.0), - color=rcParams['grid.color'], - linestyle=rcParams['grid.linestyle'], - linewidth=rcParams['grid.linewidth'], - alpha=rcParams['grid.alpha'], - markersize=0) + color=self._grid_color, + linestyle=self._grid_linestyle, + linewidth=self._grid_linewidth, + alpha=self._grid_alpha, + markersize=0, + **self._grid_kw) l.set_transform(self.axes.get_xaxis_transform(which='grid')) l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS self._set_artist_props(l) @@ -568,12 +601,12 @@ def _get_gridline(self): 'Get the default line2D instance' # x in axes coords, y in data coords l = mlines.Line2D(xdata=(0, 1), ydata=(0, 0), - color=rcParams['grid.color'], - linestyle=rcParams['grid.linestyle'], - linewidth=rcParams['grid.linewidth'], - alpha=rcParams['grid.alpha'], - markersize=0) - + color=self._grid_color, + linestyle=self._grid_linestyle, + linewidth=self._grid_linewidth, + alpha=self._grid_alpha, + markersize=0, + **self._grid_kw) l.set_transform(self.axes.get_yaxis_transform(which='grid')) l.get_path()._interpolation_steps = GRIDLINE_INTERPOLATION_STEPS self._set_artist_props(l) @@ -626,13 +659,6 @@ def __init__(self, axes, pickradius=15): artist.Artist.__init__(self) self.set_figure(axes.figure) - # Keep track of setting to the default value, this allows use to know - # if any of the following values is explicitly set by the user, so as - # to not overwrite their settings with any of our 'auto' settings. - self.isDefault_majloc = True - self.isDefault_minloc = True - self.isDefault_majfmt = True - self.isDefault_minfmt = True self.isDefault_label = True self.axes = axes @@ -656,7 +682,6 @@ def __init__(self, axes, pickradius=15): self._minor_tick_kw = dict() self.cla() - self._set_scale('linear') def set_label_coords(self, x, y, transform=None): """ @@ -719,24 +744,16 @@ def get_children(self): children.extend(minorticks) return children - def cla(self): + def cla(self, shared=None): 'clear the current axis' - self.set_major_locator(mticker.AutoLocator()) - self.set_major_formatter(mticker.ScalarFormatter()) - self.set_minor_locator(mticker.NullLocator()) - self.set_minor_formatter(mticker.NullFormatter()) - self.set_label_text('') - self._set_artist_props(self.label) + self.label.set_text('') # self.set_label_text would change isDefault_ - # Keep track of setting to the default value, this allows use to know - # if any of the following values is explicitly set by the user, so as - # to not overwrite their settings with any of our 'auto' settings. - self.isDefault_majloc = True - self.isDefault_minloc = True - self.isDefault_majfmt = True - self.isDefault_minfmt = True - self.isDefault_label = True + if shared is None: + self._set_scale('linear') + else: + name = shared.get_scale() + self._scale = mscale.scale_factory(name, self) # Clear the callback registry for this axis, or it may "leak" self.callbacks = cbook.CallbackRegistry() @@ -747,9 +764,6 @@ def cla(self): self._gridOnMinor = (rcParams['axes.grid'] and rcParams['axes.grid.which'] in ('both', 'minor')) - self.label.set_text('') - self._set_artist_props(self.label) - self.reset_ticks() self.converter = None @@ -758,9 +772,11 @@ def cla(self): self.stale = True def reset_ticks(self): - # build a few default ticks; grow as necessary later; only - # define 1 so properties set on ticks will be copied as they - # grow + """ + Re-initialize the major and minor Tick lists. + + Each list starts with a single fresh Tick. + """ del self.majorTicks[:] del self.minorTicks[:] @@ -769,6 +785,11 @@ def reset_ticks(self): self._lastNumMajorTicks = 1 self._lastNumMinorTicks = 1 + try: + self.set_clip_path(self.axes.patch) + except AttributeError: + pass + def set_tick_params(self, which='major', reset=False, **kw): """ Set appearance parameters for ticks and ticklabels. @@ -786,6 +807,7 @@ def set_tick_params(self, which='major', reset=False, **kw): if reset: d.clear() d.update(kwtrans) + if reset: self.reset_ticks() else: @@ -809,7 +831,8 @@ def _translate_tick_kw(kw, to_init_kw=True): kwkeys1 = ['length', 'direction', 'left', 'bottom', 'right', 'top', 'labelleft', 'labelbottom', 'labelright', 'labeltop', 'rotation'] - kwkeys = kwkeys0 + kwkeys1 + kwkeys2 = _gridline_param_names + kwkeys = kwkeys0 + kwkeys1 + kwkeys2 kwtrans = dict() if to_init_kw: if 'length' in kw: @@ -951,7 +974,7 @@ def _update_ticks(self, renderer): """ interval = self.get_view_interval() - tick_tups = list(self.iter_ticks()) + tick_tups = list(self.iter_ticks()) # iter_ticks calls the locator if self._smart_bounds and tick_tups: # handle inverted limits view_low, view_high = sorted(interval) @@ -1376,30 +1399,21 @@ def grid(self, b=None, which='major', **kwargs): if len(kwargs): b = True which = which.lower() + gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()} if which in ['minor', 'both']: if b is None: self._gridOnMinor = not self._gridOnMinor else: self._gridOnMinor = b - for tick in self.minorTicks: # don't use get_ticks here! - if tick is None: - continue - tick.gridOn = self._gridOnMinor - if len(kwargs): - tick.gridline.update(kwargs) - self._minor_tick_kw['gridOn'] = self._gridOnMinor + self.set_tick_params(which='minor', gridOn=self._gridOnMinor, + **gridkw) if which in ['major', 'both']: if b is None: self._gridOnMajor = not self._gridOnMajor else: self._gridOnMajor = b - for tick in self.majorTicks: # don't use get_ticks here! - if tick is None: - continue - tick.gridOn = self._gridOnMajor - if len(kwargs): - tick.gridline.update(kwargs) - self._major_tick_kw['gridOn'] = self._gridOnMajor + self.set_tick_params(which='major', gridOn=self._gridOnMajor, + **gridkw) self.stale = True def update_units(self, data): @@ -1429,11 +1443,11 @@ def _update_axisinfo(self): check the axis converter for the stored units to see if the axis info needs to be updated """ - if self.converter is None: return info = self.converter.axisinfo(self.units, self) + if info is None: return if info.majloc is not None and \ diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 29a38b6663c2..e1df53b97a04 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -10,9 +10,10 @@ from matplotlib.ticker import (NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter) from matplotlib.ticker import (NullLocator, LogLocator, AutoLocator, + AutoMinorLocator, SymmetricalLogLocator, LogitLocator) from matplotlib.transforms import Transform, IdentityTransform -from matplotlib import docstring +from matplotlib import docstring, rcParams class ScaleBase(object): @@ -73,8 +74,12 @@ def set_default_locators_and_formatters(self, axis): """ axis.set_major_locator(AutoLocator()) axis.set_major_formatter(ScalarFormatter()) - axis.set_minor_locator(NullLocator()) axis.set_minor_formatter(NullFormatter()) + # update the minor locator for x and y axis based on rcParams + if rcParams['xtick.minor.visible']: + axis.set_minor_locator(AutoMinorLocator()) + else: + axis.set_minor_locator(NullLocator()) def get_transform(self): """ diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index c6b04fe43dba..de5c294ad856 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -151,7 +151,7 @@ def _ensure_position_is_set(self): self._position = ('outward', 0.0) # in points self.set_position(self._position) - def register_axis(self, axis): + def register_axis(self, axis, clear_axis=True): """register an axis An axis should be registered with its corresponding spine from @@ -159,14 +159,14 @@ def register_axis(self, axis): properties when needed. """ self.axis = axis - if self.axis is not None: + if clear_axis and self.axis is not None: self.axis.cla() self.stale = True - def cla(self): + def cla(self, clear_axis=True): """Clear the current spine""" self._position = None # clear position - if self.axis is not None: + if clear_axis and self.axis is not None: self.axis.cla() def is_frame_like(self): diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf index 209842345b46..c16fc9c2d916 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf and b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.png b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.png index 9abe25a233cd..7af54d6eefbf 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.png and b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg index 668efde6a8ad..ff18e2b4df0b 100644 --- a/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg +++ b/lib/matplotlib/tests/baseline_images/test_skew/skew_rects.svg @@ -2,7 +2,7 @@ - + - - - - - - - @@ -60,188 +60,188 @@ L 230.4 86.4 +" id="m368fc901b1" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="mc63e59a608" style="stroke:#000000;stroke-width:0.5;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + - + + + + - + - + - + - + - - - + - - - - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -249,209 +249,209 @@ L -4 0 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -459,209 +459,209 @@ L 460.8 86.4 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -669,209 +669,209 @@ L 691.2 86.4 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -879,209 +879,209 @@ L 921.6 86.4 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1089,209 +1089,209 @@ L 1152 86.4 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1299,209 +1299,209 @@ L 230.4 247.282759 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1509,209 +1509,209 @@ L 460.8 247.282759 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1719,209 +1719,209 @@ L 691.2 247.282759 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1929,209 +1929,209 @@ L 921.6 247.282759 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2139,209 +2139,209 @@ L 1152 247.282759 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2349,209 +2349,209 @@ L 230.4 408.165517 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2559,209 +2559,209 @@ L 460.8 408.165517 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2769,209 +2769,209 @@ L 691.2 408.165517 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2979,209 +2979,209 @@ L 921.6 408.165517 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3189,209 +3189,209 @@ L 1152 408.165517 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3399,209 +3399,209 @@ L 230.4 569.048276 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3609,209 +3609,209 @@ L 460.8 569.048276 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3819,209 +3819,209 @@ L 691.2 569.048276 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4029,209 +4029,209 @@ L 921.6 569.048276 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4239,209 +4239,209 @@ L 1152 569.048276 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4449,209 +4449,209 @@ L 230.4 729.931034 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4659,209 +4659,209 @@ L 460.8 729.931034 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -4869,209 +4869,209 @@ L 691.2 729.931034 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5079,209 +5079,209 @@ L 921.6 729.931034 - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - + - + - + - + - + - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -5289,80 +5289,80 @@ L 1152 729.931034 - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index f1cc67fde7a4..14ef9f04fdbe 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4220,6 +4220,57 @@ def test_empty_shared_subplots(): assert y1 >= 6 +def test_shared_with_aspect(): + # allow sharing one axis + for adjustable in ['box', 'datalim']: + fig, axes = plt.subplots(nrows=2, sharex=True) + axes[0].set_aspect(2, adjustable=adjustable, share=True) + assert axes[1].get_aspect() == 2 + assert axes[1].get_adjustable() == adjustable + + fig, axes = plt.subplots(nrows=2, sharex=True) + axes[0].set_aspect(2, adjustable=adjustable) + assert axes[1].get_aspect() == 'auto' + + # Share 2 axes only with 'box': + fig, axes = plt.subplots(nrows=2, sharex=True, sharey=True) + axes[0].set_aspect(2, share=True) + axes[0].plot([1, 2], [3, 4]) + axes[1].plot([3, 4], [1, 2]) + plt.draw() # Trigger apply_aspect(). + assert axes[0].get_xlim() == axes[1].get_xlim() + assert axes[0].get_ylim() == axes[1].get_ylim() + + # Different aspect ratios: + for adjustable in ['box', 'datalim']: + fig, axes = plt.subplots(nrows=2, sharey=True) + axes[0].set_aspect(2, adjustable=adjustable) + axes[1].set_aspect(0.5, adjustable=adjustable) + axes[0].plot([1, 2], [3, 4]) + axes[1].plot([3, 4], [1, 2]) + plt.draw() # Trigger apply_aspect(). + assert axes[0].get_xlim() != axes[1].get_xlim() + assert axes[0].get_ylim() == axes[1].get_ylim() + fig_aspect = fig.bbox_inches.height / fig.bbox_inches.width + for ax in axes: + p = ax.get_position() + box_aspect = p.height / p.width + lim_aspect = ax.viewLim.height / ax.viewLim.width + expected = fig_aspect * box_aspect / lim_aspect + assert round(expected, 4) == round(ax.get_aspect(), 4) + + +@pytest.mark.parametrize('twin', ('x', 'y')) +def test_twin_with_aspect(twin): + fig, ax = plt.subplots() + # test twinx or twiny + ax_twin = getattr(ax, 'twin{}'.format(twin))() + ax.set_aspect(5) + ax_twin.set_aspect(2) + assert_array_equal(ax.bbox.extents, + ax_twin.bbox.extents) + + def test_relim_visible_only(): x1 = (0., 10.) y1 = (0., 10.) diff --git a/lib/matplotlib/tests/test_skew.py b/lib/matplotlib/tests/test_skew.py index 79cb75c2c273..a80d3cdaf002 100644 --- a/lib/matplotlib/tests/test_skew.py +++ b/lib/matplotlib/tests/test_skew.py @@ -189,16 +189,16 @@ def test_set_line_coll_dash_image(): @image_comparison(baseline_images=['skew_rects'], remove_text=True) -def test_skew_rectange(): +def test_skew_rectangle(): - fix, axes = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(16, 12)) + fix, axes = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(8, 8)) axes = axes.flat rotations = list(itertools.product([-3, -1, 0, 1, 3], repeat=2)) - axes[0].set_xlim([-4, 4]) - axes[0].set_ylim([-4, 4]) - axes[0].set_aspect('equal') + axes[0].set_xlim([-3, 3]) + axes[0].set_ylim([-3, 3]) + axes[0].set_aspect('equal', share=True) for ax, (xrots, yrots) in zip(axes, rotations): xdeg, ydeg = 45 * xrots, 45 * yrots @@ -209,4 +209,4 @@ def test_skew_rectange(): transform=t + ax.transData, alpha=0.5, facecolor='coral')) - plt.subplots_adjust(wspace=0, left=0, right=1, bottom=0) + plt.subplots_adjust(wspace=0, left=0.01, right=0.99, bottom=0.01, top=0.99) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy