Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c52cb41

Browse files
authored
Enables setting hatch linewidth in Patches and Collections, also fixes setting hatch linewidth by rcParams (#28048)
1 parent e8654e1 commit c52cb41

File tree

12 files changed

+103
-27
lines changed

12 files changed

+103
-27
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``PdfFile.hatchPatterns``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... is deprecated.

lib/matplotlib/backend_bases.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,10 @@ def get_hatch_linewidth(self):
961961
"""Get the hatch linewidth."""
962962
return self._hatch_linewidth
963963

964+
def set_hatch_linewidth(self, hatch_linewidth):
965+
"""Set the hatch linewidth."""
966+
self._hatch_linewidth = hatch_linewidth
967+
964968
def get_sketch_params(self):
965969
"""
966970
Return the sketch parameters for the artist.

lib/matplotlib/backend_bases.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class GraphicsContextBase:
167167
def get_hatch_color(self) -> ColorType: ...
168168
def set_hatch_color(self, hatch_color: ColorType) -> None: ...
169169
def get_hatch_linewidth(self) -> float: ...
170+
def set_hatch_linewidth(self, hatch_linewidth: float) -> None: ...
170171
def get_sketch_params(self) -> tuple[float, float, float] | None: ...
171172
def set_sketch_params(
172173
self,

lib/matplotlib/backends/backend_pdf.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ def __init__(self, filename, metadata=None):
731731
self._soft_mask_states = {}
732732
self._soft_mask_seq = (Name(f'SM{i}') for i in itertools.count(1))
733733
self._soft_mask_groups = []
734-
self.hatchPatterns = {}
734+
self._hatch_patterns = {}
735735
self._hatch_pattern_seq = (Name(f'H{i}') for i in itertools.count(1))
736736
self.gouraudTriangles = []
737737

@@ -1534,26 +1534,29 @@ def _write_soft_mask_groups(self):
15341534

15351535
def hatchPattern(self, hatch_style):
15361536
# The colors may come in as numpy arrays, which aren't hashable
1537-
if hatch_style is not None:
1538-
edge, face, hatch = hatch_style
1539-
if edge is not None:
1540-
edge = tuple(edge)
1541-
if face is not None:
1542-
face = tuple(face)
1543-
hatch_style = (edge, face, hatch)
1544-
1545-
pattern = self.hatchPatterns.get(hatch_style, None)
1537+
edge, face, hatch, lw = hatch_style
1538+
if edge is not None:
1539+
edge = tuple(edge)
1540+
if face is not None:
1541+
face = tuple(face)
1542+
hatch_style = (edge, face, hatch, lw)
1543+
1544+
pattern = self._hatch_patterns.get(hatch_style, None)
15461545
if pattern is not None:
15471546
return pattern
15481547

15491548
name = next(self._hatch_pattern_seq)
1550-
self.hatchPatterns[hatch_style] = name
1549+
self._hatch_patterns[hatch_style] = name
15511550
return name
15521551

1552+
hatchPatterns = _api.deprecated("3.10")(property(lambda self: {
1553+
k: (e, f, h) for k, (e, f, h, l) in self._hatch_patterns.items()
1554+
}))
1555+
15531556
def writeHatches(self):
15541557
hatchDict = dict()
15551558
sidelen = 72.0
1556-
for hatch_style, name in self.hatchPatterns.items():
1559+
for hatch_style, name in self._hatch_patterns.items():
15571560
ob = self.reserveObject('hatch pattern')
15581561
hatchDict[name] = ob
15591562
res = {'Procsets':
@@ -1568,7 +1571,7 @@ def writeHatches(self):
15681571
# Change origin to match Agg at top-left.
15691572
'Matrix': [1, 0, 0, 1, 0, self.height * 72]})
15701573

1571-
stroke_rgb, fill_rgb, hatch = hatch_style
1574+
stroke_rgb, fill_rgb, hatch, lw = hatch_style
15721575
self.output(stroke_rgb[0], stroke_rgb[1], stroke_rgb[2],
15731576
Op.setrgb_stroke)
15741577
if fill_rgb is not None:
@@ -1577,7 +1580,7 @@ def writeHatches(self):
15771580
0, 0, sidelen, sidelen, Op.rectangle,
15781581
Op.fill)
15791582

1580-
self.output(mpl.rcParams['hatch.linewidth'], Op.setlinewidth)
1583+
self.output(lw, Op.setlinewidth)
15811584

15821585
self.output(*self.pathOperations(
15831586
Path.hatch(hatch),
@@ -2508,14 +2511,14 @@ def alpha_cmd(self, alpha, forced, effective_alphas):
25082511
name = self.file.alphaState(effective_alphas)
25092512
return [name, Op.setgstate]
25102513

2511-
def hatch_cmd(self, hatch, hatch_color):
2514+
def hatch_cmd(self, hatch, hatch_color, hatch_linewidth):
25122515
if not hatch:
25132516
if self._fillcolor is not None:
25142517
return self.fillcolor_cmd(self._fillcolor)
25152518
else:
25162519
return [Name('DeviceRGB'), Op.setcolorspace_nonstroke]
25172520
else:
2518-
hatch_style = (hatch_color, self._fillcolor, hatch)
2521+
hatch_style = (hatch_color, self._fillcolor, hatch, hatch_linewidth)
25192522
name = self.file.hatchPattern(hatch_style)
25202523
return [Name('Pattern'), Op.setcolorspace_nonstroke,
25212524
name, Op.setcolor_nonstroke]
@@ -2580,8 +2583,8 @@ def clip_cmd(self, cliprect, clippath):
25802583
(('_dashes',), dash_cmd),
25812584
(('_rgb',), rgb_cmd),
25822585
# must come after fillcolor and rgb
2583-
(('_hatch', '_hatch_color'), hatch_cmd),
2584-
)
2586+
(('_hatch', '_hatch_color', '_hatch_linewidth'), hatch_cmd),
2587+
)
25852588

25862589
def delta(self, other):
25872590
"""
@@ -2609,11 +2612,11 @@ def delta(self, other):
26092612
break
26102613

26112614
# Need to update hatching if we also updated fillcolor
2612-
if params == ('_hatch', '_hatch_color') and fill_performed:
2615+
if cmd.__name__ == 'hatch_cmd' and fill_performed:
26132616
different = True
26142617

26152618
if different:
2616-
if params == ('_fillcolor',):
2619+
if cmd.__name__ == 'fillcolor_cmd':
26172620
fill_performed = True
26182621
theirs = [getattr(other, p) for p in params]
26192622
cmds.extend(cmd(self, *theirs))

lib/matplotlib/backends/backend_ps.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -505,12 +505,11 @@ def set_font(self, fontname, fontsize, store=True):
505505
self.fontname = fontname
506506
self.fontsize = fontsize
507507

508-
def create_hatch(self, hatch):
508+
def create_hatch(self, hatch, linewidth):
509509
sidelen = 72
510510
if hatch in self._hatches:
511511
return self._hatches[hatch]
512512
name = 'H%d' % len(self._hatches)
513-
linewidth = mpl.rcParams['hatch.linewidth']
514513
pageheight = self.height * 72
515514
self._pswriter.write(f"""\
516515
<< /PatternType 1
@@ -933,7 +932,7 @@ def _draw_ps(self, ps, gc, rgbFace, *, fill=True, stroke=True):
933932
write("grestore\n")
934933

935934
if hatch:
936-
hatch_name = self.create_hatch(hatch)
935+
hatch_name = self.create_hatch(hatch, gc.get_hatch_linewidth())
937936
write("gsave\n")
938937
write(_nums_to_str(*gc.get_hatch_color()[:3]))
939938
write(f" {hatch_name} setpattern fill grestore\n")

lib/matplotlib/backends/backend_svg.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,12 @@ def _get_hatch(self, gc, rgbFace):
500500
edge = gc.get_hatch_color()
501501
if edge is not None:
502502
edge = tuple(edge)
503-
dictkey = (gc.get_hatch(), rgbFace, edge)
503+
lw = gc.get_hatch_linewidth()
504+
dictkey = (gc.get_hatch(), rgbFace, edge, lw)
504505
oid = self._hatchd.get(dictkey)
505506
if oid is None:
506507
oid = self._make_id('h', dictkey)
507-
self._hatchd[dictkey] = ((gc.get_hatch_path(), rgbFace, edge), oid)
508+
self._hatchd[dictkey] = ((gc.get_hatch_path(), rgbFace, edge, lw), oid)
508509
else:
509510
_, oid = oid
510511
return oid
@@ -515,7 +516,7 @@ def _write_hatches(self):
515516
HATCH_SIZE = 72
516517
writer = self.writer
517518
writer.start('defs')
518-
for (path, face, stroke), oid in self._hatchd.values():
519+
for (path, face, stroke, lw), oid in self._hatchd.values():
519520
writer.start(
520521
'pattern',
521522
id=oid,
@@ -539,7 +540,7 @@ def _write_hatches(self):
539540
hatch_style = {
540541
'fill': rgb2hex(stroke),
541542
'stroke': rgb2hex(stroke),
542-
'stroke-width': str(mpl.rcParams['hatch.linewidth']),
543+
'stroke-width': str(lw),
543544
'stroke-linecap': 'butt',
544545
'stroke-linejoin': 'miter'
545546
}

lib/matplotlib/collections.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def __init__(self, *,
175175
self._edge_is_mapped = None
176176
self._mapped_colors = None # calculated in update_scalarmappable
177177
self._hatch_color = mcolors.to_rgba(mpl.rcParams['hatch.color'])
178+
self._hatch_linewidth = mpl.rcParams['hatch.linewidth']
178179
self.set_facecolor(facecolors)
179180
self.set_edgecolor(edgecolors)
180181
self.set_linewidth(linewidths)
@@ -365,6 +366,7 @@ def draw(self, renderer):
365366
if self._hatch:
366367
gc.set_hatch(self._hatch)
367368
gc.set_hatch_color(self._hatch_color)
369+
gc.set_hatch_linewidth(self._hatch_linewidth)
368370

369371
if self.get_sketch_params() is not None:
370372
gc.set_sketch_params(*self.get_sketch_params())
@@ -543,6 +545,14 @@ def get_hatch(self):
543545
"""Return the current hatching pattern."""
544546
return self._hatch
545547

548+
def set_hatch_linewidth(self, lw):
549+
"""Set the hatch linewidth."""
550+
self._hatch_linewidth = lw
551+
552+
def get_hatch_linewidth(self):
553+
"""Return the hatch linewidth."""
554+
return self._hatch_linewidth
555+
546556
def set_offsets(self, offsets):
547557
"""
548558
Set the offsets for the collection.

lib/matplotlib/collections.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class Collection(colorizer.ColorizingArtist):
4949
def get_urls(self) -> Sequence[str | None]: ...
5050
def set_hatch(self, hatch: str) -> None: ...
5151
def get_hatch(self) -> str: ...
52+
def set_hatch_linewidth(self, lw: float) -> None: ...
53+
def get_hatch_linewidth(self) -> float: ...
5254
def set_offsets(self, offsets: ArrayLike) -> None: ...
5355
def get_offsets(self) -> ArrayLike: ...
5456
def set_linewidth(self, lw: float | Sequence[float]) -> None: ...

lib/matplotlib/patches.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(self, *,
7272
joinstyle = JoinStyle.miter
7373

7474
self._hatch_color = colors.to_rgba(mpl.rcParams['hatch.color'])
75+
self._hatch_linewidth = mpl.rcParams['hatch.linewidth']
7576
self._fill = bool(fill) # needed for set_facecolor call
7677
if color is not None:
7778
if edgecolor is not None or facecolor is not None:
@@ -571,6 +572,14 @@ def get_hatch(self):
571572
"""Return the hatching pattern."""
572573
return self._hatch
573574

575+
def set_hatch_linewidth(self, lw):
576+
"""Set the hatch linewidth."""
577+
self._hatch_linewidth = lw
578+
579+
def get_hatch_linewidth(self):
580+
"""Return the hatch linewidth."""
581+
return self._hatch_linewidth
582+
574583
def _draw_paths_with_artist_properties(
575584
self, renderer, draw_path_args_list):
576585
"""
@@ -605,6 +614,7 @@ def _draw_paths_with_artist_properties(
605614
if self._hatch:
606615
gc.set_hatch(self._hatch)
607616
gc.set_hatch_color(self._hatch_color)
617+
gc.set_hatch_linewidth(self._hatch_linewidth)
608618

609619
if self.get_sketch_params() is not None:
610620
gc.set_sketch_params(*self.get_sketch_params())

lib/matplotlib/patches.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class Patch(artist.Artist):
5959
def set_joinstyle(self, s: JoinStyleType) -> None: ...
6060
def get_joinstyle(self) -> Literal["miter", "round", "bevel"]: ...
6161
def set_hatch(self, hatch: str) -> None: ...
62+
def set_hatch_linewidth(self, lw: float) -> None: ...
63+
def get_hatch_linewidth(self) -> float: ...
6264
def get_hatch(self) -> str: ...
6365
def get_path(self) -> Path: ...
6466

lib/matplotlib/tests/test_collections.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,3 +1361,26 @@ def test_striped_lines(fig_test, fig_ref, gapcolor):
13611361
for x, gcol, ls in zip(x, itertools.cycle(gapcolor),
13621362
itertools.cycle(linestyles)):
13631363
ax_ref.axvline(x, 0, 1, linewidth=20, linestyle=ls, gapcolor=gcol, alpha=0.5)
1364+
1365+
1366+
@check_figures_equal(extensions=['png', 'pdf', 'svg', 'eps'])
1367+
def test_hatch_linewidth(fig_test, fig_ref):
1368+
ax_test = fig_test.add_subplot()
1369+
ax_ref = fig_ref.add_subplot()
1370+
1371+
lw = 2.0
1372+
1373+
polygons = [
1374+
[(0.1, 0.1), (0.1, 0.4), (0.4, 0.4), (0.4, 0.1)],
1375+
[(0.6, 0.6), (0.6, 0.9), (0.9, 0.9), (0.9, 0.6)],
1376+
]
1377+
ref = PolyCollection(polygons, hatch="x")
1378+
ref.set_hatch_linewidth(lw)
1379+
1380+
with mpl.rc_context({"hatch.linewidth": lw}):
1381+
test = PolyCollection(polygons, hatch="x")
1382+
1383+
ax_ref.add_collection(ref)
1384+
ax_test.add_collection(test)
1385+
1386+
assert test.get_hatch_linewidth() == ref.get_hatch_linewidth() == lw

lib/matplotlib/tests/test_patches.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,3 +960,20 @@ def test_arrow_set_data():
960960
)
961961
arrow.set_data(x=.5, dx=3, dy=8, width=1.2)
962962
assert np.allclose(expected2, np.round(arrow.get_verts(), 2))
963+
964+
965+
@check_figures_equal(extensions=["png", "pdf", "svg", "eps"])
966+
def test_set_and_get_hatch_linewidth(fig_test, fig_ref):
967+
ax_test = fig_test.add_subplot()
968+
ax_ref = fig_ref.add_subplot()
969+
970+
lw = 2.0
971+
972+
with plt.rc_context({"hatch.linewidth": lw}):
973+
ax_ref.add_patch(mpatches.Rectangle((0, 0), 1, 1, hatch="x"))
974+
975+
ax_test.add_patch(mpatches.Rectangle((0, 0), 1, 1, hatch="x"))
976+
ax_test.patches[0].set_hatch_linewidth(lw)
977+
978+
assert ax_ref.patches[0].get_hatch_linewidth() == lw
979+
assert ax_test.patches[0].get_hatch_linewidth() == lw

0 commit comments

Comments
 (0)