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