Buckets:
| """Contains classes for generating hatch patterns.""" | |
| import numpy as np | |
| from matplotlib import _api | |
| from matplotlib.path import Path | |
| class HatchPatternBase: | |
| """The base class for a hatch pattern.""" | |
| pass | |
| class HorizontalHatch(HatchPatternBase): | |
| def __init__(self, hatch, density): | |
| self.num_lines = int((hatch.count('-') + hatch.count('+')) * density) | |
| self.num_vertices = self.num_lines * 2 | |
| def set_vertices_and_codes(self, vertices, codes): | |
| steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False, | |
| retstep=True) | |
| steps += stepsize / 2. | |
| vertices[0::2, 0] = 0.0 | |
| vertices[0::2, 1] = steps | |
| vertices[1::2, 0] = 1.0 | |
| vertices[1::2, 1] = steps | |
| codes[0::2] = Path.MOVETO | |
| codes[1::2] = Path.LINETO | |
| class VerticalHatch(HatchPatternBase): | |
| def __init__(self, hatch, density): | |
| self.num_lines = int((hatch.count('|') + hatch.count('+')) * density) | |
| self.num_vertices = self.num_lines * 2 | |
| def set_vertices_and_codes(self, vertices, codes): | |
| steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False, | |
| retstep=True) | |
| steps += stepsize / 2. | |
| vertices[0::2, 0] = steps | |
| vertices[0::2, 1] = 0.0 | |
| vertices[1::2, 0] = steps | |
| vertices[1::2, 1] = 1.0 | |
| codes[0::2] = Path.MOVETO | |
| codes[1::2] = Path.LINETO | |
| class NorthEastHatch(HatchPatternBase): | |
| def __init__(self, hatch, density): | |
| self.num_lines = int( | |
| (hatch.count('/') + hatch.count('x') + hatch.count('X')) * density) | |
| if self.num_lines: | |
| self.num_vertices = (self.num_lines + 1) * 2 | |
| else: | |
| self.num_vertices = 0 | |
| def set_vertices_and_codes(self, vertices, codes): | |
| steps = np.linspace(-0.5, 0.5, self.num_lines + 1) | |
| vertices[0::2, 0] = 0.0 + steps | |
| vertices[0::2, 1] = 0.0 - steps | |
| vertices[1::2, 0] = 1.0 + steps | |
| vertices[1::2, 1] = 1.0 - steps | |
| codes[0::2] = Path.MOVETO | |
| codes[1::2] = Path.LINETO | |
| class SouthEastHatch(HatchPatternBase): | |
| def __init__(self, hatch, density): | |
| self.num_lines = int( | |
| (hatch.count('\\') + hatch.count('x') + hatch.count('X')) | |
| * density) | |
| if self.num_lines: | |
| self.num_vertices = (self.num_lines + 1) * 2 | |
| else: | |
| self.num_vertices = 0 | |
| def set_vertices_and_codes(self, vertices, codes): | |
| steps = np.linspace(-0.5, 0.5, self.num_lines + 1) | |
| vertices[0::2, 0] = 0.0 + steps | |
| vertices[0::2, 1] = 1.0 + steps | |
| vertices[1::2, 0] = 1.0 + steps | |
| vertices[1::2, 1] = 0.0 + steps | |
| codes[0::2] = Path.MOVETO | |
| codes[1::2] = Path.LINETO | |
| class Shapes(HatchPatternBase): | |
| filled = False | |
| def __init__(self, hatch, density): | |
| if self.num_rows == 0: | |
| self.num_shapes = 0 | |
| self.num_vertices = 0 | |
| else: | |
| self.num_shapes = ((self.num_rows // 2 + 1) * (self.num_rows + 1) + | |
| (self.num_rows // 2) * self.num_rows) | |
| self.num_vertices = (self.num_shapes * | |
| len(self.shape_vertices) * | |
| (1 if self.filled else 2)) | |
| def set_vertices_and_codes(self, vertices, codes): | |
| offset = 1.0 / self.num_rows | |
| shape_vertices = self.shape_vertices * offset * self.size | |
| shape_codes = self.shape_codes | |
| if not self.filled: | |
| shape_vertices = np.concatenate( # Forward, then backward. | |
| [shape_vertices, shape_vertices[::-1] * 0.9]) | |
| shape_codes = np.concatenate([shape_codes, shape_codes]) | |
| vertices_parts = [] | |
| codes_parts = [] | |
| for row in range(self.num_rows + 1): | |
| if row % 2 == 0: | |
| cols = np.linspace(0, 1, self.num_rows + 1) | |
| else: | |
| cols = np.linspace(offset / 2, 1 - offset / 2, self.num_rows) | |
| row_pos = row * offset | |
| for col_pos in cols: | |
| vertices_parts.append(shape_vertices + [col_pos, row_pos]) | |
| codes_parts.append(shape_codes) | |
| np.concatenate(vertices_parts, out=vertices) | |
| np.concatenate(codes_parts, out=codes) | |
| class Circles(Shapes): | |
| def __init__(self, hatch, density): | |
| path = Path.unit_circle() | |
| self.shape_vertices = path.vertices | |
| self.shape_codes = path.codes | |
| super().__init__(hatch, density) | |
| class SmallCircles(Circles): | |
| size = 0.2 | |
| def __init__(self, hatch, density): | |
| self.num_rows = (hatch.count('o')) * density | |
| super().__init__(hatch, density) | |
| class LargeCircles(Circles): | |
| size = 0.35 | |
| def __init__(self, hatch, density): | |
| self.num_rows = (hatch.count('O')) * density | |
| super().__init__(hatch, density) | |
| class SmallFilledCircles(Circles): | |
| size = 0.1 | |
| filled = True | |
| def __init__(self, hatch, density): | |
| self.num_rows = (hatch.count('.')) * density | |
| super().__init__(hatch, density) | |
| class Stars(Shapes): | |
| size = 1.0 / 3.0 | |
| filled = True | |
| def __init__(self, hatch, density): | |
| self.num_rows = (hatch.count('*')) * density | |
| path = Path.unit_regular_star(5) | |
| self.shape_vertices = path.vertices | |
| self.shape_codes = np.full(len(self.shape_vertices), Path.LINETO, | |
| dtype=Path.code_type) | |
| self.shape_codes[0] = Path.MOVETO | |
| super().__init__(hatch, density) | |
| _hatch_types = [ | |
| HorizontalHatch, | |
| VerticalHatch, | |
| NorthEastHatch, | |
| SouthEastHatch, | |
| SmallCircles, | |
| LargeCircles, | |
| SmallFilledCircles, | |
| Stars | |
| ] | |
| def _validate_hatch_pattern(hatch): | |
| valid_hatch_patterns = set(r'-+|/\xXoO.*') | |
| if hatch is not None: | |
| invalids = set(hatch).difference(valid_hatch_patterns) | |
| if invalids: | |
| valid = ''.join(sorted(valid_hatch_patterns)) | |
| invalids = ''.join(sorted(invalids)) | |
| _api.warn_deprecated( | |
| '3.4', | |
| removal='3.11', # one release after custom hatches (#20690) | |
| message=f'hatch must consist of a string of "{valid}" or ' | |
| 'None, but found the following invalid values ' | |
| f'"{invalids}". Passing invalid values is deprecated ' | |
| 'since %(since)s and will become an error in %(removal)s.' | |
| ) | |
| def get_path(hatchpattern, density=6): | |
| """ | |
| Given a hatch specifier, *hatchpattern*, generates Path to render | |
| the hatch in a unit square. *density* is the number of lines per | |
| unit square. | |
| """ | |
| density = int(density) | |
| patterns = [hatch_type(hatchpattern, density) | |
| for hatch_type in _hatch_types] | |
| num_vertices = sum([pattern.num_vertices for pattern in patterns]) | |
| if num_vertices == 0: | |
| return Path(np.empty((0, 2))) | |
| vertices = np.empty((num_vertices, 2)) | |
| codes = np.empty(num_vertices, Path.code_type) | |
| cursor = 0 | |
| for pattern in patterns: | |
| if pattern.num_vertices != 0: | |
| vertices_chunk = vertices[cursor:cursor + pattern.num_vertices] | |
| codes_chunk = codes[cursor:cursor + pattern.num_vertices] | |
| pattern.set_vertices_and_codes(vertices_chunk, codes_chunk) | |
| cursor += pattern.num_vertices | |
| return Path(vertices, codes) | |
Xet Storage Details
- Size:
- 7.45 kB
- Xet hash:
- e25b4b7e094ead1e3516efefd243d4cc612d1b832275977bc22119add6746295
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.