diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 3c578d431adc..6597d70f8ac7 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -654,6 +654,7 @@ 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') @@ -785,6 +786,7 @@ 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): @@ -1665,6 +1667,13 @@ def axis_date(self, tz=None): tz = pytz.timezone(tz) self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz)) + def get_tick_space(self): + """ + Return the estimated number of ticks that can fit on the axis. + """ + # Must be overridden in the subclass + raise NotImplementedError() + class XAxis(Axis): __name__ = 'xaxis' @@ -1988,6 +1997,18 @@ def set_default_intervals(self): self.axes.viewLim.intervalx = xmin, xmax 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())) + self._tick_space = np.floor(length / size) + return self._tick_space + class YAxis(Axis): __name__ = 'yaxis' @@ -2318,3 +2339,14 @@ def set_default_intervals(self): if not viewMutated: self.axes.viewLim.intervaly = ymin, ymax 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())) + self._tick_space = np.floor(length / size) + return self._tick_space diff --git a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png index 074f1bf404d9..612eb3dd3c48 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png and b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png new file mode 100644 index 000000000000..91d220768457 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3a1a25053da5..b91c6553c348 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4191,6 +4191,12 @@ def test_axes_margins(): assert ax.get_ybound() == (-0.5, 9.5) +@image_comparison(baseline_images=["auto_numticks"], style='default', + extensions=['png']) +def test_auto_numticks(): + fig, axes = plt.subplots(4, 4) + + if __name__ == '__main__': import nose import sys diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index b487671fb5e9..2c7ce1f62b1b 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -202,6 +202,10 @@ def get_data_interval(self): def set_data_interval(self, vmin, vmax): self.dataLim.intervalx = vmin, vmax + def get_tick_space(self): + # Just use the long-standing default of nbins==9 + return 9 + class TickHelper(object): axis = None @@ -1349,7 +1353,9 @@ def __init__(self, *args, **kwargs): Keyword args: *nbins* - Maximum number of intervals; one less than max number of ticks. + Maximum number of intervals; one less than max number of + ticks. If the string `'auto'`, the number of bins will be + automatically determined based on the length of the axis. *steps* Sequence of nice numbers starting with 1 and ending with 10; @@ -1387,7 +1393,9 @@ def __init__(self, *args, **kwargs): def set_params(self, **kwargs): """Set parameters within this locator.""" if 'nbins' in kwargs: - self._nbins = int(kwargs['nbins']) + self._nbins = kwargs['nbins'] + if self._nbins != 'auto': + self._nbins = int(self._nbins) if 'trim' in kwargs: self._trim = kwargs['trim'] if 'integer' in kwargs: @@ -1416,6 +1424,8 @@ def set_params(self, **kwargs): def bin_boundaries(self, vmin, vmax): nbins = self._nbins + if nbins == 'auto': + nbins = self.axis.get_tick_space() scale, offset = scale_range(vmin, vmax, nbins) if self._integer: scale = max(1, scale) @@ -1901,7 +1911,11 @@ def tick_values(self, vmin, vmax): class AutoLocator(MaxNLocator): def __init__(self): - MaxNLocator.__init__(self, nbins=9, steps=[1, 2, 5, 10]) + if rcParams['_internal.classic_mode']: + nbins = 9 + else: + nbins = 'auto' + MaxNLocator.__init__(self, nbins=nbins, steps=[1, 2, 5, 10]) class AutoMinorLocator(Locator): 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