From 80f868aa33b33c2a9d204c073c1feec6190f125b Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sun, 13 Nov 2016 20:21:58 -1000 Subject: [PATCH 1/3] BUG: make autoscale('tight') set margins to zero; closes #6968 --- lib/matplotlib/axes/_base.py | 108 ++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index edddd7a7f8a5..95a052461ac4 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2185,6 +2185,10 @@ def autoscale(self, enable=True, axis='both', tight=None): if axis in ['y', 'both']: self._autoscaleYon = bool(enable) scaley = self._autoscaleYon + if tight and scalex: + self._xmargin = 0 + if tight and scaley: + self._ymargin = 0 self.autoscale_view(tight=tight, scalex=scalex, scaley=scaley) def autoscale_view(self, tight=None, scalex=True, scaley=True): @@ -2194,6 +2198,14 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): setting *scaley* to *False*. The autoscaling preserves any axis direction reversal that has already been done. + If *tight* is *False*, the axis major locator will be used + to expand the view limits if rcParams['axes.autolimit_mode'] + is 'round_numbers'. Note that any margins that are in effect + will be applied first, regardless of whether *tight* is + *True* or *False*. Specifying *tight* as *True* or *False* + saves the setting as a private attribute of the Axes; specifying + it as *None* (the default) applies the previously saved value. + The data limits are not updated automatically when artist data are changed after the artist has been added to an Axes instance. In that case, use :meth:`matplotlib.axes.Axes.relim` prior to calling @@ -2246,52 +2258,56 @@ def autoscale_view(self, tight=None, scalex=True, scaley=True): def handle_single_axis(scale, autoscaleon, shared_axes, interval, minpos, axis, margin, do_lower_margin, do_upper_margin, set_bound): - if scale and autoscaleon: - shared = shared_axes.get_siblings(self) - dl = [ax.dataLim for ax in shared] - # ignore non-finite data limits if good limits exist - finite_dl = [d for d in dl if np.isfinite(d).all()] - if len(finite_dl): - dl = finite_dl - - bb = mtransforms.BboxBase.union(dl) - x0, x1 = getattr(bb, interval) - locator = axis.get_major_locator() - try: - # e.g., DateLocator has its own nonsingular() - x0, x1 = locator.nonsingular(x0, x1) - except AttributeError: - # Default nonsingular for, e.g., MaxNLocator - 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: - x0, x1 = locator.view_limits(x0, x1) - set_bound(x0, x1) + + if not (scale and autoscaleon): + return # nothing to do... + + shared = shared_axes.get_siblings(self) + dl = [ax.dataLim for ax in shared] + # ignore non-finite data limits if good limits exist + finite_dl = [d for d in dl if np.isfinite(d).all()] + if len(finite_dl): + dl = finite_dl + + bb = mtransforms.BboxBase.union(dl) + x0, x1 = getattr(bb, interval) + locator = axis.get_major_locator() + try: + # e.g., DateLocator has its own nonsingular() + x0, x1 = locator.nonsingular(x0, x1) + except AttributeError: + # Default nonsingular for, e.g., MaxNLocator + 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: + 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, From fe34ad20fc3013c4991028a353d16aecb3342557 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Sun, 4 Sep 2016 21:54:51 +0100 Subject: [PATCH 2/3] Add test for tight=True in autoscale --- lib/matplotlib/tests/test_axes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 29ed50eaf6e3..a475cfb9edf1 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -167,6 +167,16 @@ def test_autoscale_tiny_range(): ax[i].plot([0, 1], [1, 1 + y1]) +@cleanup +def test_autoscale_tight(): + fig, ax = plt.subplots(1, 1) + ax.plot([1, 2, 3, 4]) + ax.autoscale(enable=True, axis='x', tight=False) + ax.autoscale(enable=True, axis='y', tight=True) + assert_allclose(ax.get_xlim(), (-0.15, 3.15)) + assert_allclose(ax.get_ylim(), (1.0, 4.0)) + + @image_comparison(baseline_images=['offset_points'], remove_text=True) def test_basic_annotate(): From c4e3426a33b97e0b865594575ac10ac6a07b00c8 Mon Sep 17 00:00:00 2001 From: Eric Firing Date: Sun, 13 Nov 2016 22:01:15 -1000 Subject: [PATCH 3/3] Make test use default style --- lib/matplotlib/tests/test_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index a475cfb9edf1..0bcbaa492b45 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -167,7 +167,7 @@ def test_autoscale_tiny_range(): ax[i].plot([0, 1], [1, 1 + y1]) -@cleanup +@cleanup(style='default') def test_autoscale_tight(): fig, ax = plt.subplots(1, 1) ax.plot([1, 2, 3, 4])