diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index bfc927c7b0cd..34fd38713bec 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -17,6 +17,18 @@ revision, see the :ref:`github-stats`. .. contents:: Table of Contents +.. _whats-new-1-2-2: + +new in matplotlib 1.2.2 +======================= + +Improved collections +-------------------- + +The individual items of a collection may now have different alpha +values and be rendered correctly. This also fixes a bug where +collections were always filled in the PDF backend. + .. _whats-new-1-2: new in matplotlib-1.2 diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 18046336741d..52190fe0295f 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -352,6 +352,8 @@ def _iter_collection(self, gc, master_transform, all_transforms, gc0 = self.new_gc() gc0.copy_properties(gc) + original_alpha = gc.get_alpha() + if Nfacecolors == 0: rgbFace = None @@ -374,22 +376,28 @@ def _iter_collection(self, gc, master_transform, all_transforms, yo = -(yp - yo) if not (np.isfinite(xo) and np.isfinite(yo)): continue + gc0.set_alpha(original_alpha) if Nfacecolors: rgbFace = facecolors[i % Nfacecolors] if Nedgecolors: - fg = edgecolors[i % Nedgecolors] - if Nfacecolors == 0 and len(fg)==4: - gc0.set_alpha(fg[3]) - gc0.set_foreground(fg) if Nlinewidths: gc0.set_linewidth(linewidths[i % Nlinewidths]) if Nlinestyles: gc0.set_dashes(*linestyles[i % Nlinestyles]) - if rgbFace is not None and len(rgbFace)==4: + fg = edgecolors[i % Nedgecolors] + if len(fg) == 4: + if fg[3] == 0.0: + gc0.set_linewidth(0) + else: + gc0.set_alpha(gc0.get_alpha() * fg[3]) + gc0.set_foreground(fg[:3]) + else: + gc0.set_foreground(fg) + if rgbFace is not None and len(rgbFace) == 4: if rgbFace[3] == 0: rgbFace = None else: - gc0.set_alpha(rgbFace[3]) + gc0.set_alpha(gc0.get_alpha() * rgbFace[3]) rgbFace = rgbFace[:3] gc0.set_antialiased(antialiaseds[i % Naa]) if Nurls: diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 4ff617c307f1..6373aca6d2aa 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -1523,10 +1523,36 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms, offsets, offsetTrans, facecolors, edgecolors, linewidths, linestyles, antialiaseds, urls, offset_position): + # We can only reuse the objects if the presence of fill and + # stroke (and the amount of alpha for each) is the same for + # all of them + can_do_optimization = True + + if not len(facecolors): + filled = False + else: + if np.all(facecolors[:, 3] == facecolors[0, 3]): + filled = facecolors[0, 3] != 0.0 + else: + can_do_optimization = False + + if not len(edgecolors): + stroked = False + else: + if np.all(edgecolors[:, 3] == edgecolors[0, 3]): + stroked = edgecolors[0, 3] != 0.0 + else: + can_do_optimization = False + + if not can_do_optimization: + return RendererBase.draw_path_collection( + self, gc, master_transform, paths, all_transforms, + offsets, offsetTrans, facecolors, edgecolors, + linewidths, linestyles, antialiaseds, urls, + offset_position) + padding = np.max(linewidths) path_codes = [] - filled = len(facecolors) - stroked = len(edgecolors) for i, (path, transform) in enumerate(self._iter_collection_raw_paths( master_transform, paths, all_transforms)): name = self.file.pathCollectionObject( diff --git a/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.pdf b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.pdf new file mode 100644 index 000000000000..22879f1e141c Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.png b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.png new file mode 100644 index 000000000000..990e886b6a70 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.svg b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.svg new file mode 100644 index 000000000000..ebddeb511324 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/mixed_collection.svg @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index c54a7a4cc36e..38377fabb06f 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1058,3 +1058,35 @@ def test_eb_line_zorder(): ax.axhline(-j, lw=5, color='k', zorder=j) ax.set_title("errorbar zorder test") + + +@image_comparison(baseline_images=['mixed_collection'], remove_text=True) +def test_mixed_collection(): + from matplotlib import patches + from matplotlib import collections + + x = range(10) + + # First illustrate basic pyplot interface, using defaults where possible. + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + + c = patches.Circle((8, 8), radius=4, facecolor='none', edgecolor='green') + + # PDF can optimize this one + p1 = collections.PatchCollection([c], match_original=True) + p1.set_offsets([[0, 0], [24, 24]]) + p1.set_linewidths([1, 5]) + + # PDF can't optimize this one, because the alpha of the edge changes + p2 = collections.PatchCollection([c], match_original=True) + p2.set_offsets([[48, 0], [-32, -16]]) + p2.set_linewidths([1, 5]) + p2.set_edgecolors([[0, 0, 0.1, 1.0], [0, 0, 0.1, 0.5]]) + + ax.patch.set_color('0.5') + ax.add_collection(p1) + ax.add_collection(p2) + + ax.set_xlim(0, 16) + ax.set_ylim(0, 16)