|
1 |
| -import warnings |
| 1 | +r""" |
| 2 | + Map matplotlib hatches to tikz patterns |
| 3 | +
|
| 4 | + For matplotlib hatches, see: |
| 5 | + https://matplotlib.org/3.1.1/gallery/shapes_and_collections/hatch_demo.html |
2 | 6 |
|
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 |
| 7 | + For patterns in tikzpgf: |
| 8 | + Ch 26 Pattern Lbrary in the manual |
| 9 | + Requires \usetikzlibrary{patterns} |
| 10 | +""" |
8 | 11 |
|
9 |
| -# These methods exist, and might be relevant: |
| 12 | +# These methods exist, and might be relevant (in the future?): |
10 | 13 | # matplotlib.backend_bases.GraphicsContextBase.set/get_hatch_color
|
11 | 14 | # matplotlib.backend_bases.GraphicsContextBase.set/get_hatch_linewidth
|
12 | 15 | # hatch_density is mentioned in mpl API Changes in 2.0.1
|
13 | 16 |
|
14 |
| -# for matplotlib hatches, see: |
15 |
| -# https://matplotlib.org/3.1.1/gallery/shapes_and_collections/hatch_demo.html |
16 | 17 |
|
17 |
| -# hatches in tikzpgf: Ch 26 Pattern Lbrary with \usetikzlibrary{patterns} |
| 18 | +import warnings |
| 19 | + |
18 | 20 |
|
19 | 21 | BAD_MP_HATCH = ["o", "O"] # Bad hatch/pattern correspondance
|
20 |
| -UNUSED_PGF_HATCH = ["dots"] |
21 |
| -_MP_HATCH2PGF_HATCH = { |
| 22 | +UNUSED_PGF_PATTERN = ["dots"] |
| 23 | +_MP_HATCH2PGF_PATTERN = { |
22 | 24 | "-": "horizontal lines",
|
23 | 25 | "|": "vertical lines",
|
24 | 26 | "/": "north east lines",
|
|
32 | 34 | }
|
33 | 35 |
|
34 | 36 |
|
35 |
| -def add_custom_pattern(mpl_hatch, pattern_name, pattern_definition): |
36 |
| - pass |
| 37 | +def add_custom_pattern(mpl_hatch, pattern_name, pattern_definition=None): |
| 38 | + """ |
| 39 | + The patterns of tikzpgf are quite simple, and cannot be customized but for the |
| 40 | + color. A solution is to expose a function like this one to allow the user to |
| 41 | + populate _MP_HATCH2PGF_PATTERN with custom (hatch, pattern) pairs. mpl does no |
| 42 | + validation of the hatch string, it just ignores it if it does not recognize it, |
| 43 | + so it is possible to have <any> string be a mpl_hatch. |
| 44 | +
|
| 45 | + If the pattern definition is passed, it could be added at the start of the code |
| 46 | + in a similar fashion to |
| 47 | + > data["custom colors"] = {} |
| 48 | + in get_tikz_code(). tikzplotlib pattern definitions would mend the bad |
| 49 | + correspondence between the mpl hatches and tikz patterns, with custom patterns |
| 50 | + for the mpl hatches 'o' and 'O' |
| 51 | +
|
| 52 | + Want some opinions on this before I implement it.. |
| 53 | +
|
| 54 | + From https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.patches.Patch.html: |
| 55 | + > Letters can be combined, in which case all the specified hatchings are done. |
| 56 | + > If same letter repeats, it increases the density of hatching of that pattern. |
| 57 | + To achieve something like this, custom patterns must be created |
| 58 | + https://tex.stackexchange.com/questions/29359/pgfplots-how-to-fill-the-area- |
| 59 | + under-a-curve-with-oblique-lines-hatching-as-a/29367#29367 |
| 60 | + """ |
37 | 61 |
|
38 | 62 |
|
39 |
| -def __validate_mpl_hatch(mpl_hatch): |
40 |
| - if len(mpl_hatch) > 1: |
| 63 | +def __validate_hatch(hatch): |
| 64 | + """ Warn about the shortcomings of patterns """ |
| 65 | + if len(hatch) > 1: |
41 | 66 | warnings.warn(
|
42 |
| - f"tikzplotlib: Hatch '{mpl_hatch}' cannot be rendered. " |
| 67 | + f"tikzplotlib: Hatch '{hatch}' cannot be rendered. " |
43 | 68 | + "Only single character hatches are supported, e.g., "
|
44 | 69 | + r"{'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}. "
|
45 |
| - + f"Hatch '{mpl_hatch[0]}' will be used." |
| 70 | + + f"Hatch '{hatch[0]}' will be used." |
46 | 71 | )
|
47 |
| - mpl_hatch = mpl_hatch[0] |
| 72 | + hatch = hatch[0] |
48 | 73 |
|
49 |
| - if mpl_hatch in BAD_MP_HATCH: |
| 74 | + if hatch in BAD_MP_HATCH: |
50 | 75 | warnings.warn(
|
51 | 76 | f"tikzplotlib: The hatches {BAD_MP_HATCH} do not have good PGF"
|
52 | 77 | + " counterparts."
|
53 | 78 | )
|
54 |
| - return mpl_hatch |
| 79 | + return hatch |
55 | 80 |
|
56 | 81 |
|
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. |
| 82 | +def _mpl_hatch2pgfp_pattern(data, hatch, color_name, color_rgba): |
| 83 | + r""" |
| 84 | + Translates a hatch from matplotlib to the corresponding patten in PGFPlots. |
| 85 | +
|
| 86 | + Input: |
| 87 | + hatch - str, like {'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'} |
| 88 | + color_name - str, xcolor or custom color name |
| 89 | + color_rgba - np.array, the rgba value of the color |
| 90 | + Output: |
| 91 | + draw_options - list, empty or with a post action string |
61 | 92 | """
|
62 | 93 | data["tikz libs"].add("patterns")
|
63 | 94 |
|
64 |
| - mpl_hatch = __validate_mpl_hatch(mpl_hatch) |
| 95 | + hatch = __validate_hatch(hatch) |
65 | 96 | try:
|
66 |
| - pgfplots_hatch = _MP_HATCH2PGF_HATCH.get(mpl_hatch) |
| 97 | + pgfplots_pattern = _MP_HATCH2PGF_PATTERN.get(hatch) |
67 | 98 | except KeyError:
|
68 |
| - warnings.warn("tikzplotlib: The hatch", mpl_hatch, "cannot be handled.") |
| 99 | + warnings.warn("tikzplotlib: The hatch", hatch, "is ignored.") |
69 | 100 | 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 |
| 101 | + |
| 102 | + pattern_options = [f"pattern={pgfplots_pattern}"] |
| 103 | + if color_name != "black": |
| 104 | + # PGFPlots render patterns in 'pattern color' (default: black) |
| 105 | + pattern_options += [f"pattern color={color_name}"] |
| 106 | + if color_rgba[3] != 1: |
| 107 | + ff = data["float format"] |
| 108 | + # PGFPlots render patterns according to opacity fill. |
| 109 | + pattern_options.append(("fill opacity=" + ff).format(color_rgba[3])) |
| 110 | + |
| 111 | + # Add pattern as postaction to allow color fill and pattern together |
81 | 112 | # https://tex.stackexchange.com/questions/24964/
|
82 | 113 | # 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() |
| 114 | + postaction = f"postaction={{{', '.join(pattern_options)}}}" |
| 115 | + |
| 116 | + return data, [postaction] |
0 commit comments