Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/hatch.py: 28%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Contains classes for generating hatch patterns."""
3import numpy as np
5from matplotlib import _api
6from matplotlib.path import Path
9class HatchPatternBase:
10 """The base class for a hatch pattern."""
11 pass
14class HorizontalHatch(HatchPatternBase):
15 def __init__(self, hatch, density):
16 self.num_lines = int((hatch.count('-') + hatch.count('+')) * density)
17 self.num_vertices = self.num_lines * 2
19 def set_vertices_and_codes(self, vertices, codes):
20 steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False,
21 retstep=True)
22 steps += stepsize / 2.
23 vertices[0::2, 0] = 0.0
24 vertices[0::2, 1] = steps
25 vertices[1::2, 0] = 1.0
26 vertices[1::2, 1] = steps
27 codes[0::2] = Path.MOVETO
28 codes[1::2] = Path.LINETO
31class VerticalHatch(HatchPatternBase):
32 def __init__(self, hatch, density):
33 self.num_lines = int((hatch.count('|') + hatch.count('+')) * density)
34 self.num_vertices = self.num_lines * 2
36 def set_vertices_and_codes(self, vertices, codes):
37 steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False,
38 retstep=True)
39 steps += stepsize / 2.
40 vertices[0::2, 0] = steps
41 vertices[0::2, 1] = 0.0
42 vertices[1::2, 0] = steps
43 vertices[1::2, 1] = 1.0
44 codes[0::2] = Path.MOVETO
45 codes[1::2] = Path.LINETO
48class NorthEastHatch(HatchPatternBase):
49 def __init__(self, hatch, density):
50 self.num_lines = int(
51 (hatch.count('/') + hatch.count('x') + hatch.count('X')) * density)
52 if self.num_lines:
53 self.num_vertices = (self.num_lines + 1) * 2
54 else:
55 self.num_vertices = 0
57 def set_vertices_and_codes(self, vertices, codes):
58 steps = np.linspace(-0.5, 0.5, self.num_lines + 1)
59 vertices[0::2, 0] = 0.0 + steps
60 vertices[0::2, 1] = 0.0 - steps
61 vertices[1::2, 0] = 1.0 + steps
62 vertices[1::2, 1] = 1.0 - steps
63 codes[0::2] = Path.MOVETO
64 codes[1::2] = Path.LINETO
67class SouthEastHatch(HatchPatternBase):
68 def __init__(self, hatch, density):
69 self.num_lines = int(
70 (hatch.count('\\') + hatch.count('x') + hatch.count('X'))
71 * density)
72 if self.num_lines:
73 self.num_vertices = (self.num_lines + 1) * 2
74 else:
75 self.num_vertices = 0
77 def set_vertices_and_codes(self, vertices, codes):
78 steps = np.linspace(-0.5, 0.5, self.num_lines + 1)
79 vertices[0::2, 0] = 0.0 + steps
80 vertices[0::2, 1] = 1.0 + steps
81 vertices[1::2, 0] = 1.0 + steps
82 vertices[1::2, 1] = 0.0 + steps
83 codes[0::2] = Path.MOVETO
84 codes[1::2] = Path.LINETO
87class Shapes(HatchPatternBase):
88 filled = False
90 def __init__(self, hatch, density):
91 if self.num_rows == 0:
92 self.num_shapes = 0
93 self.num_vertices = 0
94 else:
95 self.num_shapes = ((self.num_rows // 2 + 1) * (self.num_rows + 1) +
96 (self.num_rows // 2) * self.num_rows)
97 self.num_vertices = (self.num_shapes *
98 len(self.shape_vertices) *
99 (1 if self.filled else 2))
101 def set_vertices_and_codes(self, vertices, codes):
102 offset = 1.0 / self.num_rows
103 shape_vertices = self.shape_vertices * offset * self.size
104 shape_codes = self.shape_codes
105 if not self.filled:
106 shape_vertices = np.concatenate( # Forward, then backward.
107 [shape_vertices, shape_vertices[::-1] * 0.9])
108 shape_codes = np.concatenate([shape_codes, shape_codes])
109 vertices_parts = []
110 codes_parts = []
111 for row in range(self.num_rows + 1):
112 if row % 2 == 0:
113 cols = np.linspace(0, 1, self.num_rows + 1)
114 else:
115 cols = np.linspace(offset / 2, 1 - offset / 2, self.num_rows)
116 row_pos = row * offset
117 for col_pos in cols:
118 vertices_parts.append(shape_vertices + [col_pos, row_pos])
119 codes_parts.append(shape_codes)
120 np.concatenate(vertices_parts, out=vertices)
121 np.concatenate(codes_parts, out=codes)
124class Circles(Shapes):
125 def __init__(self, hatch, density):
126 path = Path.unit_circle()
127 self.shape_vertices = path.vertices
128 self.shape_codes = path.codes
129 super().__init__(hatch, density)
132class SmallCircles(Circles):
133 size = 0.2
135 def __init__(self, hatch, density):
136 self.num_rows = (hatch.count('o')) * density
137 super().__init__(hatch, density)
140class LargeCircles(Circles):
141 size = 0.35
143 def __init__(self, hatch, density):
144 self.num_rows = (hatch.count('O')) * density
145 super().__init__(hatch, density)
148class SmallFilledCircles(Circles):
149 size = 0.1
150 filled = True
152 def __init__(self, hatch, density):
153 self.num_rows = (hatch.count('.')) * density
154 super().__init__(hatch, density)
157class Stars(Shapes):
158 size = 1.0 / 3.0
159 filled = True
161 def __init__(self, hatch, density):
162 self.num_rows = (hatch.count('*')) * density
163 path = Path.unit_regular_star(5)
164 self.shape_vertices = path.vertices
165 self.shape_codes = np.full(len(self.shape_vertices), Path.LINETO,
166 dtype=Path.code_type)
167 self.shape_codes[0] = Path.MOVETO
168 super().__init__(hatch, density)
170_hatch_types = [
171 HorizontalHatch,
172 VerticalHatch,
173 NorthEastHatch,
174 SouthEastHatch,
175 SmallCircles,
176 LargeCircles,
177 SmallFilledCircles,
178 Stars
179 ]
182def _validate_hatch_pattern(hatch):
183 valid_hatch_patterns = set(r'-+|/\xXoO.*')
184 if hatch is not None:
185 invalids = set(hatch).difference(valid_hatch_patterns)
186 if invalids:
187 valid = ''.join(sorted(valid_hatch_patterns))
188 invalids = ''.join(sorted(invalids))
189 _api.warn_deprecated(
190 '3.4',
191 removal='3.11', # one release after custom hatches (#20690)
192 message=f'hatch must consist of a string of "{valid}" or '
193 'None, but found the following invalid values '
194 f'"{invalids}". Passing invalid values is deprecated '
195 'since %(since)s and will become an error %(removal)s.'
196 )
199def get_path(hatchpattern, density=6):
200 """
201 Given a hatch specifier, *hatchpattern*, generates Path to render
202 the hatch in a unit square. *density* is the number of lines per
203 unit square.
204 """
205 density = int(density)
207 patterns = [hatch_type(hatchpattern, density)
208 for hatch_type in _hatch_types]
209 num_vertices = sum([pattern.num_vertices for pattern in patterns])
211 if num_vertices == 0:
212 return Path(np.empty((0, 2)))
214 vertices = np.empty((num_vertices, 2))
215 codes = np.empty(num_vertices, Path.code_type)
217 cursor = 0
218 for pattern in patterns:
219 if pattern.num_vertices != 0:
220 vertices_chunk = vertices[cursor:cursor + pattern.num_vertices]
221 codes_chunk = codes[cursor:cursor + pattern.num_vertices]
222 pattern.set_vertices_and_codes(vertices_chunk, codes_chunk)
223 cursor += pattern.num_vertices
225 return Path(vertices, codes)