Initial commit
This commit is contained in:
231
.venv/lib/python3.9/site-packages/matplotlib/hatch.py
Normal file
231
.venv/lib/python3.9/site-packages/matplotlib/hatch.py
Normal file
@@ -0,0 +1,231 @@
|
||||
"""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
|
||||
if not self.filled:
|
||||
inner_vertices = shape_vertices[::-1] * 0.9
|
||||
shape_codes = self.shape_codes
|
||||
shape_size = len(shape_vertices)
|
||||
|
||||
cursor = 0
|
||||
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[cursor:cursor + shape_size] = (shape_vertices +
|
||||
(col_pos, row_pos))
|
||||
codes[cursor:cursor + shape_size] = shape_codes
|
||||
cursor += shape_size
|
||||
if not self.filled:
|
||||
vertices[cursor:cursor + shape_size] = (inner_vertices +
|
||||
(col_pos, row_pos))
|
||||
codes[cursor:cursor + shape_size] = shape_codes
|
||||
cursor += shape_size
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# TODO: __init__ and class attributes override all attributes set by
|
||||
# SmallCircles. Should this class derive from Circles instead?
|
||||
class SmallFilledCircles(SmallCircles):
|
||||
size = 0.1
|
||||
filled = True
|
||||
|
||||
def __init__(self, hatch, density):
|
||||
self.num_rows = (hatch.count('.')) * density
|
||||
# Not super().__init__!
|
||||
Circles.__init__(self, 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',
|
||||
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 %(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)
|
Reference in New Issue
Block a user