diff --git a/doc/api/next_api_changes/2018-01-30-AL.rst b/doc/api/next_api_changes/2018-01-30-AL.rst new file mode 100644 index 000000000000..512cdbfbc764 --- /dev/null +++ b/doc/api/next_api_changes/2018-01-30-AL.rst @@ -0,0 +1,10 @@ +Minor Locator no longer try to avoid overstriking major Locators +```````````````````````````````````````````````````````````````` + +Previously, certain locator classes (`LogLocator`, `AutoMinorLocator`) +contained custom logic to avoid emitting tick locations that collided with +major ticks when they were used as minor locators. + +This logic has now moved to the Axis class; thus, for example, +``xaxis.minor.locator()`` now includes positions that collide with +``xaxis.major.locator()``, but ``xaxis.get_minorticklocs()`` does not. diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index e8c60ee99573..3fb94e13ae14 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1013,17 +1013,15 @@ def _set_artist_props(self, a): def iter_ticks(self): """ - Iterate through all of the major and minor ticks. + Yield ``(Tick, location, label)`` tuples for major and minor ticks. """ - major_locs = self.major.locator() - major_ticks = self.get_major_ticks(len(major_locs)) + major_locs = self.get_majorticklocs() major_labels = self.major.formatter.format_ticks(major_locs) - - minor_locs = self.minor.locator() - minor_ticks = self.get_minor_ticks(len(minor_locs)) - minor_labels = self.minor.formatter.format_ticks(minor_locs) - + major_ticks = self.get_major_ticks(len(major_locs)) yield from zip(major_ticks, major_locs, major_labels) + minor_locs = self.get_minorticklocs() + minor_labels = self.minor.formatter.format_ticks(minor_locs) + minor_ticks = self.get_minor_ticks(len(minor_locs)) yield from zip(minor_ticks, minor_locs, minor_labels) def get_ticklabel_extents(self, renderer): @@ -1304,18 +1302,29 @@ def get_ticklines(self, minor=False): return self.get_majorticklines() def get_majorticklocs(self): - "Get the major tick locations in data coordinates as a numpy array" + """Get the array of major tick locations in data coordinates.""" return self.major.locator() def get_minorticklocs(self): - "Get the minor tick locations in data coordinates as a numpy array" - return self.minor.locator() + """Get the array of minor tick locations in data coordinates.""" + # Remove minor ticks duplicating major ticks. + major_locs = self.major.locator() + minor_locs = self.minor.locator() + transform = self._scale.get_transform() + tr_minor_locs = transform.transform(minor_locs) + tr_major_locs = transform.transform(major_locs) + lo, hi = sorted(transform.transform(self.get_view_interval())) + # Use the transformed view limits as scale. 1e-5 is the default rtol + # for np.isclose. + tol = (hi - lo) * 1e-5 + minor_locs = [ + loc for loc, tr_loc in zip(minor_locs, tr_minor_locs) + if not np.isclose(tr_loc, tr_major_locs, atol=tol, rtol=0).any()] + return minor_locs def get_ticklocs(self, minor=False): - "Get the tick locations in data coordinates as a numpy array" - if minor: - return self.minor.locator() - return self.major.locator() + """Get the array of tick locations in data coordinates.""" + return self.get_minorticklocs() if minor else self.get_majorticklocs() def get_ticks_direction(self, minor=False): """ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png b/lib/matplotlib/tests/baseline_images/test_axes/symlog.png deleted file mode 100644 index 4c20548006d3..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg b/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg deleted file mode 100644 index 170ec4464325..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg +++ /dev/null @@ -1,679 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog2.png b/lib/matplotlib/tests/baseline_images/test_axes/symlog2.png deleted file mode 100644 index 44e5cfa3025e..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog2.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog2.svg b/lib/matplotlib/tests/baseline_images/test_axes/symlog2.svg deleted file mode 100644 index 977f6ccdc084..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_axes/symlog2.svg +++ /dev/null @@ -1,2143 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.pdf b/lib/matplotlib/tests/baseline_images/test_scale/log_scales.pdf deleted file mode 100644 index 182416404c08..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.png b/lib/matplotlib/tests/baseline_images/test_scale/log_scales.png deleted file mode 100644 index 404023bce904..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.svg b/lib/matplotlib/tests/baseline_images/test_scale/log_scales.svg deleted file mode 100644 index 52dd71dc5d8b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_scale/log_scales.svg +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.pdf b/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.pdf deleted file mode 100644 index 664c30a7b78e..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.pdf and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.png b/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.png deleted file mode 100644 index c34dc0bb9403..000000000000 Binary files a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.png and /dev/null differ diff --git a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.svg b/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.svg deleted file mode 100644 index 39c20581360b..000000000000 --- a/lib/matplotlib/tests/baseline_images/test_tightlayout/outward_ticks.svg +++ /dev/null @@ -1,1345 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index f9519f8074cc..1c657c4832a0 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1064,7 +1064,10 @@ def test_fill_between_interpolate_decreasing(): ax.set_ylim(800, 600) -@image_comparison(baseline_images=['symlog']) +# test_symlog and test_symlog2 used to have baseline images in all three +# formats, but the png and svg baselines got invalidated by the removal of +# minor tick overstriking. +@image_comparison(baseline_images=['symlog'], extensions=['pdf']) def test_symlog(): x = np.array([0, 1, 2, 4, 6, 9, 12, 24]) y = np.array([1000000, 500000, 100000, 100, 5, 0, 0, 0]) @@ -1076,7 +1079,7 @@ def test_symlog(): ax.set_ylim(-1, 10000000) -@image_comparison(baseline_images=['symlog2'], +@image_comparison(baseline_images=['symlog2'], extensions=['pdf'], remove_text=True) def test_symlog2(): # Numbers from -50 to 50, with 0.1 as step diff --git a/lib/matplotlib/tests/test_scale.py b/lib/matplotlib/tests/test_scale.py index 26822a7adc69..75a61c310c1d 100644 --- a/lib/matplotlib/tests/test_scale.py +++ b/lib/matplotlib/tests/test_scale.py @@ -1,4 +1,4 @@ -from matplotlib.testing.decorators import image_comparison +from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib.pyplot as plt from matplotlib.scale import Log10Transform, InvertedLog10Transform @@ -8,12 +8,17 @@ import pytest -@image_comparison(baseline_images=['log_scales'], remove_text=True) -def test_log_scales(): - ax = plt.figure().add_subplot(122, yscale='log', xscale='symlog') - - ax.axvline(24.1) - ax.axhline(24.1) +@check_figures_equal() +def test_log_scales(fig_test, fig_ref): + ax_test = fig_test.add_subplot(122, yscale='log', xscale='symlog') + ax_test.axvline(24.1) + ax_test.axhline(24.1) + xlim = ax_test.get_xlim() + ylim = ax_test.get_ylim() + ax_ref = fig_ref.add_subplot(122, yscale='log', xscale='symlog') + ax_ref.set(xlim=xlim, ylim=ylim) + ax_ref.plot([24.1, 24.1], ylim, 'b') + ax_ref.plot(xlim, [24.1, 24.1], 'b') @image_comparison(baseline_images=['logit_scales'], remove_text=True, diff --git a/lib/matplotlib/tests/test_tightlayout.py b/lib/matplotlib/tests/test_tightlayout.py index 5898c1a94aeb..bae8951f8cd3 100644 --- a/lib/matplotlib/tests/test_tightlayout.py +++ b/lib/matplotlib/tests/test_tightlayout.py @@ -1,6 +1,7 @@ import warnings import numpy as np +from numpy.testing import assert_array_equal from matplotlib.testing.decorators import image_comparison import matplotlib.pyplot as plt @@ -161,8 +162,6 @@ def test_tight_layout9(): plt.tight_layout() -# The following test is misleading when the text is removed. -@image_comparison(baseline_images=['outward_ticks'], remove_text=False) def test_outward_ticks(): 'Test automatic use of tight_layout' fig = plt.figure() @@ -173,8 +172,6 @@ def test_outward_ticks(): tickdir='out', length=32, width=3, tick1On=True, which='minor') ax.yaxis.set_tick_params( tickdir='out', length=32, width=3, tick1On=True, which='minor') - # The following minor ticks are not labelled, and they - # are drawn over the major ticks and labels--ugly! ax.xaxis.set_ticks([0], minor=True) ax.yaxis.set_ticks([0], minor=True) ax = fig.add_subplot(222) @@ -187,6 +184,14 @@ def test_outward_ticks(): ax.xaxis.set_tick_params(tickdir='out', length=32, width=3) ax.yaxis.set_tick_params(tickdir='out', length=32, width=3) plt.tight_layout() + assert_array_equal( + np.round([ax.get_position().get_points() for ax in fig.axes], 3), + # These values were obtained after visual checking that they correspond + # to a tight layouting that did take the ticks into account. + [[[0.091, 0.590], [0.437, 0.903]], + [[0.581, 0.590], [0.927, 0.903]], + [[0.091, 0.140], [0.437, 0.454]], + [[0.581, 0.140], [0.927, 0.454]]]) def add_offsetboxes(ax, size=10, margin=.1, color='black'): diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 290ce7a5f65c..90030a3415a7 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -2257,13 +2257,9 @@ def tick_values(self, vmin, vmax): # If we're a minor locator *that expects at least two ticks per # decade* and the major locator stride is 1 and there's no more # than one minor tick, switch to AutoLocator. - ticklocs = AutoLocator().tick_values(vmin, vmax) - # Don't overstrike the major labels. Assumes major locs are - # at b = self._base - ticklocs = ticklocs[ - ~is_close_to_int(np.log(ticklocs) / np.log(b))] - return ticklocs - return self.raise_if_exceeds(ticklocs) + return AutoLocator().tick_values(vmin, vmax) + else: + return self.raise_if_exceeds(ticklocs) def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' @@ -2644,10 +2640,6 @@ def __call__(self): tmin = ((vmin - t0) // minorstep + 1) * minorstep tmax = ((vmax - t0) // minorstep + 1) * minorstep locs = np.arange(tmin, tmax, minorstep) + t0 - mod = np.abs((locs - t0) % majorstep) - cond1 = mod > minorstep / 10.0 - cond2 = ~np.isclose(mod, majorstep, atol=0) - locs = locs[cond1 & cond2] return self.raise_if_exceeds(locs)