Skip to content

Commit 5215f52

Browse files
committed
Added hatch handling
1 parent 7bb0da6 commit 5215f52

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

tikzplotlib/_hatches.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import warnings
2+
3+
# From https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.patches.Patch.html:
4+
# > Letters can be combined, in which case all the specified hatchings are done.
5+
# > If same letter repeats, it increases the density of hatching of that pattern.
6+
# To achieve something like this, custom patterns must be created
7+
# https://tex.stackexchange.com/questions/29359/pgfplots-how-to-fill-the-area-under-a-curve-with-oblique-lines-hatching-as-a/29367#29367
8+
9+
# These methods exist, and might be relevant:
10+
# matplotlib.backend_bases.GraphicsContextBase.set/get_hatch_color
11+
# matplotlib.backend_bases.GraphicsContextBase.set/get_hatch_linewidth
12+
# hatch_density is mentioned in mpl API Changes in 2.0.1
13+
14+
# for matplotlib hatches, see:
15+
# https://matplotlib.org/3.1.1/gallery/shapes_and_collections/hatch_demo.html
16+
17+
# hatches in tikzpgf: Ch 26 Pattern Lbrary with \usetikzlibrary{patterns}
18+
19+
BAD_MP_HATCH = ["o", "O"] # Bad hatch/pattern correspondance
20+
UNUSED_PGF_HATCH = ["dots"]
21+
_MP_HATCH2PGF_HATCH = {
22+
"-": "horizontal lines",
23+
"|": "vertical lines",
24+
"/": "north east lines",
25+
"\\": "north west lines",
26+
"+": "grid",
27+
"x": "crosshatch",
28+
".": "crosshatch dots",
29+
"*": "fivepointed stars",
30+
"o": "sixpointed stars",
31+
"O": "bricks",
32+
}
33+
34+
35+
def add_custom_pattern(mpl_hatch, pattern_name, pattern_definition):
36+
pass
37+
38+
39+
def __validate_mpl_hatch(mpl_hatch):
40+
if len(mpl_hatch) > 1:
41+
warnings.warn(
42+
f"tikzplotlib: Hatch '{mpl_hatch}' cannot be rendered. "
43+
+ "Only single character hatches are supported, e.g., "
44+
+ r"{'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}. "
45+
+ f"Hatch '{mpl_hatch[0]}' will be used."
46+
)
47+
mpl_hatch = mpl_hatch[0]
48+
49+
if mpl_hatch in BAD_MP_HATCH:
50+
warnings.warn(
51+
f"tikzplotlib: The hatches {BAD_MP_HATCH} do not have good PGF"
52+
+ " counterparts."
53+
)
54+
return mpl_hatch
55+
56+
57+
def _mpl_hatch2pgfp_hatch(data, mpl_hatch, color_name, color_rgba):
58+
"""
59+
Translates a hatch style of matplotlib to the corresponding style
60+
in PGFPlots.
61+
"""
62+
data["tikz libs"].add("patterns")
63+
64+
mpl_hatch = __validate_mpl_hatch(mpl_hatch)
65+
try:
66+
pgfplots_hatch = _MP_HATCH2PGF_HATCH.get(mpl_hatch)
67+
except KeyError:
68+
warnings.warn("tikzplotlib: The hatch", mpl_hatch, "cannot be handled.")
69+
return data, []
70+
else:
71+
pattern_options = [f"pattern={pgfplots_hatch}"]
72+
if color_name != "black":
73+
# PGFPlots render patterns in 'pattern color' (default: black)
74+
pattern_options += [f"pattern color={color_name}"]
75+
if color_rgba[3] != 1:
76+
ff = data["float format"]
77+
# PGFPlots render patterns according to opacity fill.
78+
pattern_options.append(("fill opacity=" + ff).format(color_rgba[3]))
79+
80+
# Add pattern as postaction to allow fill and pattern
81+
# https://tex.stackexchange.com/questions/24964/
82+
# how-to-combine-fill-and-pattern-in-a-pgfplot-bar-plot
83+
hatch_postaction = f"postaction={{{', '.join(pattern_options)}}}"
84+
85+
return data, [hatch_postaction]
86+
87+
88+
if __name__ == "__main__":
89+
import warnings
90+
import matplotlib.pyplot as plt
91+
import matplotlib
92+
93+
fig, ax = plt.subplots()
94+
95+
ax.bar(range(1, 5), range(1, 5), color="red", edgecolor="black", hatch="O")
96+
97+
for c in ax.get_children():
98+
try:
99+
hatch = c.get_hatch()
100+
except AttributeError as error:
101+
pass
102+
else:
103+
if hatch is not None:
104+
print(c, hatch)
105+
try:
106+
color = c.get_hatch_color()
107+
except AttributeError as error:
108+
pass
109+
else:
110+
if color is not None:
111+
print(c, color)
112+
try:
113+
lw = c.get_hatch_linewidth()
114+
except AttributeError as error:
115+
pass
116+
else:
117+
if lw is not None:
118+
print(c, lw)
119+
120+
plt.show()

0 commit comments

Comments
 (0)