From 43f16a010b550bbd6fcb53776e25c66430a63512 Mon Sep 17 00:00:00 2001 From: hhalwan Date: Sat, 9 Apr 2022 14:48:37 -0400 Subject: [PATCH 1/3] Made it so that PatchCollection uses the hatch of the patch in the collection. PatchCollection will currently ignore hatches of the provided patches, only allowing a hatch passed as a keyword into the constructor. If match_original is true, we now keep the hatch of the first patch provided in the collection. Additionally, the hatchcolor is determined by the edge color, so we will only pass the edgecolors of the patches to the Collection if the edgecolor is not blank - its default color of [0, 0, 0, 0]. This ensures that the hatch will use its default of black in the case where no edgecolor of the patches was ever set. --- lib/matplotlib/collections.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index d0df32638be8..24f2e7e8895a 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1808,9 +1808,10 @@ def __init__(self, patches, match_original=False, **kwargs): *match_original* If True, use the colors and linewidths of the original - patches. If False, new colors may be assigned by - providing the standard collection arguments, facecolor, - edgecolor, linewidths, norm or cmap. + patches. Also use the hatch of the first patch provided. + If False, new colors may be assigned by providing the + standard collection arguments, facecolor, edgecolor, + linewidths, norm or cmap. If any of *edgecolors*, *facecolors*, *linewidths*, *antialiaseds* are None, they default to their `.rcParams` patch setting, in sequence @@ -1827,12 +1828,20 @@ def determine_facecolor(patch): if patch.get_fill(): return patch.get_facecolor() return [0, 0, 0, 0] - + def is_default(c): + return mcolors.same_color([0, 0, 0, 0], c) kwargs['facecolors'] = [determine_facecolor(p) for p in patches] - kwargs['edgecolors'] = [p.get_edgecolor() for p in patches] + edgecolors = [p.get_edgecolor() for p in patches] + # Since the hatch color is updated when the edge color + # is updated, we will ignore the edge color if it is blank (the + # default of [0, 0, 0, 0]). + if not all(is_default(c) for c in edgecolors): + kwargs['edgecolors'] = edgecolors kwargs['linewidths'] = [p.get_linewidth() for p in patches] kwargs['linestyles'] = [p.get_linestyle() for p in patches] kwargs['antialiaseds'] = [p.get_antialiased() for p in patches] + if patches: + kwargs['hatch'] = patches[0].get_hatch() super().__init__(**kwargs) From b7e9588a7794b2518b61ab8a5ed963e1547495c8 Mon Sep 17 00:00:00 2001 From: hhalwan Date: Sat, 9 Apr 2022 14:53:08 -0400 Subject: [PATCH 2/3] Added a new test: The hatch of the original patches should be used for a PatchCollection when match_original is true. --- lib/matplotlib/tests/test_collections.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index acca60fd01c1..9e4888edbce0 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -10,6 +10,7 @@ from matplotlib.backend_bases import MouseEvent import matplotlib.collections as mcollections import matplotlib.colors as mcolors +import matplotlib.patches as mpatches import matplotlib.transforms as mtransforms from matplotlib.collections import (Collection, LineCollection, EventCollection, PolyCollection) @@ -1117,3 +1118,25 @@ def test_set_offset_units(): off0 = sc.get_offsets() sc.set_offsets(list(zip(y, d))) np.testing.assert_allclose(off0, sc.get_offsets()) + + +@check_figures_equal(extensions=['png']) +def test_patch_collection_keeps_hatch(fig_test, fig_ref): + # When creating a PatchCollection, the hatch of the original patches should + # be used if match_original == True. + ax = fig_test.subplots() + patches = [ + mpatches.Rectangle((0, 0), 0.2, .2, hatch="/"), + mpatches.Rectangle((0.4, 0.4), 0.2, .2, hatch="/") + ] + collection = mcollections.PatchCollection(patches, match_original=True) + ax.add_collection(collection) + + ax = fig_ref.subplots() + patches = [ + mpatches.Rectangle((0, 0), 0.2, .2), + mpatches.Rectangle((0.4, 0.4), 0.2, .2) + ] + collection = mcollections.PatchCollection(patches, match_original=False, + hatch="/") + ax.add_collection(collection) From ed48cb05559b42d3f96db0c1bc966cfc99cffc4b Mon Sep 17 00:00:00 2001 From: hhalwan Date: Thu, 14 Apr 2022 09:38:34 -0400 Subject: [PATCH 3/3] Added warning if more than one type of hatch is detected. Checks patch._original_edgecolor to see if edgecolors were set. --- lib/matplotlib/collections.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 24f2e7e8895a..c182290e8fda 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1828,20 +1828,22 @@ def determine_facecolor(patch): if patch.get_fill(): return patch.get_facecolor() return [0, 0, 0, 0] - def is_default(c): - return mcolors.same_color([0, 0, 0, 0], c) kwargs['facecolors'] = [determine_facecolor(p) for p in patches] - edgecolors = [p.get_edgecolor() for p in patches] - # Since the hatch color is updated when the edge color - # is updated, we will ignore the edge color if it is blank (the - # default of [0, 0, 0, 0]). - if not all(is_default(c) for c in edgecolors): - kwargs['edgecolors'] = edgecolors + # If no edgecolor was set within the collection of patches + # then we will not supply the edgecolor argument, which allows the + # default hatch color to be used. + if any(p._original_edgecolor is not None for p in patches): + kwargs['edgecolors'] = [p.get_edgecolor() for p in patches] kwargs['linewidths'] = [p.get_linewidth() for p in patches] kwargs['linestyles'] = [p.get_linestyle() for p in patches] kwargs['antialiaseds'] = [p.get_antialiased() for p in patches] if patches: - kwargs['hatch'] = patches[0].get_hatch() + hatch = patches[0].get_hatch() + kwargs['hatch'] = hatch + if any(p.get_hatch() != hatch for p in patches): + warnings.warn("More than one type of hatch detected in " + "PatchCollection. Only using hatch of " + "first patch.") super().__init__(**kwargs)