From f257244dbf70d7b0119d94a65caf441855850837 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 31 Dec 2015 14:20:02 -0500 Subject: [PATCH 1/5] TST: test that number of ticks adjust with aspect --- lib/matplotlib/tests/test_axes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index a41af8d5b50d..780584d2e634 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4276,6 +4276,19 @@ def _helper_y(ax): assert assert_array_equal(ax_lst[0][1].get_xlim(), orig_xlim) +@cleanup +def test_adjust_numtick_aspect(): + fig, ax = plt.subplots() + ax.yaxis.get_major_locator().set_params(nbins='auto') + ax.set_xlim(0, 1000) + ax.set_aspect('equal') + fig.canvas.draw() + assert len(ax.yaxis.get_major_locator()()) == 2 + ax.set_ylim(0, 1000) + fig.canvas.draw() + assert len(ax.yaxis.get_major_locator()()) > 2 + + @image_comparison(baseline_images=["auto_numticks"], style='default', extensions=['png']) def test_auto_numticks(): From cefba05cc88e441e142a417f9993e4ceaf8095bc Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 31 Dec 2015 11:14:40 -0500 Subject: [PATCH 2/5] FIX: always use at least 2 ticks and recompute For extreme aspect-ratio plots the auto ntick logic would decide that no ticks will fit, leading to divide by 0 issue. - In ticker ensure there is always at least on bin - Axis do not cache the number of ticks so that if the on-screen aspect ratio changes the number of ticks will update correctly. --- lib/matplotlib/axis.py | 4 ++-- lib/matplotlib/ticker.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 3c8967ef676f..df3163260f61 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -2004,7 +2004,7 @@ def get_tick_space(self): # is no more than 3:1 size = tick.label1.get_size() * 3 size *= np.cos(np.deg2rad(tick.label1.get_rotation())) - self._tick_space = np.floor(length / size) + return np.floor(length / size) return self._tick_space @@ -2346,5 +2346,5 @@ def get_tick_space(self): # Having a spacing of at least 2 just looks good. size = tick.label1.get_size() * 2.0 size *= np.cos(np.deg2rad(tick.label1.get_rotation())) - self._tick_space = np.floor(length / size) + return np.floor(length / size) return self._tick_space diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index b6641be13c4f..2d416bbac431 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1434,7 +1434,7 @@ def set_params(self, **kwargs): def bin_boundaries(self, vmin, vmax): nbins = self._nbins if nbins == 'auto': - nbins = min(self.axis.get_tick_space(), 9) + nbins = max(min(self.axis.get_tick_space(), 9), 1) scale, offset = scale_range(vmin, vmax, nbins) if self._integer: scale = max(1, scale) From aac8d6efe6234e37c3fad125e489198bec125e01 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 31 Dec 2015 16:45:53 -0500 Subject: [PATCH 3/5] TST: hard-code nbins Do not let the ticks in the inset adjust based on spacing of text which is not visible. The issue with the inset locator seems to be that as previously implemented the axis's estimate of how many ticks it should have was computed once and then saved. The way that the inset axes works (if you step through this line at a time) is that the figure-space size of the axes follows the x/y limits at a fixed ratio to the screen-space of the same range in the main axes. Thus, when the inset axes is created it is 'big' in that it has limits of [0, 1] at 6x zoom -> about half the size of the host axes and so it concludes that it needs a whole bunch of ticks and remembers that. One of the changes in this PR makes it so that the number of ticks is recomputed every time, thus many fewer ticks are used when the axes is small (even though the text is all turned off). --- lib/mpl_toolkits/tests/test_axes_grid1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mpl_toolkits/tests/test_axes_grid1.py b/lib/mpl_toolkits/tests/test_axes_grid1.py index 92c7f3f467d3..c20826604b35 100644 --- a/lib/mpl_toolkits/tests/test_axes_grid1.py +++ b/lib/mpl_toolkits/tests/test_axes_grid1.py @@ -101,7 +101,8 @@ def get_demo_image(): axins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6 axins.imshow(Z2, extent=extent, interpolation="nearest", origin="lower") - + axins.yaxis.get_major_locator().set_params(nbins=7) + axins.xaxis.get_major_locator().set_params(nbins=7) # sub region of the original image x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 axins.set_xlim(x1, x2) From 57623d67b9c1caf8dd768ec9cbbc933ec58f9726 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 16 Jan 2016 17:08:06 -0500 Subject: [PATCH 4/5] MNT: remove caching of _tick_space --- lib/matplotlib/axis.py | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index df3163260f61..b166bcc741f4 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -664,7 +664,6 @@ def __init__(self, axes, pickradius=15): # Initialize here for testing; later add API self._major_tick_kw = dict() self._minor_tick_kw = dict() - self._tick_space = None self.cla() self._set_scale('linear') @@ -796,7 +795,6 @@ def set_tick_params(self, which='major', reset=False, **kw): for tick in self.minorTicks: tick._apply_params(**self._minor_tick_kw) self.stale = True - self._tick_space = None @staticmethod def _translate_tick_kw(kw, to_init_kw=True): @@ -1996,16 +1994,14 @@ def set_default_intervals(self): self.stale = True def get_tick_space(self): - if self._tick_space is None: - ends = self.axes.transAxes.transform([[0, 0], [1, 0]]) - length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72.0 - tick = self._get_tick(True) - # There is a heuristic here that the aspect ratio of tick text - # is no more than 3:1 - size = tick.label1.get_size() * 3 - size *= np.cos(np.deg2rad(tick.label1.get_rotation())) - return np.floor(length / size) - return self._tick_space + ends = self.axes.transAxes.transform([[0, 0], [1, 0]]) + length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72.0 + tick = self._get_tick(True) + # There is a heuristic here that the aspect ratio of tick text + # is no more than 3:1 + size = tick.label1.get_size() * 3 + size *= np.cos(np.deg2rad(tick.label1.get_rotation())) + return np.floor(length / size) class YAxis(Axis): @@ -2339,12 +2335,10 @@ def set_default_intervals(self): self.stale = True def get_tick_space(self): - if self._tick_space is None: - ends = self.axes.transAxes.transform([[0, 0], [0, 1]]) - length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72.0 - tick = self._get_tick(True) - # Having a spacing of at least 2 just looks good. - size = tick.label1.get_size() * 2.0 - size *= np.cos(np.deg2rad(tick.label1.get_rotation())) - return np.floor(length / size) - return self._tick_space + ends = self.axes.transAxes.transform([[0, 0], [0, 1]]) + length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72.0 + tick = self._get_tick(True) + # Having a spacing of at least 2 just looks good. + size = tick.label1.get_size() * 2.0 + size *= np.cos(np.deg2rad(tick.label1.get_rotation())) + return np.floor(length / size) From bc359bed849f7f617c045830ce7bfbdf7ad0c909 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 24 Jan 2016 14:58:30 -0500 Subject: [PATCH 5/5] DOC: modify examples to fix number of ticks In the inset axes (which will not have tick labels) fix the number of ticks instead of letting auto-ticker pick the number of ticks. --- examples/axes_grid/inset_locator_demo.py | 3 +++ examples/axes_grid/inset_locator_demo2.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples/axes_grid/inset_locator_demo.py b/examples/axes_grid/inset_locator_demo.py index 3fc9e975e23a..6b7582b3f5bc 100644 --- a/examples/axes_grid/inset_locator_demo.py +++ b/examples/axes_grid/inset_locator_demo.py @@ -32,6 +32,9 @@ def add_sizebar(ax, size): ax2.set_aspect(1.) axins = zoomed_inset_axes(ax2, 0.5, loc=1) # zoom = 0.5 +# fix the number of ticks on the inset axes +axins.yaxis.get_major_locator().set_params(nbins=7) +axins.xaxis.get_major_locator().set_params(nbins=7) plt.xticks(visible=False) plt.yticks(visible=False) diff --git a/examples/axes_grid/inset_locator_demo2.py b/examples/axes_grid/inset_locator_demo2.py index 97e420694b64..51374cbbc7c8 100644 --- a/examples/axes_grid/inset_locator_demo2.py +++ b/examples/axes_grid/inset_locator_demo2.py @@ -34,6 +34,9 @@ def get_demo_image(): x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9 axins.set_xlim(x1, x2) axins.set_ylim(y1, y2) +# fix the number of ticks on the inset axes +axins.yaxis.get_major_locator().set_params(nbins=7) +axins.xaxis.get_major_locator().set_params(nbins=7) plt.xticks(visible=False) plt.yticks(visible=False)