diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index c8f49c0ad92d..996949098f99 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -606,10 +606,13 @@ def _update_dividers(self): self.dividers.set_segments(segments) def _add_solids_patches(self, X, Y, C, mappable): - hatches = mappable.hatches * len(C) # Have enough hatches. + hatches = mappable.hatches * (len(C) + 1) # Have enough hatches. + if self._extend_lower(): + # remove first hatch that goes into the extend patch + hatches = hatches[1:] patches = [] for i in range(len(X) - 1): - xy = np.array([[X[i, 0], Y[i, 0]], + xy = np.array([[X[i, 0], Y[i, 1]], [X[i, 1], Y[i, 0]], [X[i + 1, 1], Y[i + 1, 0]], [X[i + 1, 0], Y[i + 1, 1]]]) @@ -661,9 +664,9 @@ def _do_extends(self, ax=None): mappable = getattr(self, 'mappable', None) if (isinstance(mappable, contour.ContourSet) and any(hatch is not None for hatch in mappable.hatches)): - hatches = mappable.hatches + hatches = mappable.hatches * (len(self._y) + 1) else: - hatches = [None] + hatches = [None] * (len(self._y) + 1) if self._extend_lower(): if not self.extendrect: @@ -687,6 +690,8 @@ def _do_extends(self, ax=None): zorder=np.nextafter(self.ax.patch.zorder, -np.inf)) self.ax.add_patch(patch) self._extend_patches.append(patch) + # remove first hatch that goes into the extend patch + hatches = hatches[1:] if self._extend_upper(): if not self.extendrect: # triangle @@ -699,10 +704,12 @@ def _do_extends(self, ax=None): # add the patch val = 0 if self._long_axis().get_inverted() else -1 color = self.cmap(self.norm(self._values[val])) + hatch_idx = len(self._y) - 1 patch = mpatches.PathPatch( mpath.Path(xy), facecolor=color, alpha=self.alpha, linewidth=0, antialiased=False, - transform=self.ax.transAxes, hatch=hatches[-1], clip_on=False, + transform=self.ax.transAxes, hatch=hatches[hatch_idx], + clip_on=False, # Place it right behind the standard patches, which is # needed if we updated the extends zorder=np.nextafter(self.ax.patch.zorder, -np.inf)) diff --git a/lib/matplotlib/tests/baseline_images/test_colorbar/contourf_extend_patches.png b/lib/matplotlib/tests/baseline_images/test_colorbar/contourf_extend_patches.png new file mode 100644 index 000000000000..0e5ef52cf549 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_colorbar/contourf_extend_patches.png differ diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 149ed4c3d22e..ee10c5b6befc 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -998,6 +998,36 @@ def test_colorbar_extend_drawedges(): np.testing.assert_array_equal(cbar.dividers.get_segments(), res) +@image_comparison(['contourf_extend_patches.png'], remove_text=True, + style='mpl20') +def test_colorbar_contourf_extend_patches(): + params = [ + ('both', 5, ['\\', '//']), + ('min', 7, ['+']), + ('max', 2, ['|', '-', '/', '\\', '//']), + ('neither', 10, ['//', '\\', '||']), + ] + + plt.rcParams['axes.linewidth'] = 2 + + fig = plt.figure(figsize=(10, 4)) + subfigs = fig.subfigures(1, 2) + fig.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.95) + + x = np.linspace(-2, 3, 50) + y = np.linspace(-2, 3, 30) + z = np.cos(x[np.newaxis, :]) + np.sin(y[:, np.newaxis]) + + cmap = mpl.colormaps["viridis"] + for orientation, subfig in zip(['horizontal', 'vertical'], subfigs): + axs = subfig.subplots(2, 2).ravel() + for ax, (extend, levels, hatches) in zip(axs, params): + cs = ax.contourf(x, y, z, levels, hatches=hatches, + cmap=cmap, extend=extend) + subfig.colorbar(cs, ax=ax, orientation=orientation, fraction=0.4, + extendfrac=0.2, aspect=5) + + def test_negative_boundarynorm(): fig, ax = plt.subplots(figsize=(1, 3)) cmap = mpl.colormaps["viridis"]