diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 59deeaef892a..f077bee87ef0 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -14,6 +14,7 @@ import warnings +@cleanup(style='classic') def test_MaxNLocator(): loc = mticker.MaxNLocator(nbins=5) test_value = np.array([20., 40., 60., 80., 100.]) @@ -26,6 +27,16 @@ def test_MaxNLocator(): assert_almost_equal(loc.tick_values(-1e15, 1e15), test_value) +@cleanup +def test_MaxNLocator_integer(): + loc = mticker.MaxNLocator(nbins=5, integer=True) + test_value = np.array([-1, 0, 1, 2]) + assert_almost_equal(loc.tick_values(-0.1, 1.1), test_value) + + test_value = np.array([-0.25, 0, 0.25, 0.5, 0.75, 1]) + assert_almost_equal(loc.tick_values(-0.1, 0.95), test_value) + + def test_LinearLocator(): loc = mticker.LinearLocator(numticks=3) test_value = np.array([-0.8, -0.3, 0.2]) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index a2be7cbd805e..ac90453832dd 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1603,7 +1603,9 @@ def __init__(self, *args, **kwargs): e.g., [1, 2, 4, 5, 10] *integer* - If True, ticks will take only integer values. + If True, ticks will take only integer values, provided + at least `min_n_ticks` integers are found within the + view limits. *symmetric* If True, autoscaling will result in a range symmetric @@ -1613,16 +1615,16 @@ def __init__(self, *args, **kwargs): ['lower' | 'upper' | 'both' | None] Remove edge ticks -- useful for stacked or ganged plots where the upper tick of one axes overlaps with the lower - tick of the axes above it. - If prune=='lower', the smallest tick will - be removed. If prune=='upper', the largest tick will be - removed. If prune=='both', the largest and smallest ticks - will be removed. If prune==None, no ticks will be removed. + tick of the axes above it, primarily when + `rcParams['axes.autolimit_mode']` is `'round_numbers'`. + If `prune=='lower'`, the smallest tick will + be removed. If `prune=='upper'`, the largest tick will be + removed. If `prune=='both'`, the largest and smallest ticks + will be removed. If `prune==None`, no ticks will be removed. *min_n_ticks* - While the estimated number of ticks is less than the minimum, - the target value *nbins* is incremented and the ticks are - recalculated. + Relax `nbins` and `integer` constraints if necessary to + obtain this minimum number of ticks. """ if args: @@ -1643,8 +1645,6 @@ def set_params(self, **kwargs): warnings.warn( "The 'trim' keyword has no effect since version 2.0.", mplDeprecation) - if 'integer' in kwargs: - self._integer = kwargs['integer'] if 'symmetric' in kwargs: self._symmetric = kwargs['symmetric'] if 'prune' in kwargs: @@ -1662,6 +1662,13 @@ def set_params(self, **kwargs): steps = list(steps) steps.append(10) self._steps = steps + # Make an extended staircase within which the needed + # step will be found. This is probably much larger + # than necessary. + flights = (0.1 * np.array(self._steps[:-1]), + self._steps, + [10 * self._steps[1]]) + self._extended_steps = np.hstack(flights) if 'integer' in kwargs: self._integer = kwargs['integer'] if self._integer: @@ -1676,42 +1683,38 @@ def _raw_ticks(self, vmin, vmax): else: nbins = self._nbins - while True: - ticks = self._try_raw_ticks(vmin, vmax, nbins) + scale, offset = scale_range(vmin, vmax, nbins) + _vmin = vmin - offset + _vmax = vmax - offset + raw_step = (vmax - vmin) / nbins + steps = self._extended_steps * scale + istep = np.nonzero(steps >= raw_step)[0][0] + + # Classic round_numbers mode may require a larger step. + if rcParams['axes.autolimit_mode'] == 'round_numbers': + for istep in range(istep, len(steps)): + step = steps[istep] + best_vmin = (_vmin // step) * step + best_vmax = best_vmin + step * nbins + if (best_vmax >= _vmax): + break + + # This is an upper limit; move to smaller steps if necessary. + for i in range(istep): + step = steps[istep - i] + if (self._integer and + np.floor(_vmax) - np.ceil(_vmin) >= self._min_n_ticks - 1): + step = max(1, step) + best_vmin = (_vmin // step) * step + + low = round(Base(step).le(_vmin - best_vmin) / step) + high = round(Base(step).ge(_vmax - best_vmin) / step) + ticks = np.arange(low, high + 1) * step + best_vmin + offset nticks = ((ticks <= vmax) & (ticks >= vmin)).sum() if nticks >= self._min_n_ticks: break - nbins += 1 - - self._nbins_used = nbins # Maybe useful for troubleshooting. return ticks - def _try_raw_ticks(self, vmin, vmax, nbins): - scale, offset = scale_range(vmin, vmax, nbins) - if self._integer: - scale = max(1, scale) - vmin = vmin - offset - vmax = vmax - offset - raw_step = (vmax - vmin) / nbins - scaled_raw_step = raw_step / scale - best_vmax = vmax - best_vmin = vmin - - steps = (x for x in self._steps if x >= scaled_raw_step) - for step in steps: - step *= scale - best_vmin = vmin // step * step - best_vmax = best_vmin + step * nbins - if best_vmax >= vmax: - break - - # More than nbins may be required, e.g. vmin, vmax = -4.1, 4.1 gives - # nbins=9 but 10 bins are actually required after rounding. So we just - # create the bins that span the range we need instead. - low = round(Base(step).le(vmin - best_vmin) / step) - high = round(Base(step).ge(vmax - best_vmin) / step) - return np.arange(low, high + 1) * step + best_vmin + offset - @cbook.deprecated("2.0") def bin_boundaries(self, vmin, vmax): return self._raw_ticks(vmin, vmax) diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.pdf b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.pdf index 23e65aa30eec..7d5b0f07fdd4 100644 Binary files a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.pdf and b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.pdf differ diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.png b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.png index 036a4bd06d64..7575e918e92c 100644 Binary files a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.png and b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.png differ diff --git a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.svg b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.svg index 07a239528807..bf248995de4f 100644 --- a/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.svg +++ b/lib/mpl_toolkits/tests/baseline_images/test_mplot3d/lines3d.svg @@ -10,448 +10,470 @@ - - - - - - - - - - - + + - - - - - + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - + - - + +