You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			226 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			226 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
"""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)
 |