diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index a146df58c9f8..3e1bb8fbb9e9 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -47,21 +47,7 @@ Margins and Autoscaling :toctree: _as_gen :nosignatures: - Artist.get_bottom_margin - Artist.get_left_margin - Artist.get_margins - Artist.get_top_margin - Artist.margins - Artist.left_margin - Artist.get_right_margin - Artist.bottom_margin - Artist.right_margin - Artist.set_bottom_margin - Artist.set_left_margin - Artist.set_margins - Artist.set_right_margin - Artist.set_top_margin - Artist.top_margin + Artist.sticky_edges Clipping -------- diff --git a/doc/api/axes_api.rst b/doc/api/axes_api.rst index ef535514b11f..92752501480e 100644 --- a/doc/api/axes_api.rst +++ b/doc/api/axes_api.rst @@ -318,32 +318,6 @@ Autoscaling -Margins -~~~~~~~ - -.. autosummary:: - :toctree: _as_gen - :nosignatures: - - - Axes.margins - Axes.set_margins - Axes.get_margins - Axes.bottom_margin - Axes.get_bottom_margin - Axes.get_left_margin - Axes.get_right_margin - Axes.get_top_margin - Axes.left_margin - Axes.right_margin - Axes.set_bottom_margin - Axes.set_left_margin - Axes.set_top_margin - Axes.set_right_margin - Axes.set_xmargin - Axes.top_margin - - Aspect ratio ------------ diff --git a/doc/api/axis_api.rst b/doc/api/axis_api.rst index 287fa94fa015..67eb83a16313 100644 --- a/doc/api/axis_api.rst +++ b/doc/api/axis_api.rst @@ -437,7 +437,6 @@ Ticks Tick.add_callback Tick.aname Tick.axes - Tick.bottom_margin Tick.contains Tick.convert_xunits Tick.convert_yunits @@ -448,7 +447,6 @@ Ticks Tick.get_alpha Tick.get_animated Tick.get_axes - Tick.get_bottom_margin Tick.get_children Tick.get_clip_box Tick.get_clip_on @@ -458,15 +456,11 @@ Ticks Tick.get_figure Tick.get_gid Tick.get_label - Tick.get_left_margin - Tick.get_margins Tick.get_path_effects Tick.get_picker Tick.get_rasterized - Tick.get_right_margin Tick.get_sketch_params Tick.get_snap - Tick.get_top_margin Tick.get_transform Tick.get_transformed_clip_path_and_affine Tick.get_url @@ -477,8 +471,6 @@ Ticks Tick.hitlist Tick.is_figure_set Tick.is_transform_set - Tick.left_margin - Tick.margins Tick.mouseover Tick.pchanged Tick.pick @@ -486,13 +478,11 @@ Ticks Tick.properties Tick.remove Tick.remove_callback - Tick.right_margin Tick.set Tick.set_agg_filter Tick.set_alpha Tick.set_animated Tick.set_axes - Tick.set_bottom_margin Tick.set_clip_box Tick.set_clip_on Tick.set_clip_path @@ -500,21 +490,16 @@ Ticks Tick.set_figure Tick.set_gid Tick.set_label - Tick.set_left_margin - Tick.set_margins Tick.set_path_effects Tick.set_picker Tick.set_rasterized - Tick.set_right_margin Tick.set_sketch_params Tick.set_snap - Tick.set_top_margin Tick.set_transform Tick.set_url Tick.set_visible Tick.set_zorder Tick.stale - Tick.top_margin Tick.update Tick.update_from Tick.zorder @@ -522,7 +507,6 @@ Ticks XTick.add_callback XTick.aname XTick.axes - XTick.bottom_margin XTick.contains XTick.convert_xunits XTick.convert_yunits @@ -533,7 +517,6 @@ Ticks XTick.get_alpha XTick.get_animated XTick.get_axes - XTick.get_bottom_margin XTick.get_children XTick.get_clip_box XTick.get_clip_on @@ -543,15 +526,11 @@ Ticks XTick.get_figure XTick.get_gid XTick.get_label - XTick.get_left_margin - XTick.get_margins XTick.get_path_effects XTick.get_picker XTick.get_rasterized - XTick.get_right_margin XTick.get_sketch_params XTick.get_snap - XTick.get_top_margin XTick.get_transform XTick.get_transformed_clip_path_and_affine XTick.get_url @@ -562,8 +541,6 @@ Ticks XTick.hitlist XTick.is_figure_set XTick.is_transform_set - XTick.left_margin - XTick.margins XTick.mouseover XTick.pchanged XTick.pick @@ -571,13 +548,11 @@ Ticks XTick.properties XTick.remove XTick.remove_callback - XTick.right_margin XTick.set XTick.set_agg_filter XTick.set_alpha XTick.set_animated XTick.set_axes - XTick.set_bottom_margin XTick.set_clip_box XTick.set_clip_on XTick.set_clip_path @@ -585,21 +560,16 @@ Ticks XTick.set_figure XTick.set_gid XTick.set_label - XTick.set_left_margin - XTick.set_margins XTick.set_path_effects XTick.set_picker XTick.set_rasterized - XTick.set_right_margin XTick.set_sketch_params XTick.set_snap - XTick.set_top_margin XTick.set_transform XTick.set_url XTick.set_visible XTick.set_zorder XTick.stale - XTick.top_margin XTick.update XTick.update_from XTick.zorder @@ -607,7 +577,6 @@ Ticks YTick.add_callback YTick.aname YTick.axes - YTick.bottom_margin YTick.contains YTick.convert_xunits YTick.convert_yunits @@ -618,7 +587,6 @@ Ticks YTick.get_alpha YTick.get_animated YTick.get_axes - YTick.get_bottom_margin YTick.get_children YTick.get_clip_box YTick.get_clip_on @@ -628,15 +596,11 @@ Ticks YTick.get_figure YTick.get_gid YTick.get_label - YTick.get_left_margin - YTick.get_margins YTick.get_path_effects YTick.get_picker YTick.get_rasterized - YTick.get_right_margin YTick.get_sketch_params YTick.get_snap - YTick.get_top_margin YTick.get_transform YTick.get_transformed_clip_path_and_affine YTick.get_url @@ -647,8 +611,6 @@ Ticks YTick.hitlist YTick.is_figure_set YTick.is_transform_set - YTick.left_margin - YTick.margins YTick.mouseover YTick.pchanged YTick.pick @@ -656,13 +618,11 @@ Ticks YTick.properties YTick.remove YTick.remove_callback - YTick.right_margin YTick.set YTick.set_agg_filter YTick.set_alpha YTick.set_animated YTick.set_axes - YTick.set_bottom_margin YTick.set_clip_box YTick.set_clip_on YTick.set_clip_path @@ -670,21 +630,16 @@ Ticks YTick.set_figure YTick.set_gid YTick.set_label - YTick.set_left_margin - YTick.set_margins YTick.set_path_effects YTick.set_picker YTick.set_rasterized - YTick.set_right_margin YTick.set_sketch_params YTick.set_snap - YTick.set_top_margin YTick.set_transform YTick.set_url YTick.set_visible YTick.set_zorder YTick.stale - YTick.top_margin YTick.update YTick.update_from YTick.zorder @@ -701,7 +656,6 @@ Axis Axis.add_callback Axis.aname Axis.axes - Axis.bottom_margin Axis.contains Axis.convert_xunits Axis.convert_yunits @@ -712,7 +666,6 @@ Axis Axis.get_alpha Axis.get_animated Axis.get_axes - Axis.get_bottom_margin Axis.get_children Axis.get_clip_box Axis.get_clip_on @@ -722,15 +675,11 @@ Axis Axis.get_figure Axis.get_gid Axis.get_label - Axis.get_left_margin - Axis.get_margins Axis.get_path_effects Axis.get_picker Axis.get_rasterized - Axis.get_right_margin Axis.get_sketch_params Axis.get_snap - Axis.get_top_margin Axis.get_transform Axis.get_transformed_clip_path_and_affine Axis.get_url @@ -741,8 +690,6 @@ Axis Axis.hitlist Axis.is_figure_set Axis.is_transform_set - Axis.left_margin - Axis.margins Axis.mouseover Axis.pchanged Axis.pick @@ -750,13 +697,11 @@ Axis Axis.properties Axis.remove Axis.remove_callback - Axis.right_margin Axis.set Axis.set_agg_filter Axis.set_alpha Axis.set_animated Axis.set_axes - Axis.set_bottom_margin Axis.set_clip_box Axis.set_clip_on Axis.set_clip_path @@ -764,21 +709,16 @@ Axis Axis.set_figure Axis.set_gid Axis.set_label - Axis.set_left_margin - Axis.set_margins Axis.set_path_effects Axis.set_picker Axis.set_rasterized - Axis.set_right_margin Axis.set_sketch_params Axis.set_snap - Axis.set_top_margin Axis.set_transform Axis.set_url Axis.set_visible Axis.set_zorder Axis.stale - Axis.top_margin Axis.update Axis.update_from Axis.zorder @@ -786,7 +726,6 @@ Axis XAxis.add_callback XAxis.aname XAxis.axes - XAxis.bottom_margin XAxis.contains XAxis.convert_xunits XAxis.convert_yunits @@ -797,7 +736,6 @@ Axis XAxis.get_alpha XAxis.get_animated XAxis.get_axes - XAxis.get_bottom_margin XAxis.get_children XAxis.get_clip_box XAxis.get_clip_on @@ -807,15 +745,11 @@ Axis XAxis.get_figure XAxis.get_gid XAxis.get_label - XAxis.get_left_margin - XAxis.get_margins XAxis.get_path_effects XAxis.get_picker XAxis.get_rasterized - XAxis.get_right_margin XAxis.get_sketch_params XAxis.get_snap - XAxis.get_top_margin XAxis.get_transform XAxis.get_transformed_clip_path_and_affine XAxis.get_url @@ -826,8 +760,6 @@ Axis XAxis.hitlist XAxis.is_figure_set XAxis.is_transform_set - XAxis.left_margin - XAxis.margins XAxis.mouseover XAxis.pchanged XAxis.pick @@ -835,13 +767,11 @@ Axis XAxis.properties XAxis.remove XAxis.remove_callback - XAxis.right_margin XAxis.set XAxis.set_agg_filter XAxis.set_alpha XAxis.set_animated XAxis.set_axes - XAxis.set_bottom_margin XAxis.set_clip_box XAxis.set_clip_on XAxis.set_clip_path @@ -849,21 +779,16 @@ Axis XAxis.set_figure XAxis.set_gid XAxis.set_label - XAxis.set_left_margin - XAxis.set_margins XAxis.set_path_effects XAxis.set_picker XAxis.set_rasterized - XAxis.set_right_margin XAxis.set_sketch_params XAxis.set_snap - XAxis.set_top_margin XAxis.set_transform XAxis.set_url XAxis.set_visible XAxis.set_zorder XAxis.stale - XAxis.top_margin XAxis.update XAxis.update_from XAxis.zorder @@ -871,7 +796,6 @@ Axis YAxis.add_callback YAxis.aname YAxis.axes - YAxis.bottom_margin YAxis.contains YAxis.convert_xunits YAxis.convert_yunits @@ -882,7 +806,6 @@ Axis YAxis.get_alpha YAxis.get_animated YAxis.get_axes - YAxis.get_bottom_margin YAxis.get_children YAxis.get_clip_box YAxis.get_clip_on @@ -892,15 +815,11 @@ Axis YAxis.get_figure YAxis.get_gid YAxis.get_label - YAxis.get_left_margin - YAxis.get_margins YAxis.get_path_effects YAxis.get_picker YAxis.get_rasterized - YAxis.get_right_margin YAxis.get_sketch_params YAxis.get_snap - YAxis.get_top_margin YAxis.get_transform YAxis.get_transformed_clip_path_and_affine YAxis.get_url @@ -911,8 +830,6 @@ Axis YAxis.hitlist YAxis.is_figure_set YAxis.is_transform_set - YAxis.left_margin - YAxis.margins YAxis.mouseover YAxis.pchanged YAxis.pick @@ -920,13 +837,11 @@ Axis YAxis.properties YAxis.remove YAxis.remove_callback - YAxis.right_margin YAxis.set YAxis.set_agg_filter YAxis.set_alpha YAxis.set_animated YAxis.set_axes - YAxis.set_bottom_margin YAxis.set_clip_box YAxis.set_clip_on YAxis.set_clip_path @@ -934,21 +849,16 @@ Axis YAxis.set_figure YAxis.set_gid YAxis.set_label - YAxis.set_left_margin - YAxis.set_margins YAxis.set_path_effects YAxis.set_picker YAxis.set_rasterized - YAxis.set_right_margin YAxis.set_sketch_params YAxis.set_snap - YAxis.set_top_margin YAxis.set_transform YAxis.set_url YAxis.set_visible YAxis.set_zorder YAxis.stale - YAxis.top_margin YAxis.update YAxis.update_from YAxis.zorder diff --git a/doc/users/dflt_style_changes.rst b/doc/users/dflt_style_changes.rst index 8a01fa699656..b0ab000a6bcb 100644 --- a/doc/users/dflt_style_changes.rst +++ b/doc/users/dflt_style_changes.rst @@ -1022,37 +1022,7 @@ The size of the padding in the x and y directions is controlled by the ``'axes.xmargin'`` and ``'axes.ymargin'`` rcParams respectively. Whether the view limits should be 'round numbers' is controlled by the ``'axes.autolimit_mode'`` rcParam. In the original ``'round_number'`` mode, -the view limits coincide with ticks. With the new default value, ``'data'``, -the outermost ticks will usually be inside the view limits, not at the ends. -Also see `~matplotlib.axes.Axes.margins`. - -For a few `~matplotlib.artist.Artist` classes, margins are undesirable. -For example, a margin should not be added for a `~matplotlib.image.AxesImage` -created with `~matplotlib.axes.Axes.imshow`. To control the application of -the margins, the `~matplotlib.artist.Artist` class has gained the properties : - - - `~matplotlib.artist.Artist.top_margin` - - `~matplotlib.artist.Artist.bottom_margin` - - `~matplotlib.artist.Artist.left_margin` - - `~matplotlib.artist.Artist.right_margin` - - `~matplotlib.artist.Artist.margins` - -along with the complimentary ``get_*`` and ``set_*`` methods. When -computing the view limits, each `~matplotlib.artist.Artist` is -checked. If *any* artist returns `False` on a given side, -the margin will be omitted there. Some plotting methods and artists -have margins disabled (`False`) by default (for example -`~matplotlib.axes.Axes.bar` disables the bottom margin). To cancel -the margins for a specific artist, pass the kwargs : - - - ``top_margin=False`` - - ``bottom_margin=False`` - - ``left_margin=False`` - - ``right_margin=False`` - -to any plotting method or artist ``__init__`` which supports ``**kwargs`` (as -any unused kwargs eventually get passed to `~matplotlib.artist.Artist.update`). - +the view limits coincide with ticks. The previous default can be restored by using:: diff --git a/examples/api/filled_step.py b/examples/api/filled_step.py index 091d6cde7863..eec83329a0e2 100644 --- a/examples/api/filled_step.py +++ b/examples/api/filled_step.py @@ -70,10 +70,10 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', values = np.r_[values, values[-1]] bottoms = np.r_[bottoms, bottoms[-1]] if orientation == 'h': - return ax.fill_betweenx(edges, values, bottoms, left_margin=False, + return ax.fill_betweenx(edges, values, bottoms, **kwargs) elif orientation == 'v': - return ax.fill_between(edges, values, bottoms, bottom_margin=False, + return ax.fill_between(edges, values, bottoms, **kwargs) else: raise AssertionError("you should never be here") diff --git a/examples/pylab_examples/trigradient_demo.py b/examples/pylab_examples/trigradient_demo.py index 6114b8493de5..dbc48c3f4c20 100644 --- a/examples/pylab_examples/trigradient_demo.py +++ b/examples/pylab_examples/trigradient_demo.py @@ -64,18 +64,22 @@ def dipole_potential(x, y): #----------------------------------------------------------------------------- # Plot the triangulation, the potential iso-contours and the vector field #----------------------------------------------------------------------------- -plt.figure() -plt.gca().set_aspect('equal') -plt.triplot(triang, color='0.8') +fig, ax = plt.subplots() +ax.set_aspect('equal') +# Enforce the margins, and enlarge them to give room for the vectors. +ax.use_sticky_edges = False +ax.margins(0.07) + +ax.triplot(triang, color='0.8') levels = np.arange(0., 1., 0.01) cmap = cm.get_cmap(name='hot', lut=None) -plt.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, - linewidths=[2.0, 1.0, 1.0, 1.0]) +ax.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, + linewidths=[2.0, 1.0, 1.0, 1.0]) # Plots direction of the electrical vector field -plt.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm, - units='xy', scale=10., zorder=3, color='blue', - width=0.007, headwidth=3., headlength=4.) +ax.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm, + units='xy', scale=10., zorder=3, color='blue', + width=0.007, headwidth=3., headlength=4.) -plt.title('Gradient plot: an electrical dipole') +ax.set_title('Gradient plot: an electrical dipole') plt.show() diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index a3ae49e1d40c..4c9e59d7deea 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -2,7 +2,7 @@ unicode_literals) import six -from collections import OrderedDict +from collections import OrderedDict, namedtuple import re import warnings @@ -76,6 +76,9 @@ def _stale_axes_callback(self, val): self.axes.stale = val +_XYPair = namedtuple("_XYPair", "x y") + + class Artist(object): """ Abstract base class for someone who renders into a @@ -123,8 +126,7 @@ def __init__(self): self._snap = None self._sketch = rcParams['path.sketch'] self._path_effects = rcParams['path.effects'] - - self._margins = {} + self._sticky_edges = _XYPair([], []) def __getstate__(self): d = self.__dict__.copy() @@ -926,98 +928,28 @@ def set_zorder(self, level): self.pchanged() self.stale = True - def get_top_margin(self): - """ - Get whether a margin should be applied to the top of the Artist. - """ - return self._margins.get('top', True) - - def set_top_margin(self, margin): - """ - Set whether a margin should be applied to the top of the Artist. - """ - if margin != self._margins.get('top', True): - self.stale = True - self._margins['top'] = margin - - top_margin = property(get_top_margin, set_top_margin) - - def get_bottom_margin(self): - """ - Get whether a margin should be applied to the bottom of the Artist. - """ - return self._margins.get('bottom', True) - - def set_bottom_margin(self, margin): - """ - Set whether a margin should be applied to the bottom of the Artist. - """ - if margin != self._margins.get('bottom', True): - self.stale = True - self._margins['bottom'] = margin - - bottom_margin = property(get_bottom_margin, set_bottom_margin) - - def get_left_margin(self): - """ - Get whether a margin should be applied to the left of the Artist. + @property + def sticky_edges(self): """ - return self._margins.get('left', True) + `x` and `y` sticky edge lists. - def set_left_margin(self, margin): - """ - Set whether a margin should be applied to the left of the Artist. - """ - if margin != self._margins.get('left', True): - self.stale = True - self._margins['left'] = margin + When performing autoscaling, if a data limit coincides with a value in + the corresponding sticky_edges list, then no margin will be added--the + view limit "sticks" to the edge. A typical usecase is histograms, + where one usually expects no margin on the bottom edge (0) of the + histogram. - left_margin = property(get_left_margin, set_left_margin) + This attribute cannot be assigned to; however, the `x` and `y` lists + can be modified in place as needed. - def get_right_margin(self): - """ - Get whether a margin should be applied to the right of the Artist. - """ - return self._margins.get('right', True) + Examples + -------- - def set_right_margin(self, margin): - """ - Set whether a margin should be applied to the right of the Artist. - """ - if margin != self._margins.get('right', True): - self.stale = True - self._margins['right'] = margin + >>> artist.sticky_edges.x[:] = (xmin, xmax) + >>> artist.sticky_edges.y[:] = (ymin, ymax) - right_margin = property(get_right_margin, set_right_margin) - - def get_margins(self): - """ - Returns a dictionary describing whether a margin should be applied on - each of the sides (top, bottom, left and right). - """ - return self._margins - - def set_margins(self, margins): - """ - Set the dictionary describing whether a margin should be applied on - each of the sides (top, bottom, left and right). Missing keys are - assumed to be `True`. If `True` or `False` are passed in, all - sides are set to that value. """ - if margins in (True, False): - margins = { - 'top': margins, - 'bottom': margins, - 'left': margins, - 'right': margins - } - - if margins != self._margins: - self.stale = True - - self._margins = margins - - margins = property(get_margins, set_margins) + return self._sticky_edges def update_from(self, other): 'Copy properties from *other* to *self*.' @@ -1031,6 +963,8 @@ def update_from(self, other): self._label = other._label self._sketch = other._sketch self._path_effects = other._path_effects + self.sticky_edges.x[:] = other.sticky_edges.x[:] + self.sticky_edges.y[:] = other.sticky_edges.y[:] self.pchanged() self.stale = True diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 3b1554580f41..333e8b15534e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2097,13 +2097,6 @@ def make_iterable(x): if yerr is not None: yerr = self.convert_yunits(yerr) - margins = {} - - if orientation == 'vertical': - margins = {'bottom': False} - elif orientation == 'horizontal': - margins = {'left': False} - if align == 'center': if orientation == 'vertical': left = [left[i] - width[i] / 2. for i in xrange(len(left))] @@ -2128,11 +2121,13 @@ def make_iterable(x): edgecolor=e, linewidth=lw, label='_nolegend_', - margins=margins ) r.update(kwargs) r.get_path()._interpolation_steps = 100 - #print r.get_label(), label, 'label' in kwargs + if orientation == 'vertical': + r.sticky_edges.y.append(0) + elif orientation == 'horizontal': + r.sticky_edges.x.append(0) self.add_patch(r) patches.append(r) @@ -4424,6 +4419,8 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, corners = ((xmin, ymin), (xmax, ymax)) self.update_datalim(corners) + collection.sticky_edges.x[:] = [xmin, xmax] + collection.sticky_edges.y[:] = [ymin, ymax] self.autoscale_view(tight=True) # add the collection last @@ -5452,7 +5449,7 @@ def pcolor(self, *args, **kwargs): kwargs.setdefault('snap', False) - collection = mcoll.PolyCollection(verts, margins=False, **kwargs) + collection = mcoll.PolyCollection(verts, **kwargs) collection.set_alpha(alpha) collection.set_array(C) @@ -5481,13 +5478,15 @@ def pcolor(self, *args, **kwargs): x = transformed_pts[..., 0] y = transformed_pts[..., 1] + self.add_collection(collection, autolim=False) + minx = np.amin(x) maxx = np.amax(x) miny = np.amin(y) maxy = np.amax(y) - + collection.sticky_edges.x[:] = [minx, maxx] + collection.sticky_edges.y[:] = [miny, maxy] corners = (minx, miny), (maxx, maxy) - self.add_collection(collection, autolim=False) self.update_datalim(corners) self.autoscale_view() return collection @@ -5603,10 +5602,9 @@ def pcolormesh(self, *args, **kwargs): coords[:, 0] = X coords[:, 1] = Y - collection = mcoll.QuadMesh( - Nx - 1, Ny - 1, coords, - antialiased=antialiased, shading=shading, margins=False, - **kwargs) + collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords, + antialiased=antialiased, shading=shading, + **kwargs) collection.set_alpha(alpha) collection.set_array(C) if norm is not None and not isinstance(norm, mcolors.Normalize): @@ -5632,13 +5630,15 @@ def pcolormesh(self, *args, **kwargs): X = transformed_pts[..., 0] Y = transformed_pts[..., 1] + self.add_collection(collection, autolim=False) + minx = np.amin(X) maxx = np.amax(X) miny = np.amin(Y) maxy = np.amax(Y) - + collection.sticky_edges.x[:] = [minx, maxx] + collection.sticky_edges.y[:] = [miny, maxy] corners = (minx, miny), (maxx, maxy) - self.add_collection(collection, autolim=False) self.update_datalim(corners) self.autoscale_view() return collection @@ -5790,8 +5790,7 @@ def pcolorfast(self, *args, **kwargs): # The QuadMesh class can also be changed to # handle relevant superclass kwargs; the initializer # should do much more than it does now. - collection = mcoll.QuadMesh(nc, nr, coords, 0, edgecolors="None", - margins=False) + collection = mcoll.QuadMesh(nc, nr, coords, 0, edgecolors="None") collection.set_alpha(alpha) collection.set_array(C) collection.set_cmap(cmap) @@ -5800,27 +5799,23 @@ def pcolorfast(self, *args, **kwargs): xl, xr, yb, yt = X.min(), X.max(), Y.min(), Y.max() ret = collection - else: - # One of the image styles: + else: # It's one of the two image styles. xl, xr, yb, yt = x[0], x[-1], y[0], y[-1] - if style == "image": - - im = mimage.AxesImage(self, cmap, norm, - interpolation='nearest', - origin='lower', - extent=(xl, xr, yb, yt), - **kwargs) - im.set_data(C) - im.set_alpha(alpha) - self.add_image(im) - ret = im - if style == "pcolorimage": - im = mimage.PcolorImage(self, x, y, C, - cmap=cmap, - norm=norm, - alpha=alpha, - **kwargs) + if style == "image": + im = mimage.AxesImage(self, cmap, norm, + interpolation='nearest', + origin='lower', + extent=(xl, xr, yb, yt), + **kwargs) + im.set_data(C) + im.set_alpha(alpha) + elif style == "pcolorimage": + im = mimage.PcolorImage(self, x, y, C, + cmap=cmap, + norm=norm, + alpha=alpha, + **kwargs) self.add_image(im) ret = im @@ -5828,6 +5823,9 @@ def pcolorfast(self, *args, **kwargs): ret.set_clim(vmin, vmax) else: ret.autoscale_None() + + ret.sticky_edges.x[:] = [xl, xr] + ret.sticky_edges.y[:] = [yb, yt] self.update_datalim(np.array([[xl, yb], [xr, yt]])) self.autoscale_view(tight=True) return ret @@ -6240,21 +6238,17 @@ def _normalize_input(inp, ename='input'): else: n = [m[slc].cumsum()[slc] for m in n] - if orientation == 'horizontal': - margins = {'left': False} - else: - margins = {'bottom': False} - patches = [] + # Save autoscale state for later restoration; turn autoscaling + # off so we can do it all a single time at the end, instead + # of having it done by bar or fill and then having to be redone. + _saved_autoscalex = self.get_autoscalex_on() + _saved_autoscaley = self.get_autoscaley_on() + self.set_autoscalex_on(False) + self.set_autoscaley_on(False) + if histtype.startswith('bar'): - # Save autoscale state for later restoration; turn autoscaling - # off so we can do it all a single time at the end, instead - # of having it done by bar or fill and then having to be redone. - _saved_autoscalex = self.get_autoscalex_on() - _saved_autoscaley = self.get_autoscaley_on() - self.set_autoscalex_on(False) - self.set_autoscaley_on(False) totwidth = np.diff(bins) @@ -6301,10 +6295,6 @@ def _normalize_input(inp, ename='input'): bottom[:] = m boffset += dw - self.set_autoscalex_on(_saved_autoscalex) - self.set_autoscaley_on(_saved_autoscaley) - self.autoscale_view() - elif histtype.startswith('step'): # these define the perimeter of the polygon x = np.zeros(4 * len(bins) - 3) @@ -6331,19 +6321,19 @@ def _normalize_input(inp, ename='input'): if np.min(bottom) > 0: minimum = np.min(bottom) elif normed or weights is not None: - # For normed data, set to log base * minimum data value + # For normed data, set to minimum data value / logbase # (gives 1 full tick-label unit for the lowest filled bin) ndata = np.array(n) minimum = (np.min(ndata[ndata > 0])) / logbase else: - # For non-normed data, set the min to log base, + # For non-normed data, set the min to 1 / log base, # again so that there is 1 full tick-label unit # for the lowest bin minimum = 1.0 / logbase y[0], y[-1] = minimum, minimum else: - minimum = np.min(bins) + minimum = 0 if align == 'left' or align == 'center': x -= 0.5*(bins[1]-bins[0]) @@ -6384,42 +6374,20 @@ def _normalize_input(inp, ename='input'): closed=True if fill else None, facecolor=c, edgecolor=None if fill else c, - fill=fill if fill else None, - margins=margins)) + fill=fill if fill else None)) + for patch_list in patches: + for patch in patch_list: + if orientation == 'vertical': + patch.sticky_edges.y.append(minimum) + elif orientation == 'horizontal': + patch.sticky_edges.x.append(minimum) # we return patches, so put it back in the expected order patches.reverse() - # adopted from adjust_x/ylim part of the bar method - if orientation == 'horizontal': - xmin0 = max(_saved_bounds[0]*0.9, minimum) - xmax = self.dataLim.intervalx[1] - for m in n: - # make sure there are counts - if np.sum(m) > 0: - # filter out the 0 height bins - xmin = np.amin(m[m != 0]) - # If no counts, set min to zero - else: - xmin = 0.0 - xmin = max(xmin*0.9, minimum) if not input_empty else minimum - xmin = min(xmin0, xmin) - self.dataLim.intervalx = (xmin, xmax) - elif orientation == 'vertical': - ymin0 = max(_saved_bounds[1]*0.9, minimum) - ymax = self.dataLim.intervaly[1] - - for m in n: - # make sure there are counts - if np.sum(m) > 0: - # filter out the 0 height bins - ymin = np.amin(m[m != 0]) - # If no counts, set min to zero - else: - ymin = 0.0 - ymin = max(ymin*0.9, minimum) if not input_empty else minimum - ymin = min(ymin0, ymin) - self.dataLim.intervaly = (ymin, ymax) + self.set_autoscalex_on(_saved_autoscalex) + self.set_autoscaley_on(_saved_autoscaley) + self.autoscale_view() if label is None: labels = [None] @@ -6439,14 +6407,6 @@ def _normalize_input(inp, ename='input'): p.update(kwargs) p.set_label('_nolegend_') - if binsgiven: - if orientation == 'vertical': - self.update_datalim( - [(bins[0], 0), (bins[-1], 0)], updatey=False) - else: - self.update_datalim( - [(0, bins[0]), (0, bins[-1])], updatex=False) - if nx == 1: return n[0], bins, cbook.silent_list('Patch', patches[0]) else: diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index c7b8ccae1e37..664faf9e05df 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1030,6 +1030,7 @@ def cla(self): self._xmargin = rcParams['axes.xmargin'] self._ymargin = rcParams['axes.ymargin'] self._tight = None + self._use_sticky_edges = True self._update_transScale() # needed? self._get_lines = _process_plot_var_args(self) @@ -2034,6 +2035,28 @@ def set_autoscaley_on(self, b): """ self._autoscaleYon = b + @property + def use_sticky_edges(self): + """ + When autoscaling, whether to obey all `Artist.sticky_edges`. + + Default is ``True``. + + Setting this to ``False`` ensures that the specified margins + will be applied, even if the plot includes an image, for + example, which would otherwise force a view limit to coincide + with its data limit. + + The changing this property does not change the plot until + `autoscale` or `autoscale_view` is called. + """ + return self._use_sticky_edges + + @use_sticky_edges.setter + def use_sticky_edges(self, b): + self._use_sticky_edges = bool(b) + # No effect until next autoscaling, which will mark the axes as stale. + def set_xmargin(self, m): """ Set padding of X data limits prior to autoscaling. @@ -2199,53 +2222,22 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): case, use :meth:`matplotlib.axes.Axes.relim` prior to calling autoscale_view. """ - if tight is None: - _tight = self._tight - else: - _tight = self._tight = bool(tight) - - if self._xmargin or self._ymargin: - margins = { - 'top': True, - 'bottom': True, - 'left': True, - 'right': True - } - for artist_set in [self.collections, self.patches, self.lines, - self.artists, self.images]: - for artist in artist_set: - artist_margins = artist.margins - for key in ['left', 'right', 'top', 'bottom']: - margins[key] &= artist_margins.get(key, True) - - if self._xmargin: - for axes in self._shared_x_axes.get_siblings(self): - for artist_set in [axes.collections, axes.patches, - axes.lines, axes.artists, axes.images]: - for artist in artist_set: - artist_margins = artist.margins - for key in ['left', 'right']: - margins[key] &= artist_margins.get(key, True) - - if self._ymargin: - for axes in self._shared_y_axes.get_siblings(self): - for artist_set in [axes.collections, axes.patches, - axes.lines, axes.artists, axes.images]: - for artist in artist_set: - artist_margins = artist.margins - for key in ['top', 'bottom']: - margins[key] &= artist_margins.get(key, True) - else: - margins = { - 'top': False, - 'bottom': False, - 'left': False, - 'right': False - } + if tight is not None: + self._tight = bool(tight) + + if self.use_sticky_edges and (self._xmargin or self._ymargin): + stickies = [artist.sticky_edges for artist in self.get_children()] + x_stickies = sum([sticky.x for sticky in stickies], []) + y_stickies = sum([sticky.y for sticky in stickies], []) + if self.get_xscale().lower() == 'log': + x_stickies = [xs for xs in x_stickies if xs > 0] + if self.get_yscale().lower() == 'log': + y_stickies = [ys for ys in y_stickies if ys > 0] + else: # Small optimization. + x_stickies, y_stickies = [], [] def handle_single_axis(scale, autoscaleon, shared_axes, interval, - minpos, axis, margin, do_lower_margin, - do_upper_margin, set_bound): + minpos, axis, margin, stickies, set_bound): if not (scale and autoscaleon): return # nothing to do... @@ -2268,43 +2260,35 @@ def handle_single_axis(scale, autoscaleon, shared_axes, interval, x0, x1 = mtransforms.nonsingular( x0, x1, increasing=False, expander=0.05) - if margin > 0 and (do_lower_margin or do_upper_margin): - if axis.get_scale() == 'linear': - delta = (x1 - x0) * margin - if do_lower_margin: - x0 -= delta - if do_upper_margin: - x1 += delta - else: - # If we have a non-linear scale, we need to - # add the margin in figure space and then - # transform back - minpos = getattr(bb, minpos) - transform = axis.get_transform() - inverse_trans = transform.inverted() - x0, x1 = axis._scale.limit_range_for_scale( - x0, x1, minpos) - x0t, x1t = transform.transform([x0, x1]) - delta = (x1t - x0t) * margin - if do_lower_margin: - x0t -= delta - if do_upper_margin: - x1t += delta - x0, x1 = inverse_trans.transform([x0t, x1t]) - - if not _tight: + # Add the margin in figure space and then transform back, to handle + # non-linear scales. + minpos = getattr(bb, minpos) + transform = axis.get_transform() + inverse_trans = transform.inverted() + # We cannot use exact equality due to floating point issues e.g. + # with streamplot. + do_lower_margin = not np.any(np.isclose(x0, stickies)) + do_upper_margin = not np.any(np.isclose(x1, stickies)) + x0, x1 = axis._scale.limit_range_for_scale(x0, x1, minpos) + x0t, x1t = transform.transform([x0, x1]) + delta = (x1t - x0t) * margin + if do_lower_margin: + x0t -= delta + if do_upper_margin: + x1t += delta + x0, x1 = inverse_trans.transform([x0t, x1t]) + + if not self._tight: x0, x1 = locator.view_limits(x0, x1) set_bound(x0, x1) # End of definition of internal function 'handle_single_axis'. handle_single_axis( - scalex, self._autoscaleXon, self._shared_x_axes, - 'intervalx', 'minposx', self.xaxis, self._xmargin, - margins['left'], margins['right'], self.set_xbound) + scalex, self._autoscaleXon, self._shared_x_axes, 'intervalx', + 'minposx', self.xaxis, self._xmargin, x_stickies, self.set_xbound) handle_single_axis( - scaley, self._autoscaleYon, self._shared_y_axes, - 'intervaly', 'minposy', self.yaxis, self._ymargin, - margins['bottom'], margins['top'], self.set_ybound) + scaley, self._autoscaleYon, self._shared_y_axes, 'intervaly', + 'minposy', self.yaxis, self._ymargin, y_stickies, self.set_ybound) def _get_axis_list(self): return (self.xaxis, self.yaxis) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 32ddabbb5a73..a7962d36079b 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -13,7 +13,6 @@ from six.moves import xrange, zip from itertools import repeat import collections - import datetime import errno import functools diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 102d1a18ee5d..e1cb1f1bf257 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -766,6 +766,7 @@ class ContourSet(cm.ScalarMappable, ContourLabeler): same as levels for line contours; half-way between levels for filled contours. See :meth:`_process_colors`. """ + def __init__(self, ax, *args, **kwargs): """ Draw contour lines or filled regions, depending on @@ -804,7 +805,7 @@ def __init__(self, ax, *args, **kwargs): level0segs = [polygon0] and level0kinds = [polygon0kinds]. Keyword arguments are as described in - :class:`~matplotlib.contour.QuadContourSet` object. + :attr:`matplotlib.contour.QuadContourSet.contour_doc`. **Examples:** @@ -937,8 +938,7 @@ def __init__(self, ax, *args, **kwargs): edgecolors='none', alpha=self.alpha, transform=self.get_transform(), - zorder=zorder, - margins=False) + zorder=zorder) self.ax.add_collection(col, autolim=False) self.collections.append(col) else: @@ -959,11 +959,17 @@ def __init__(self, ax, *args, **kwargs): linestyles=[lstyle], alpha=self.alpha, transform=self.get_transform(), - zorder=zorder, - margins=False) + zorder=zorder) col.set_label('_nolegend_') self.ax.add_collection(col, autolim=False) self.collections.append(col) + + for col in self.collections: + col.sticky_edges.x[:] = [self._mins[0], self._maxs[0]] + col.sticky_edges.y[:] = [self._mins[1], self._maxs[1]] + self.ax.update_datalim([self._mins, self._maxs]) + self.ax.autoscale_view(tight=True) + self.changed() # set the colors def get_transform(self): @@ -1069,21 +1075,10 @@ def _process_args(self, *args, **kwargs): raise ValueError('allkinds has different length to allsegs') # Determine x,y bounds and update axes data limits. - havelimits = False - for segs in self.allsegs: - for seg in segs: - seg = np.asarray(seg) - if havelimits: - min = np.minimum(min, seg.min(axis=0)) - max = np.maximum(max, seg.max(axis=0)) - else: - min = seg.min(axis=0) - max = seg.max(axis=0) - havelimits = True - - if havelimits: - self.ax.update_datalim([min, max]) - self.ax.autoscale_view(tight=True) + flatseglist = [s for seg in self.allsegs for s in seg] + points = np.concatenate(flatseglist, axis=0) + self._mins = points.min(axis=0) + self._maxs = points.max(axis=0) def _get_allsegs_and_allkinds(self): """ @@ -1423,16 +1418,6 @@ class QuadContourSet(ContourSet): Same as levels for line contours; half-way between levels for filled contours. See :meth:`_process_colors` method. """ - def __init__(self, ax, *args, **kwargs): - """ - Calculate and draw contour lines or filled regions, depending - on whether keyword arg 'filled' is False (default) or True. - - The first argument of the initializer must be an axes - object. The remaining arguments and keyword arguments - are described in QuadContourSet.contour_doc. - """ - ContourSet.__init__(self, ax, *args, **kwargs) def _process_args(self, *args, **kwargs): """ @@ -1448,6 +1433,8 @@ def _process_args(self, *args, **kwargs): contour_generator = args[0].Cntr else: contour_generator = args[0]._contour_generator + self._mins = args[0]._mins + self._maxs = args[0]._maxs else: self._corner_mask = kwargs.get('corner_mask', None) if self._corner_mask is None: @@ -1480,12 +1467,8 @@ def _process_args(self, *args, **kwargs): x = transformed_pts[..., 0] y = transformed_pts[..., 1] - x0 = ma.minimum(x) - x1 = ma.maximum(x) - y0 = ma.minimum(y) - y1 = ma.maximum(y) - self.ax.update_datalim([(x0, y0), (x1, y1)]) - self.ax.autoscale_view(tight=True) + self._mins = [ma.min(x), ma.min(y)] + self._maxs = [ma.max(x), ma.max(y)] if self._corner_mask == 'legacy': self.Cntr = contour_generator diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index a519cc17afe1..3cb306fb9371 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -234,7 +234,6 @@ def __init__(self, ax, self.set_filterrad(filterrad) self.set_interpolation(interpolation) self.set_resample(resample) - self.set_margins(False) self.axes = ax self._imcache = None @@ -741,6 +740,8 @@ def set_extent(self, extent): xmin, xmax, ymin, ymax = extent corners = (xmin, ymin), (xmax, ymax) self.axes.update_datalim(corners) + self.sticky_edges.x[:] = [xmin, xmax] + self.sticky_edges.y[:] = [ymin, ymax] if self.axes._autoscaleXon: self.axes.set_xlim((xmin, xmax), auto=None) if self.axes._autoscaleYon: diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 4ba04e3717dd..36a75cca3ad8 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -262,8 +262,8 @@ def limit_range_for_scale(self, vmin, vmax, minpos): """ Limit the domain to positive values. """ - return (vmin <= 0.0 and minpos or vmin, - vmax <= 0.0 and minpos or vmax) + return (minpos if vmin <= 0 else vmin, + minpos if vmax <= 0 else vmax) class SymmetricalLogTransform(Transform): @@ -497,8 +497,8 @@ def limit_range_for_scale(self, vmin, vmax, minpos): """ Limit the domain to values between 0 and 1 (excluded). """ - return (vmin <= 0 and minpos or vmin, - vmax >= 1 and (1 - minpos) or vmax) + return (minpos if vmin <= 0 else minpos, + 1 - minpos if vmax >= 1 else vmax) _scale_mapping = { diff --git a/lib/matplotlib/stackplot.py b/lib/matplotlib/stackplot.py index 412d288cd695..26b90ca66643 100644 --- a/lib/matplotlib/stackplot.py +++ b/lib/matplotlib/stackplot.py @@ -70,11 +70,8 @@ def stackplot(axes, x, *args, **kwargs): # Assume data passed has not been 'stacked', so stack it here. stack = np.cumsum(y, axis=0) - r = [] - margins = {} if baseline == 'zero': first_line = 0. - margins['bottom'] = False elif baseline == 'sym': first_line = -np.sum(y, 0) * 0.5 @@ -85,7 +82,6 @@ def stackplot(axes, x, *args, **kwargs): first_line = (y * (m - 0.5 - np.arange(0, m)[:, None])).sum(0) first_line /= -m stack += first_line - margins['bottom'] = False elif baseline == 'weighted_wiggle': m, n = y.shape @@ -104,7 +100,6 @@ def stackplot(axes, x, *args, **kwargs): center = np.cumsum(center.sum(0)) first_line = center - 0.5 * total stack += first_line - margins['bottom'] = False else: errstr = "Baseline method %s not recognised. " % baseline @@ -113,11 +108,11 @@ def stackplot(axes, x, *args, **kwargs): # Color between x = 0 and the first array. color = axes._get_lines.get_next_color() - r.append(axes.fill_between(x, first_line, stack[0, :], - facecolor=color, - label= six.next(labels, None), - margins=margins, - **kwargs)) + coll = axes.fill_between(x, first_line, stack[0, :], + facecolor=color, label=six.next(labels, None), + **kwargs) + coll.sticky_edges.y[:] = [0] + r = [coll] # Color between array i-1 and array i for i in xrange(len(y) - 1): @@ -125,6 +120,5 @@ def stackplot(axes, x, *args, **kwargs): r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :], facecolor=color, label= six.next(labels, None), - margins=margins, **kwargs)) return r diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py index ccf068739cd9..6dc15e95b2ae 100644 --- a/lib/matplotlib/streamplot.py +++ b/lib/matplotlib/streamplot.py @@ -156,10 +156,10 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, # Check if start_points are outside the data boundaries for xs, ys in sp2: - if (xs < grid.x_origin or xs > grid.x_origin + grid.width - or ys < grid.y_origin or ys > grid.y_origin + grid.height): - raise ValueError("Starting point ({}, {}) outside of" - " data boundaries".format(xs, ys)) + if not (grid.x_origin <= xs <= grid.x_origin + grid.width + and grid.y_origin <= ys <= grid.y_origin + grid.height): + raise ValueError("Starting point ({}, {}) outside of data " + "boundaries".format(xs, ys)) # Convert start_points from data to array coords # Shift the seed points from the bottom left of the data so that @@ -210,18 +210,15 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, line_colors.append(color_values) arrow_kw['color'] = cmap(norm(color_values[n])) - p = patches.FancyArrowPatch(arrow_tail, - arrow_head, - transform=transform, - margins=False, - **arrow_kw) + p = patches.FancyArrowPatch( + arrow_tail, arrow_head, transform=transform, **arrow_kw) axes.add_patch(p) arrows.append(p) - lc = mcollections.LineCollection(streamlines, - transform=transform, - margins=False, - **line_kw) + lc = mcollections.LineCollection( + streamlines, transform=transform, **line_kw) + lc.sticky_edges.x[:] = [grid.x_origin, grid.x_origin + grid.width] + lc.sticky_edges.y[:] = [grid.y_origin, grid.y_origin + grid.height] if use_multicolor_lines: lc.set_array(np.ma.hstack(line_colors)) lc.set_cmap(cmap) @@ -229,7 +226,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, axes.add_collection(lc) axes.autoscale_view() - ac = matplotlib.collections.PatchCollection(arrows, margins=False) + ac = matplotlib.collections.PatchCollection(arrows) stream_container = StreamplotSet(lc, ac) return stream_container diff --git a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.pdf b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.pdf index 2963038692f8..95155db23c99 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.pdf and b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.png b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.png index 24c69eb926b5..9a3c3186ea57 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.png and b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.svg b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.svg index 2f1c86fdf19e..64674b5f5bd2 100644 --- a/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.svg +++ b/lib/matplotlib/tests/baseline_images/test_transforms/pre_transform_data.svg @@ -27,452 +27,452 @@ z " style="fill:#ffffff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -487,4328 +487,4328 @@ C -2.000462 -1.161816 -2.236068 -0.593012 -2.236068 0 C -2.236068 0.593012 -2.000462 1.161816 -1.581139 1.581139 C -1.161816 2.000462 -0.593012 2.236068 0 2.236068 z -" id="m8257232a59" style="stroke:#000000;"/> +" id="meff777c5d4" style="stroke:#000000;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4838,20 +4838,20 @@ L 518.4 43.2 +" id="m41b0efa5af" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m80dd548a08" style="stroke:#000000;stroke-width:0.5;"/> - + @@ -4877,7 +4877,7 @@ Q 6.59375 54.828125 13.0625 64.515625 Q 19.53125 74.21875 31.78125 74.21875 " id="DejaVuSans-30"/> - + @@ -4885,12 +4885,12 @@ Q 19.53125 74.21875 31.78125 74.21875 - + - + @@ -4920,7 +4920,7 @@ Q 44.1875 33.984375 37.640625 27.21875 Q 31.109375 20.453125 19.1875 8.296875 " id="DejaVuSans-32"/> - + @@ -4929,12 +4929,12 @@ Q 31.109375 20.453125 19.1875 8.296875 - + - + @@ -4958,7 +4958,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -4967,12 +4967,12 @@ z - + - + @@ -5007,7 +5007,7 @@ Q 40.921875 74.21875 44.703125 73.484375 Q 48.484375 72.75 52.59375 71.296875 " id="DejaVuSans-36"/> - + @@ -5016,12 +5016,12 @@ Q 48.484375 72.75 52.59375 71.296875 - + - + @@ -5064,7 +5064,7 @@ Q 25.390625 66.40625 21.84375 63.234375 Q 18.3125 60.0625 18.3125 54.390625 " id="DejaVuSans-38"/> - + @@ -5073,12 +5073,12 @@ Q 18.3125 60.0625 18.3125 54.390625 - + - + @@ -5098,7 +5098,7 @@ L 12.40625 0 z " id="DejaVuSans-31"/> - + @@ -5112,25 +5112,25 @@ z +" id="m49ab0988c4" style="stroke:#000000;stroke-width:0.5;"/> - + +" id="m67cbd1d5f8" style="stroke:#000000;stroke-width:0.5;"/> - + - + @@ -5138,17 +5138,17 @@ L -4 0 - + - + - + @@ -5157,17 +5157,17 @@ L -4 0 - + - + - + @@ -5176,17 +5176,17 @@ L -4 0 - + - + - + @@ -5195,17 +5195,17 @@ L -4 0 - + - + - + @@ -5214,17 +5214,17 @@ L -4 0 - + - + - + @@ -5235,7 +5235,7 @@ L -4 0 - + diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_contouring.png b/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_contouring.png index 655841671c49..d6d2b993fd80 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_contouring.png and b/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_contouring.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_gradient.png b/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_gradient.png index 071102bcf92d..52b0d21362e4 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_gradient.png and b/lib/matplotlib/tests/baseline_images/test_triangulation/tri_smooth_gradient.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 2524255729f6..c142271d1e42 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -174,6 +174,23 @@ def test_autoscale_tight(): assert_allclose(ax.get_xlim(), (-0.15, 3.15)) assert_allclose(ax.get_ylim(), (1.0, 4.0)) +@cleanup(style='default') +def test_use_sticky_edges(): + fig, ax = plt.subplots() + ax.imshow([[0, 1], [2, 3]], origin='lower') + assert_allclose(ax.get_xlim(), (-0.5, 1.5)) + assert_allclose(ax.get_ylim(), (-0.5, 1.5)) + ax.use_sticky_edges = False + ax.autoscale() + xlim = (-0.5 - 2 * ax._xmargin, 1.5 + 2 * ax._xmargin) + ylim = (-0.5 - 2 * ax._ymargin, 1.5 + 2 * ax._ymargin) + assert_allclose(ax.get_xlim(), xlim) + assert_allclose(ax.get_ylim(), ylim) + # Make sure it is reversible: + ax.use_sticky_edges = True + ax.autoscale() + assert_allclose(ax.get_xlim(), (-0.5, 1.5)) + assert_allclose(ax.get_ylim(), (-0.5, 1.5)) @image_comparison(baseline_images=['offset_points'], remove_text=True) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 49a7d40baffb..9f417f13d47d 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -773,7 +773,7 @@ def z(x, y): @image_comparison(baseline_images=['tri_smooth_gradient'], extensions=['png'], remove_text=True, - tol=0.015 if on_win else 0) + tol=0.03 if on_win else 0) def test_tri_smooth_gradient(): # Image comparison based on example trigradient_demo. @@ -823,6 +823,8 @@ def dipole_potential(x, y): plt.quiver(triang.x, triang.y, Ex/E_norm, Ey/E_norm, units='xy', scale=10., zorder=3, color='blue', width=0.007, headwidth=3., headlength=4.) + # We are leaving ax.use_sticky_margins as True, so the + # view limits are the contour data limits. def test_tritools(): diff --git a/lib/matplotlib/tri/tricontour.py b/lib/matplotlib/tri/tricontour.py index 02fac3822170..a528e13386b0 100644 --- a/lib/matplotlib/tri/tricontour.py +++ b/lib/matplotlib/tri/tricontour.py @@ -50,12 +50,8 @@ def _process_args(self, *args, **kwargs): else: tri, z = self._contour_args(args, kwargs) C = _tri.TriContourGenerator(tri.get_cpp_triangulation(), z) - x0 = tri.x.min() - x1 = tri.x.max() - y0 = tri.y.min() - y1 = tri.y.max() - self.ax.update_datalim([(x0, y0), (x1, y1)]) - self.ax.autoscale_view() + self._mins = [tri.x.min(), tri.y.min()] + self._maxs = [tri.x.max(), tri.y.max()] self.cppContourGenerator = C 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