diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index b1cd08d955a7..16b0377a43bc 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1006,24 +1006,34 @@ def _reset_locator_formatter_scale(self): self.locator = None self.minorlocator = None self.formatter = None - if ((self.spacing == 'uniform') and - ((self.boundaries is not None) or - isinstance(self.norm, colors.BoundaryNorm))): - funcs = (self._forward_boundaries, self._inverse_boundaries) - self.ax.set_xscale('function', functions=funcs) - self.ax.set_yscale('function', functions=funcs) - self.__scale = 'function' - elif hasattr(self.norm, '_scale') and (self.norm._scale is not None): + if (self.boundaries is not None or + isinstance(self.norm, colors.BoundaryNorm)): + if self.spacing == 'uniform': + funcs = (self._forward_boundaries, self._inverse_boundaries) + self.ax.set_xscale('function', functions=funcs) + self.ax.set_yscale('function', functions=funcs) + self.__scale = 'function' + elif self.spacing == 'proportional': + self.__scale = 'linear' + self.ax.set_xscale('linear') + self.ax.set_yscale('linear') + elif hasattr(self.norm, '_scale') and self.norm._scale is not None: + # use the norm's scale: self.ax.set_xscale(self.norm._scale) self.ax.set_yscale(self.norm._scale) self.__scale = self.norm._scale.name - else: + elif type(self.norm) is colors.Normalize: + # plain Normalize: self.ax.set_xscale('linear') self.ax.set_yscale('linear') - if type(self.norm) is colors.Normalize: - self.__scale = 'linear' - else: - self.__scale = 'manual' + self.__scale = 'linear' + else: + # norm._scale is None or not an attr: derive the scale from + # the Norm: + funcs = (self.norm, self.norm.inverse) + self.ax.set_xscale('function', functions=funcs) + self.ax.set_yscale('function', functions=funcs) + self.__scale = 'function' def _locate(self, x): """ diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index f678a4ffefd5..3b7e5988eab7 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1152,7 +1152,7 @@ def __init__(self, vmin=None, vmax=None, clip=False): self.vmin = _sanitize_extrema(vmin) self.vmax = _sanitize_extrema(vmax) self.clip = clip - self._scale = scale.LinearScale(axis=None) + self._scale = None # will default to LinearScale for colorbar @staticmethod def process_value(value): @@ -1334,6 +1334,16 @@ def __call__(self, value, clip=None): result = np.atleast_1d(result)[0] return result + def inverse(self, value): + if not self.scaled(): + raise ValueError("Not invertible until both vmin and vmax are set") + (vmin,), _ = self.process_value(self.vmin) + (vmax,), _ = self.process_value(self.vmax) + (vcenter,), _ = self.process_value(self.vcenter) + + result = np.interp(value, [0, 0.5, 1.], [vmin, vcenter, vmax]) + return result + class CenteredNorm(Normalize): def __init__(self, vcenter=0, halfrange=None, clip=False): diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png new file mode 100644 index 000000000000..27d9227dcfe0 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/colorbar_twoslope.png differ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index ac054755c4c1..6167618575dd 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -771,3 +771,17 @@ def test_inset_colorbar_layout(): np.testing.assert_allclose(cb.ax.get_position().bounds, [0.87, 0.342, 0.0237, 0.315], atol=0.01) assert cb.ax.outer_ax in ax.child_axes + + +@image_comparison(['colorbar_twoslope.png'], remove_text=True, + style='mpl20') +def test_twoslope_colorbar(): + # Note that the first tick = 20, and should be in the middle + # of the colorbar (white) + fig, ax = plt.subplots() + + norm = mcolors.TwoSlopeNorm(20, 0, 100) + pc = ax.pcolormesh(np.arange(1, 11), np.arange(1, 11), + np.arange(100).reshape(10, 10), + norm=norm, cmap='RdBu_r') + fig.colorbar(pc) diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 81dd65bab713..ae004e957591 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -1406,6 +1406,5 @@ def test_norm_deepcopy(): norm = mcolors.Normalize() norm.vmin = 0.0002 norm2 = copy.deepcopy(norm) - assert isinstance(norm2._scale, mscale.LinearScale) + assert norm2._scale is None assert norm2.vmin == norm.vmin - assert norm2._scale is not norm._scale