Improve type annotations based on mypy errors
This commit is contained in:
parent
bd4085365f
commit
157df47884
@ -6,7 +6,7 @@ import gdsii.library
|
|||||||
import gdsii.structure
|
import gdsii.structure
|
||||||
import gdsii.elements
|
import gdsii.elements
|
||||||
|
|
||||||
from typing import List, Any, Dict, Tuple, Callable
|
from typing import List, Any, Dict, Tuple, Callable, Union, Sequence, Iterable, Optional
|
||||||
import re
|
import re
|
||||||
import io
|
import io
|
||||||
import copy
|
import copy
|
||||||
@ -39,13 +39,13 @@ path_cap_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def write(patterns: Pattern or List[Pattern],
|
def write(patterns: Union[Pattern, List[Pattern]],
|
||||||
stream: io.BufferedIOBase,
|
stream: io.BufferedIOBase,
|
||||||
meters_per_unit: float,
|
meters_per_unit: float,
|
||||||
logical_units_per_unit: float = 1,
|
logical_units_per_unit: float = 1,
|
||||||
library_name: str = 'masque-gdsii-write',
|
library_name: str = 'masque-gdsii-write',
|
||||||
modify_originals: bool = False,
|
modify_originals: bool = False,
|
||||||
disambiguate_func: Callable[[List[Pattern]], None] = None):
|
disambiguate_func: Callable[[Iterable[Pattern]], None] = None):
|
||||||
"""
|
"""
|
||||||
Write a `Pattern` or list of patterns to a GDSII file, by first calling
|
Write a `Pattern` or list of patterns to a GDSII file, by first calling
|
||||||
`.polygonize()` to change the shapes into polygons, and then writing patterns
|
`.polygonize()` to change the shapes into polygons, and then writing patterns
|
||||||
@ -119,8 +119,8 @@ def write(patterns: Pattern or List[Pattern],
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def writefile(patterns: List[Pattern] or Pattern,
|
def writefile(patterns: Union[List[Pattern], Pattern],
|
||||||
filename: str or pathlib.Path,
|
filename: Union[str, pathlib.Path],
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
@ -137,7 +137,7 @@ def writefile(patterns: List[Pattern] or Pattern,
|
|||||||
"""
|
"""
|
||||||
path = pathlib.Path(filename)
|
path = pathlib.Path(filename)
|
||||||
if path.suffix == '.gz':
|
if path.suffix == '.gz':
|
||||||
open_func = gzip.open
|
open_func: Callable = gzip.open
|
||||||
else:
|
else:
|
||||||
open_func = open
|
open_func = open
|
||||||
|
|
||||||
@ -185,7 +185,8 @@ def dose2dtype(patterns: List[Pattern],
|
|||||||
dose_vals = set()
|
dose_vals = set()
|
||||||
for pat_id, pat_dose in sd_table:
|
for pat_id, pat_dose in sd_table:
|
||||||
pat = patterns_by_id[pat_id]
|
pat = patterns_by_id[pat_id]
|
||||||
[dose_vals.add(shape.dose * pat_dose) for shape in pat.shapes]
|
for shape in pat.shapes:
|
||||||
|
dose_vals.add(shape.dose * pat_dose)
|
||||||
|
|
||||||
if len(dose_vals) > 256:
|
if len(dose_vals) > 256:
|
||||||
raise PatternError('Too many dose values: {}, maximum 256 when using dtypes.'.format(len(dose_vals)))
|
raise PatternError('Too many dose values: {}, maximum 256 when using dtypes.'.format(len(dose_vals)))
|
||||||
@ -228,10 +229,10 @@ def dose2dtype(patterns: List[Pattern],
|
|||||||
return patterns, dose_vals_list
|
return patterns, dose_vals_list
|
||||||
|
|
||||||
|
|
||||||
def readfile(filename: str or pathlib.Path,
|
def readfile(filename: Union[str, pathlib.Path],
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> (Dict[str, Pattern], Dict[str, Any]):
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `gdsii.read()` that takes a filename or path instead of a stream.
|
Wrapper for `gdsii.read()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -244,7 +245,7 @@ def readfile(filename: str or pathlib.Path,
|
|||||||
"""
|
"""
|
||||||
path = pathlib.Path(filename)
|
path = pathlib.Path(filename)
|
||||||
if path.suffix == '.gz':
|
if path.suffix == '.gz':
|
||||||
open_func = gzip.open
|
open_func: Callable = gzip.open
|
||||||
else:
|
else:
|
||||||
open_func = open
|
open_func = open
|
||||||
|
|
||||||
@ -256,7 +257,7 @@ def readfile(filename: str or pathlib.Path,
|
|||||||
def read(stream: io.BufferedIOBase,
|
def read(stream: io.BufferedIOBase,
|
||||||
use_dtype_as_dose: bool = False,
|
use_dtype_as_dose: bool = False,
|
||||||
clean_vertices: bool = True,
|
clean_vertices: bool = True,
|
||||||
) -> (Dict[str, Pattern], Dict[str, Any]):
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Read a gdsii file and translate it into a dict of Pattern objects. GDSII structures are
|
Read a gdsii file and translate it into a dict of Pattern objects. GDSII structures are
|
||||||
translated into Pattern objects; boundaries are translated into polygons, and srefs and arefs
|
translated into Pattern objects; boundaries are translated into polygons, and srefs and arefs
|
||||||
@ -466,8 +467,8 @@ def _aref_to_gridrep(element: gdsii.elements.ARef) -> GridRepetition:
|
|||||||
return gridrep
|
return gridrep
|
||||||
|
|
||||||
|
|
||||||
def _subpatterns_to_refs(subpatterns: List[SubPattern or GridRepetition]
|
def _subpatterns_to_refs(subpatterns: List[Union[SubPattern, GridRepetition]]
|
||||||
) -> List[gdsii.elements.ARef or gdsii.elements.SRef]:
|
) -> List[Union[gdsii.elements.ARef, gdsii.elements.SRef]]:
|
||||||
refs = []
|
refs = []
|
||||||
for subpat in subpatterns:
|
for subpat in subpatterns:
|
||||||
if subpat.pattern is None:
|
if subpat.pattern is None:
|
||||||
@ -574,7 +575,7 @@ def disambiguate_pattern_names(patterns,
|
|||||||
# Should never happen since zero-length names are replaced
|
# Should never happen since zero-length names are replaced
|
||||||
raise PatternError('Zero-length name after sanitize+encode,\n originally "{}"'.format(pat.name))
|
raise PatternError('Zero-length name after sanitize+encode,\n originally "{}"'.format(pat.name))
|
||||||
if len(encoded_name) > max_name_length:
|
if len(encoded_name) > max_name_length:
|
||||||
raise PatternError('Pattern name "{}" length > {} after encode,\n originally "{}"'.format(encoded_name, max_name_length, pat.name))
|
raise PatternError('Pattern name "{!r}" length > {} after encode,\n originally "{}"'.format(encoded_name, max_name_length, pat.name))
|
||||||
|
|
||||||
pat.name = encoded_name
|
pat.name = encoded_name
|
||||||
used_names.append(suffixed_name)
|
used_names.append(suffixed_name)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
SVG file format readers and writers
|
SVG file format readers and writers
|
||||||
"""
|
"""
|
||||||
|
from typing import Dict, Optional
|
||||||
import svgwrite
|
import svgwrite
|
||||||
import numpy
|
import numpy
|
||||||
import warnings
|
import warnings
|
||||||
@ -56,7 +56,7 @@ def writefile(pattern: Pattern,
|
|||||||
debug=(not custom_attributes))
|
debug=(not custom_attributes))
|
||||||
|
|
||||||
# Get a dict of id(pattern) -> pattern
|
# Get a dict of id(pattern) -> pattern
|
||||||
patterns_by_id = {**(pattern.referenced_patterns_by_id()), id(pattern): pattern}
|
patterns_by_id = {**(pattern.referenced_patterns_by_id()), id(pattern): pattern} # type: Dict[int, Optional[Pattern]]
|
||||||
|
|
||||||
# Now create a group for each row in sd_table (ie, each pattern + dose combination)
|
# Now create a group for each row in sd_table (ie, each pattern + dose combination)
|
||||||
# and add in any Boundary and Use elements
|
# and add in any Boundary and Use elements
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
Base object for containing a lithography mask.
|
Base object for containing a lithography mask.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List, Callable, Tuple, Dict, Union, Set
|
from typing import List, Callable, Tuple, Dict, Union, Set, Sequence, Optional, Type
|
||||||
|
from typing import MutableMapping, Iterable
|
||||||
import copy
|
import copy
|
||||||
import itertools
|
import itertools
|
||||||
import pickle
|
import pickle
|
||||||
@ -39,7 +40,7 @@ class Pattern:
|
|||||||
labels: List[Label]
|
labels: List[Label]
|
||||||
""" List of all labels in this Pattern. """
|
""" List of all labels in this Pattern. """
|
||||||
|
|
||||||
subpatterns: List[SubPattern or GridRepetition]
|
subpatterns: List[Union[SubPattern, GridRepetition]]
|
||||||
""" List of all objects referencing other patterns in this Pattern.
|
""" List of all objects referencing other patterns in this Pattern.
|
||||||
Examples are SubPattern (gdsii "instances") or GridRepetition (gdsii "arrays")
|
Examples are SubPattern (gdsii "instances") or GridRepetition (gdsii "arrays")
|
||||||
Multiple objects in this list may reference the same Pattern object
|
Multiple objects in this list may reference the same Pattern object
|
||||||
@ -54,9 +55,9 @@ class Pattern:
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
name: str = '',
|
name: str = '',
|
||||||
shapes: List[Shape] = (),
|
shapes: Sequence[Shape] = (),
|
||||||
labels: List[Label] = (),
|
labels: Sequence[Label] = (),
|
||||||
subpatterns: List[SubPattern] = (),
|
subpatterns: Sequence[Union[SubPattern, GridRepetition]] = (),
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -129,7 +130,7 @@ class Pattern:
|
|||||||
def subset(self,
|
def subset(self,
|
||||||
shapes_func: Callable[[Shape], bool] = None,
|
shapes_func: Callable[[Shape], bool] = None,
|
||||||
labels_func: Callable[[Label], bool] = None,
|
labels_func: Callable[[Label], bool] = None,
|
||||||
subpatterns_func: Callable[[SubPattern], bool] = None,
|
subpatterns_func: Callable[[Union[SubPattern, GridRepetition]], bool] = None,
|
||||||
recursive: bool = False,
|
recursive: bool = False,
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
@ -172,9 +173,9 @@ class Pattern:
|
|||||||
return pat
|
return pat
|
||||||
|
|
||||||
def apply(self,
|
def apply(self,
|
||||||
func: Callable[['Pattern'], 'Pattern'],
|
func: Callable[[Optional['Pattern']], Optional['Pattern']],
|
||||||
memo: Dict[int, 'Pattern'] = None,
|
memo: Optional[Dict[int, Optional['Pattern']]] = None,
|
||||||
) -> 'Pattern':
|
) -> Optional['Pattern']:
|
||||||
"""
|
"""
|
||||||
Recursively apply func() to this pattern and any pattern it references.
|
Recursively apply func() to this pattern and any pattern it references.
|
||||||
func() is expected to take and return a Pattern.
|
func() is expected to take and return a Pattern.
|
||||||
@ -217,9 +218,9 @@ class Pattern:
|
|||||||
def dfs(self,
|
def dfs(self,
|
||||||
visit_before: visitor_function_t = None,
|
visit_before: visitor_function_t = None,
|
||||||
visit_after: visitor_function_t = None,
|
visit_after: visitor_function_t = None,
|
||||||
transform: numpy.ndarray or bool or None = False ,
|
transform: Union[numpy.ndarray, bool, None] = False,
|
||||||
memo: Dict = None,
|
memo: Optional[Dict] = None,
|
||||||
hierarchy: Tuple['Pattern'] = (),
|
hierarchy: Tuple['Pattern', ...] = (),
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Experimental convenience function.
|
Experimental convenience function.
|
||||||
@ -270,7 +271,7 @@ class Pattern:
|
|||||||
|
|
||||||
pat = self
|
pat = self
|
||||||
if visit_before is not None:
|
if visit_before is not None:
|
||||||
pat = visit_before(pat, hierarchy=hierarchy, memo=memo, transform=transform)
|
pat = visit_before(pat, hierarchy=hierarchy, memo=memo, transform=transform) # type: ignore
|
||||||
|
|
||||||
for subpattern in self.subpatterns:
|
for subpattern in self.subpatterns:
|
||||||
if transform is not False:
|
if transform is not False:
|
||||||
@ -293,12 +294,12 @@ class Pattern:
|
|||||||
hierarchy=hierarchy + (self,))
|
hierarchy=hierarchy + (self,))
|
||||||
|
|
||||||
if visit_after is not None:
|
if visit_after is not None:
|
||||||
pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform)
|
pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform) # type: ignore
|
||||||
return pat
|
return pat
|
||||||
|
|
||||||
def polygonize(self,
|
def polygonize(self,
|
||||||
poly_num_points: int = None,
|
poly_num_points: Optional[int] = None,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Calls `.to_polygons(...)` on all the shapes in this Pattern and any referenced patterns,
|
Calls `.to_polygons(...)` on all the shapes in this Pattern and any referenced patterns,
|
||||||
@ -349,7 +350,7 @@ class Pattern:
|
|||||||
def subpatternize(self,
|
def subpatternize(self,
|
||||||
recursive: bool = True,
|
recursive: bool = True,
|
||||||
norm_value: int = int(1e6),
|
norm_value: int = int(1e6),
|
||||||
exclude_types: Tuple[Shape] = (Polygon,)
|
exclude_types: Tuple[Type] = (Polygon,)
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Iterates through this `Pattern` and all referenced `Pattern`s. Within each `Pattern`, it iterates
|
Iterates through this `Pattern` and all referenced `Pattern`s. Within each `Pattern`, it iterates
|
||||||
@ -387,7 +388,7 @@ class Pattern:
|
|||||||
# Create a dict which uses the label tuple from `.normalized_form()` as a key, and which
|
# Create a dict which uses the label tuple from `.normalized_form()` as a key, and which
|
||||||
# stores `(function_to_create_normalized_shape, [(index_in_shapes, values), ...])`, where
|
# stores `(function_to_create_normalized_shape, [(index_in_shapes, values), ...])`, where
|
||||||
# values are the `(offset, scale, rotation, dose)` values as calculated by `.normalized_form()`
|
# values are the `(offset, scale, rotation, dose)` values as calculated by `.normalized_form()`
|
||||||
shape_table = defaultdict(lambda: [None, list()])
|
shape_table: MutableMapping[Tuple, List] = defaultdict(lambda: [None, list()])
|
||||||
for i, shape in enumerate(self.shapes):
|
for i, shape in enumerate(self.shapes):
|
||||||
if not any((isinstance(shape, t) for t in exclude_types)):
|
if not any((isinstance(shape, t) for t in exclude_types)):
|
||||||
label, values, func = shape.normalized_form(norm_value)
|
label, values, func = shape.normalized_form(norm_value)
|
||||||
@ -429,9 +430,9 @@ class Pattern:
|
|||||||
is of the form `[[x0, y0], [x1, y1],...]`.
|
is of the form `[[x0, y0], [x1, y1],...]`.
|
||||||
"""
|
"""
|
||||||
pat = self.deepcopy().deepunlock().polygonize().flatten()
|
pat = self.deepcopy().deepunlock().polygonize().flatten()
|
||||||
return [shape.vertices + shape.offset for shape in pat.shapes]
|
return [shape.vertices + shape.offset for shape in pat.shapes] # type: ignore # mypy can't figure out that shapes are all Polygons now
|
||||||
|
|
||||||
def referenced_patterns_by_id(self) -> Dict[int, 'Pattern']:
|
def referenced_patterns_by_id(self) -> Dict[int, Optional['Pattern']]:
|
||||||
"""
|
"""
|
||||||
Create a dictionary with `{id(pat): pat}` for all Pattern objects referenced by this
|
Create a dictionary with `{id(pat): pat}` for all Pattern objects referenced by this
|
||||||
Pattern (operates recursively on all referenced Patterns as well)
|
Pattern (operates recursively on all referenced Patterns as well)
|
||||||
@ -447,7 +448,7 @@ class Pattern:
|
|||||||
ids.update(subpat.pattern.referenced_patterns_by_id())
|
ids.update(subpat.pattern.referenced_patterns_by_id())
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def referenced_patterns_by_name(self) -> List[Tuple[str, 'Pattern']]:
|
def referenced_patterns_by_name(self) -> List[Tuple[Optional[str], Optional['Pattern']]]:
|
||||||
"""
|
"""
|
||||||
Create a list of `(pat.name, pat)` tuples for all Pattern objects referenced by this
|
Create a list of `(pat.name, pat)` tuples for all Pattern objects referenced by this
|
||||||
Pattern (operates recursively on all referenced Patterns as well).
|
Pattern (operates recursively on all referenced Patterns as well).
|
||||||
@ -507,7 +508,7 @@ class Pattern:
|
|||||||
"""
|
"""
|
||||||
subpatterns = copy.deepcopy(self.subpatterns)
|
subpatterns = copy.deepcopy(self.subpatterns)
|
||||||
self.subpatterns = []
|
self.subpatterns = []
|
||||||
shape_counts = {}
|
shape_counts: Dict[Tuple, int] = {}
|
||||||
for subpat in subpatterns:
|
for subpat in subpatterns:
|
||||||
if subpat.pattern is None:
|
if subpat.pattern is None:
|
||||||
continue
|
continue
|
||||||
@ -839,7 +840,7 @@ class Pattern:
|
|||||||
pyplot.show()
|
pyplot.show()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_toplevel(patterns: List['Pattern']) -> List['Pattern']:
|
def find_toplevel(patterns: Iterable['Pattern']) -> List['Pattern']:
|
||||||
"""
|
"""
|
||||||
Given a list of Pattern objects, return those that are not referenced by
|
Given a list of Pattern objects, return those that are not referenced by
|
||||||
any other pattern.
|
any other pattern.
|
||||||
@ -863,7 +864,7 @@ class Pattern:
|
|||||||
return memo
|
return memo
|
||||||
|
|
||||||
patterns = set(patterns)
|
patterns = set(patterns)
|
||||||
not_toplevel = set()
|
not_toplevel: Set['Pattern'] = set()
|
||||||
for pattern in patterns:
|
for pattern in patterns:
|
||||||
not_toplevel |= get_children(pattern, not_toplevel)
|
not_toplevel |= get_children(pattern, not_toplevel)
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ from numpy import pi
|
|||||||
from .error import PatternError, PatternLockedError
|
from .error import PatternError, PatternLockedError
|
||||||
from .utils import is_scalar, rotation_matrix_2d, vector2
|
from .utils import is_scalar, rotation_matrix_2d, vector2
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import Pattern
|
||||||
|
|
||||||
|
|
||||||
# TODO need top-level comment about what order rotation/scale/offset/mirror/array are applied
|
# TODO need top-level comment about what order rotation/scale/offset/mirror/array are applied
|
||||||
@ -51,7 +53,7 @@ class GridRepetition:
|
|||||||
_scale: float
|
_scale: float
|
||||||
""" Scaling factor applied to individual instances in the grid (not the grid vectors) """
|
""" Scaling factor applied to individual instances in the grid (not the grid vectors) """
|
||||||
|
|
||||||
_mirrored: List[bool]
|
_mirrored: numpy.ndarray # ndarray[bool]
|
||||||
""" Whether to mirror individual instances across the x and y axes
|
""" Whether to mirror individual instances across the x and y axes
|
||||||
(Applies to individual instances in the grid, not the grid vectors)
|
(Applies to individual instances in the grid, not the grid vectors)
|
||||||
"""
|
"""
|
||||||
@ -64,7 +66,7 @@ class GridRepetition:
|
|||||||
_a_count: int
|
_a_count: int
|
||||||
""" Number of instances along the direction specified by the `a_vector` """
|
""" Number of instances along the direction specified by the `a_vector` """
|
||||||
|
|
||||||
_b_vector: numpy.ndarray or None
|
_b_vector: Optional[numpy.ndarray]
|
||||||
""" Vector `[x, y]` specifying a second lattice vector for the grid.
|
""" Vector `[x, y]` specifying a second lattice vector for the grid.
|
||||||
Specifies center-to-center spacing between adjacent elements.
|
Specifies center-to-center spacing between adjacent elements.
|
||||||
Can be `None` for a 1D array.
|
Can be `None` for a 1D array.
|
||||||
@ -80,14 +82,14 @@ class GridRepetition:
|
|||||||
""" If `True`, disallows changes to the GridRepetition """
|
""" If `True`, disallows changes to the GridRepetition """
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
pattern: 'Pattern',
|
pattern: Optional['Pattern'],
|
||||||
a_vector: numpy.ndarray,
|
a_vector: numpy.ndarray,
|
||||||
a_count: int,
|
a_count: int,
|
||||||
b_vector: numpy.ndarray = None,
|
b_vector: Optional[numpy.ndarray] = None,
|
||||||
b_count: int = 1,
|
b_count: int = 1,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: List[bool] = None,
|
mirrored: Optional[Sequence[bool]] = None,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
scale: float = 1.0,
|
scale: float = 1.0,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
@ -155,7 +157,7 @@ class GridRepetition:
|
|||||||
locked=self.locked)
|
locked=self.locked)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'GridReptition':
|
def __deepcopy__(self, memo: Dict = None) -> 'GridRepetition':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self).unlock()
|
new = copy.copy(self).unlock()
|
||||||
new.pattern = copy.deepcopy(self.pattern, memo)
|
new.pattern = copy.deepcopy(self.pattern, memo)
|
||||||
@ -230,11 +232,11 @@ class GridRepetition:
|
|||||||
|
|
||||||
# Mirrored property
|
# Mirrored property
|
||||||
@property
|
@property
|
||||||
def mirrored(self) -> List[bool]:
|
def mirrored(self) -> numpy.ndarray: # ndarray[bool]
|
||||||
return self._mirrored
|
return self._mirrored
|
||||||
|
|
||||||
@mirrored.setter
|
@mirrored.setter
|
||||||
def mirrored(self, val: List[bool]):
|
def mirrored(self, val: Sequence[bool]):
|
||||||
if is_scalar(val):
|
if is_scalar(val):
|
||||||
raise PatternError('Mirrored must be a 2-element list of booleans')
|
raise PatternError('Mirrored must be a 2-element list of booleans')
|
||||||
self._mirrored = numpy.array(val, dtype=bool, copy=True)
|
self._mirrored = numpy.array(val, dtype=bool, copy=True)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Optional, Sequence
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
import numpy
|
import numpy
|
||||||
@ -32,10 +32,10 @@ class Arc(Shape):
|
|||||||
_width: float
|
_width: float
|
||||||
""" Width of the arc """
|
""" Width of the arc """
|
||||||
|
|
||||||
poly_num_points: int
|
poly_num_points: Optional[int]
|
||||||
""" Sets the default number of points for `.polygonize()` """
|
""" Sets the default number of points for `.polygonize()` """
|
||||||
|
|
||||||
poly_max_arclen: float
|
poly_max_arclen: Optional[float]
|
||||||
""" Sets the default max segement length for `.polygonize()` """
|
""" Sets the default max segement length for `.polygonize()` """
|
||||||
|
|
||||||
# radius properties
|
# radius properties
|
||||||
@ -77,7 +77,7 @@ class Arc(Shape):
|
|||||||
|
|
||||||
# arc start/stop angle properties
|
# arc start/stop angle properties
|
||||||
@property
|
@property
|
||||||
def angles(self) -> vector2:
|
def angles(self) -> numpy.ndarray: #ndarray[float]
|
||||||
"""
|
"""
|
||||||
Return the start and stop angles `[a_start, a_stop]`.
|
Return the start and stop angles `[a_start, a_stop]`.
|
||||||
Angles are measured from x-axis after rotation
|
Angles are measured from x-axis after rotation
|
||||||
@ -150,11 +150,11 @@ class Arc(Shape):
|
|||||||
radii: vector2,
|
radii: vector2,
|
||||||
angles: vector2,
|
angles: vector2,
|
||||||
width: float,
|
width: float,
|
||||||
poly_num_points: int = DEFAULT_POLY_NUM_POINTS,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
@ -182,8 +182,8 @@ class Arc(Shape):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(self,
|
||||||
poly_num_points: int = None,
|
poly_num_points: Optional[int] = None,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
) -> List[Polygon]:
|
) -> List[Polygon]:
|
||||||
if poly_num_points is None:
|
if poly_num_points is None:
|
||||||
poly_num_points = self.poly_num_points
|
poly_num_points = self.poly_num_points
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Dict
|
from typing import List, Dict, Optional
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
@ -16,10 +16,10 @@ class Circle(Shape):
|
|||||||
_radius: float
|
_radius: float
|
||||||
""" Circle radius """
|
""" Circle radius """
|
||||||
|
|
||||||
poly_num_points: int
|
poly_num_points: Optional[int]
|
||||||
""" Sets the default number of points for `.polygonize()` """
|
""" Sets the default number of points for `.polygonize()` """
|
||||||
|
|
||||||
poly_max_arclen: float
|
poly_max_arclen: Optional[float]
|
||||||
""" Sets the default max segement length for `.polygonize()` """
|
""" Sets the default max segement length for `.polygonize()` """
|
||||||
|
|
||||||
# radius property
|
# radius property
|
||||||
@ -40,8 +40,8 @@ class Circle(Shape):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
radius: float,
|
radius: float,
|
||||||
poly_num_points: int = DEFAULT_POLY_NUM_POINTS,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
@ -64,8 +64,8 @@ class Circle(Shape):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(self,
|
||||||
poly_num_points: int = None,
|
poly_num_points: Optional[int] = None,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
) -> List[Polygon]:
|
) -> List[Polygon]:
|
||||||
if poly_num_points is None:
|
if poly_num_points is None:
|
||||||
poly_num_points = self.poly_num_points
|
poly_num_points = self.poly_num_points
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Sequence, Optional
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
import numpy
|
import numpy
|
||||||
@ -22,10 +22,10 @@ class Ellipse(Shape):
|
|||||||
_rotation: float
|
_rotation: float
|
||||||
""" Angle from x-axis to first radius (ccw, radians) """
|
""" Angle from x-axis to first radius (ccw, radians) """
|
||||||
|
|
||||||
poly_num_points: int
|
poly_num_points: Optional[int]
|
||||||
""" Sets the default number of points for `.polygonize()` """
|
""" Sets the default number of points for `.polygonize()` """
|
||||||
|
|
||||||
poly_max_arclen: float
|
poly_max_arclen: Optional[float]
|
||||||
""" Sets the default max segement length for `.polygonize()` """
|
""" Sets the default max segement length for `.polygonize()` """
|
||||||
|
|
||||||
# radius properties
|
# radius properties
|
||||||
@ -85,11 +85,11 @@ class Ellipse(Shape):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
radii: vector2,
|
radii: vector2,
|
||||||
poly_num_points: int = DEFAULT_POLY_NUM_POINTS,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
@ -114,8 +114,8 @@ class Ellipse(Shape):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(self,
|
||||||
poly_num_points: int = None,
|
poly_num_points: Optional[int] = None,
|
||||||
poly_max_arclen: float = None,
|
poly_max_arclen: Optional[float] = None,
|
||||||
) -> List[Polygon]:
|
) -> List[Polygon]:
|
||||||
if poly_num_points is None:
|
if poly_num_points is None:
|
||||||
poly_num_points = self.poly_num_points
|
poly_num_points = self.poly_num_points
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Optional, Sequence
|
||||||
import copy
|
import copy
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import numpy
|
import numpy
|
||||||
@ -28,8 +28,8 @@ class Path(Shape):
|
|||||||
__slots__ = ('_vertices', '_width', '_cap', '_cap_extensions')
|
__slots__ = ('_vertices', '_width', '_cap', '_cap_extensions')
|
||||||
_vertices: numpy.ndarray
|
_vertices: numpy.ndarray
|
||||||
_width: float
|
_width: float
|
||||||
_cap_extensions: numpy.ndarray or None
|
|
||||||
_cap: PathCap
|
_cap: PathCap
|
||||||
|
_cap_extensions: Optional[numpy.ndarray]
|
||||||
|
|
||||||
Cap = PathCap
|
Cap = PathCap
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ class Path(Shape):
|
|||||||
|
|
||||||
# cap_extensions property
|
# cap_extensions property
|
||||||
@property
|
@property
|
||||||
def cap_extensions(self) -> numpy.ndarray or None:
|
def cap_extensions(self) -> Optional[numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Path end-cap extension
|
Path end-cap extension
|
||||||
|
|
||||||
@ -144,11 +144,11 @@ class Path(Shape):
|
|||||||
cap_extensions: numpy.ndarray = None,
|
cap_extensions: numpy.ndarray = None,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
) -> 'Path':
|
):
|
||||||
self.unlock()
|
self.unlock()
|
||||||
self._cap_extensions = None # Since .cap setter might access it
|
self._cap_extensions = None # Since .cap setter might access it
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ class Path(Shape):
|
|||||||
cap_extensions = None,
|
cap_extensions = None,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
) -> 'Path':
|
) -> 'Path':
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Optional, Sequence
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
@ -71,7 +71,7 @@ class Polygon(Shape):
|
|||||||
vertices: numpy.ndarray,
|
vertices: numpy.ndarray,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
@ -86,7 +86,7 @@ class Polygon(Shape):
|
|||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
self.locked = locked
|
self.locked = locked
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Dict = None) -> 'Polygon':
|
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Polygon':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self).unlock()
|
new = copy.copy(self).unlock()
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -154,14 +154,14 @@ class Polygon(Shape):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rect(xmin: float = None,
|
def rect(xmin: Optional[float] = None,
|
||||||
xctr: float = None,
|
xctr: Optional[float] = None,
|
||||||
xmax: float = None,
|
xmax: Optional[float] = None,
|
||||||
lx: float = None,
|
lx: Optional[float] = None,
|
||||||
ymin: float = None,
|
ymin: Optional[float] = None,
|
||||||
yctr: float = None,
|
yctr: Optional[float] = None,
|
||||||
ymax: float = None,
|
ymax: Optional[float] = None,
|
||||||
ly: float = None,
|
ly: Optional[float] = None,
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
) -> 'Polygon':
|
) -> 'Polygon':
|
||||||
@ -188,11 +188,17 @@ class Polygon(Shape):
|
|||||||
"""
|
"""
|
||||||
if lx is None:
|
if lx is None:
|
||||||
if xctr is None:
|
if xctr is None:
|
||||||
|
assert(xmin is not None)
|
||||||
|
assert(xmax is not None)
|
||||||
xctr = 0.5 * (xmax + xmin)
|
xctr = 0.5 * (xmax + xmin)
|
||||||
lx = xmax - xmin
|
lx = xmax - xmin
|
||||||
elif xmax is None:
|
elif xmax is None:
|
||||||
|
assert(xmin is not None)
|
||||||
|
assert(xctr is not None)
|
||||||
lx = 2 * (xctr - xmin)
|
lx = 2 * (xctr - xmin)
|
||||||
elif xmin is None:
|
elif xmin is None:
|
||||||
|
assert(xctr is not None)
|
||||||
|
assert(xmax is not None)
|
||||||
lx = 2 * (xmax - xctr)
|
lx = 2 * (xmax - xctr)
|
||||||
else:
|
else:
|
||||||
raise PatternError('Two of xmin, xctr, xmax, lx must be None!')
|
raise PatternError('Two of xmin, xctr, xmax, lx must be None!')
|
||||||
@ -200,19 +206,29 @@ class Polygon(Shape):
|
|||||||
if xctr is not None:
|
if xctr is not None:
|
||||||
pass
|
pass
|
||||||
elif xmax is None:
|
elif xmax is None:
|
||||||
|
assert(xmin is not None)
|
||||||
|
assert(lx is not None)
|
||||||
xctr = xmin + 0.5 * lx
|
xctr = xmin + 0.5 * lx
|
||||||
elif xmin is None:
|
elif xmin is None:
|
||||||
|
assert(xmax is not None)
|
||||||
|
assert(lx is not None)
|
||||||
xctr = xmax - 0.5 * lx
|
xctr = xmax - 0.5 * lx
|
||||||
else:
|
else:
|
||||||
raise PatternError('Two of xmin, xctr, xmax, lx must be None!')
|
raise PatternError('Two of xmin, xctr, xmax, lx must be None!')
|
||||||
|
|
||||||
if ly is None:
|
if ly is None:
|
||||||
if yctr is None:
|
if yctr is None:
|
||||||
|
assert(ymin is not None)
|
||||||
|
assert(ymax is not None)
|
||||||
yctr = 0.5 * (ymax + ymin)
|
yctr = 0.5 * (ymax + ymin)
|
||||||
ly = ymax - ymin
|
ly = ymax - ymin
|
||||||
elif ymax is None:
|
elif ymax is None:
|
||||||
|
assert(ymin is not None)
|
||||||
|
assert(yctr is not None)
|
||||||
ly = 2 * (yctr - ymin)
|
ly = 2 * (yctr - ymin)
|
||||||
elif ymin is None:
|
elif ymin is None:
|
||||||
|
assert(yctr is not None)
|
||||||
|
assert(ymax is not None)
|
||||||
ly = 2 * (ymax - yctr)
|
ly = 2 * (ymax - yctr)
|
||||||
else:
|
else:
|
||||||
raise PatternError('Two of ymin, yctr, ymax, ly must be None!')
|
raise PatternError('Two of ymin, yctr, ymax, ly must be None!')
|
||||||
@ -220,8 +236,12 @@ class Polygon(Shape):
|
|||||||
if yctr is not None:
|
if yctr is not None:
|
||||||
pass
|
pass
|
||||||
elif ymax is None:
|
elif ymax is None:
|
||||||
|
assert(ymin is not None)
|
||||||
|
assert(ly is not None)
|
||||||
yctr = ymin + 0.5 * ly
|
yctr = ymin + 0.5 * ly
|
||||||
elif ymin is None:
|
elif ymin is None:
|
||||||
|
assert(ly is not None)
|
||||||
|
assert(ymax is not None)
|
||||||
yctr = ymax - 0.5 * ly
|
yctr = ymax - 0.5 * ly
|
||||||
else:
|
else:
|
||||||
raise PatternError('Two of ymin, yctr, ymax, ly must be None!')
|
raise PatternError('Two of ymin, yctr, ymax, ly must be None!')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Callable
|
from typing import List, Tuple, Callable, TypeVar, Optional, TYPE_CHECKING
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
@ -6,6 +6,8 @@ import numpy
|
|||||||
from ..error import PatternError, PatternLockedError
|
from ..error import PatternError, PatternLockedError
|
||||||
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t
|
from ..utils import is_scalar, rotation_matrix_2d, vector2, layer_t
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import Polygon
|
||||||
|
|
||||||
|
|
||||||
# Type definitions
|
# Type definitions
|
||||||
@ -18,6 +20,9 @@ normalized_shape_tuple = Tuple[Tuple,
|
|||||||
DEFAULT_POLY_NUM_POINTS = 24
|
DEFAULT_POLY_NUM_POINTS = 24
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar('T', bound='Shape')
|
||||||
|
|
||||||
|
|
||||||
class Shape(metaclass=ABCMeta):
|
class Shape(metaclass=ABCMeta):
|
||||||
"""
|
"""
|
||||||
Abstract class specifying functions common to all shapes.
|
Abstract class specifying functions common to all shapes.
|
||||||
@ -53,7 +58,10 @@ class Shape(metaclass=ABCMeta):
|
|||||||
|
|
||||||
# --- Abstract methods
|
# --- Abstract methods
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def to_polygons(self, num_vertices: int, max_arclen: float) -> List['Polygon']:
|
def to_polygons(self,
|
||||||
|
num_vertices: Optional[int] = None,
|
||||||
|
max_arclen: Optional[float] = None,
|
||||||
|
) -> List['Polygon']:
|
||||||
"""
|
"""
|
||||||
Returns a list of polygons which approximate the shape.
|
Returns a list of polygons which approximate the shape.
|
||||||
|
|
||||||
@ -77,7 +85,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def rotate(self, theta: float) -> 'Shape':
|
def rotate(self: T, theta: float) -> T:
|
||||||
"""
|
"""
|
||||||
Rotate the shape around its origin (0, 0), ignoring its offset.
|
Rotate the shape around its origin (0, 0), ignoring its offset.
|
||||||
|
|
||||||
@ -90,7 +98,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def mirror(self, axis: int) -> 'Shape':
|
def mirror(self: T, axis: int) -> T:
|
||||||
"""
|
"""
|
||||||
Mirror the shape across an axis.
|
Mirror the shape across an axis.
|
||||||
|
|
||||||
@ -104,7 +112,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def scale_by(self, c: float) -> 'Shape':
|
def scale_by(self: T, c: float) -> T:
|
||||||
"""
|
"""
|
||||||
Scale the shape's size (eg. radius, for a circle) by a constant factor.
|
Scale the shape's size (eg. radius, for a circle) by a constant factor.
|
||||||
|
|
||||||
@ -117,7 +125,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def normalized_form(self, norm_value: int) -> normalized_shape_tuple:
|
def normalized_form(self: T, norm_value: int) -> normalized_shape_tuple:
|
||||||
"""
|
"""
|
||||||
Writes the shape in a standardized notation, with offset, scale, rotation, and dose
|
Writes the shape in a standardized notation, with offset, scale, rotation, and dose
|
||||||
information separated out from the remaining values.
|
information separated out from the remaining values.
|
||||||
@ -187,7 +195,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
self._dose = val
|
self._dose = val
|
||||||
|
|
||||||
# ---- Non-abstract methods
|
# ---- Non-abstract methods
|
||||||
def copy(self) -> 'Shape':
|
def copy(self: T) -> T:
|
||||||
"""
|
"""
|
||||||
Returns a deep copy of the shape.
|
Returns a deep copy of the shape.
|
||||||
|
|
||||||
@ -196,7 +204,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
def translate(self, offset: vector2) -> 'Shape':
|
def translate(self: T, offset: vector2) -> T:
|
||||||
"""
|
"""
|
||||||
Translate the shape by the given offset
|
Translate the shape by the given offset
|
||||||
|
|
||||||
@ -209,7 +217,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
self.offset += offset
|
self.offset += offset
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def rotate_around(self, pivot: vector2, rotation: float) -> 'Shape':
|
def rotate_around(self: T, pivot: vector2, rotation: float) -> T:
|
||||||
"""
|
"""
|
||||||
Rotate the shape around a point.
|
Rotate the shape around a point.
|
||||||
|
|
||||||
@ -428,7 +436,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
|
|
||||||
return manhattan_polygons
|
return manhattan_polygons
|
||||||
|
|
||||||
def lock(self) -> 'Shape':
|
def lock(self: T) -> T:
|
||||||
"""
|
"""
|
||||||
Lock the Shape, disallowing further changes
|
Lock the Shape, disallowing further changes
|
||||||
|
|
||||||
@ -438,7 +446,7 @@ class Shape(metaclass=ABCMeta):
|
|||||||
object.__setattr__(self, 'locked', True)
|
object.__setattr__(self, 'locked', True)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def unlock(self) -> 'Shape':
|
def unlock(self: T) -> T:
|
||||||
"""
|
"""
|
||||||
Unlock the Shape
|
Unlock the Shape
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict
|
from typing import List, Tuple, Dict, Sequence, Optional, MutableSequence
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi, inf
|
from numpy import pi, inf
|
||||||
@ -21,7 +21,7 @@ class Text(Shape):
|
|||||||
_string: str
|
_string: str
|
||||||
_height: float
|
_height: float
|
||||||
_rotation: float
|
_rotation: float
|
||||||
_mirrored: List[str]
|
_mirrored: numpy.ndarray #ndarray[bool]
|
||||||
font_path: str
|
font_path: str
|
||||||
|
|
||||||
# vertices property
|
# vertices property
|
||||||
@ -57,11 +57,11 @@ class Text(Shape):
|
|||||||
|
|
||||||
# Mirrored property
|
# Mirrored property
|
||||||
@property
|
@property
|
||||||
def mirrored(self) -> List[bool]:
|
def mirrored(self) -> numpy.ndarray: #ndarray[bool]
|
||||||
return self._mirrored
|
return self._mirrored
|
||||||
|
|
||||||
@mirrored.setter
|
@mirrored.setter
|
||||||
def mirrored(self, val: List[bool]):
|
def mirrored(self, val: Sequence[bool]):
|
||||||
if is_scalar(val):
|
if is_scalar(val):
|
||||||
raise PatternError('Mirrored must be a 2-element list of booleans')
|
raise PatternError('Mirrored must be a 2-element list of booleans')
|
||||||
self._mirrored = numpy.ndarray(val, dtype=bool, copy=True)
|
self._mirrored = numpy.ndarray(val, dtype=bool, copy=True)
|
||||||
@ -72,7 +72,7 @@ class Text(Shape):
|
|||||||
font_path: str,
|
font_path: str,
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: Tuple[bool] = (False, False),
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
locked: bool = False,
|
locked: bool = False,
|
||||||
@ -98,11 +98,11 @@ class Text(Shape):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(self,
|
||||||
poly_num_points: int = None, # unused
|
poly_num_points: Optional[int] = None, # unused
|
||||||
poly_max_arclen: float = None, # unused
|
poly_max_arclen: Optional[float] = None, # unused
|
||||||
) -> List[Polygon]:
|
) -> List[Polygon]:
|
||||||
all_polygons = []
|
all_polygons = []
|
||||||
total_advance = 0
|
total_advance = 0.0
|
||||||
for char in self.string:
|
for char in self.string:
|
||||||
raw_polys, advance = get_char_as_polygons(self.font_path, char)
|
raw_polys, advance = get_char_as_polygons(self.font_path, char)
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ def get_char_as_polygons(font_path: str,
|
|||||||
tags = outline.tags[start:end + 1]
|
tags = outline.tags[start:end + 1]
|
||||||
tags.append(tags[0])
|
tags.append(tags[0])
|
||||||
|
|
||||||
segments = []
|
segments: List[List[List[float]]] = []
|
||||||
for j, point in enumerate(points):
|
for j, point in enumerate(points):
|
||||||
# If we already have a segment, add this point to it
|
# If we already have a segment, add this point to it
|
||||||
if j > 0:
|
if j > 0:
|
||||||
|
@ -12,6 +12,8 @@ from numpy import pi
|
|||||||
from .error import PatternError, PatternLockedError
|
from .error import PatternError, PatternLockedError
|
||||||
from .utils import is_scalar, rotation_matrix_2d, vector2
|
from .utils import is_scalar, rotation_matrix_2d, vector2
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import Pattern
|
||||||
|
|
||||||
|
|
||||||
class SubPattern:
|
class SubPattern:
|
||||||
@ -43,7 +45,7 @@ class SubPattern:
|
|||||||
_scale: float
|
_scale: float
|
||||||
""" scale factor for the instance """
|
""" scale factor for the instance """
|
||||||
|
|
||||||
_mirrored: List[bool]
|
_mirrored: numpy.ndarray # ndarray[bool]
|
||||||
""" Whether to mirror the instanc across the x and/or y axes. """
|
""" Whether to mirror the instanc across the x and/or y axes. """
|
||||||
|
|
||||||
identifier: Tuple
|
identifier: Tuple
|
||||||
@ -58,11 +60,11 @@ class SubPattern:
|
|||||||
pattern: Optional['Pattern'],
|
pattern: Optional['Pattern'],
|
||||||
offset: vector2 = (0.0, 0.0),
|
offset: vector2 = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: List[bool] = None,
|
mirrored: Optional[Sequence[bool]] = None,
|
||||||
dose: float = 1.0,
|
dose: float = 1.0,
|
||||||
scale: float = 1.0,
|
scale: float = 1.0,
|
||||||
locked: bool = False):
|
locked: bool = False):
|
||||||
self.unlock()
|
object.__setattr__(self, 'locked', False)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
@ -161,11 +163,11 @@ class SubPattern:
|
|||||||
|
|
||||||
# Mirrored property
|
# Mirrored property
|
||||||
@property
|
@property
|
||||||
def mirrored(self) -> List[bool]:
|
def mirrored(self) -> numpy.ndarray: # ndarray[bool]
|
||||||
return self._mirrored
|
return self._mirrored
|
||||||
|
|
||||||
@mirrored.setter
|
@mirrored.setter
|
||||||
def mirrored(self, val: List[bool]):
|
def mirrored(self, val: Sequence[bool]):
|
||||||
if is_scalar(val):
|
if is_scalar(val):
|
||||||
raise PatternError('Mirrored must be a 2-element list of booleans')
|
raise PatternError('Mirrored must be a 2-element list of booleans')
|
||||||
self._mirrored = numpy.array(val, dtype=bool, copy=True)
|
self._mirrored = numpy.array(val, dtype=bool, copy=True)
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
Various helper functions
|
Various helper functions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Union, Tuple
|
from typing import Any, Union, Tuple, Sequence
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
# Type definitions
|
# Type definitions
|
||||||
vector2 = Union[numpy.ndarray, Tuple[float, float]]
|
vector2 = Union[numpy.ndarray, Tuple[float, float], Sequence[float]]
|
||||||
layer_t = Union[int, Tuple[int, int]]
|
layer_t = Union[int, Tuple[int, int]]
|
||||||
|
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ def rotation_matrix_2d(theta: float) -> numpy.ndarray:
|
|||||||
[numpy.sin(theta), +numpy.cos(theta)]])
|
[numpy.sin(theta), +numpy.cos(theta)]])
|
||||||
|
|
||||||
|
|
||||||
def normalize_mirror(mirrored: Tuple[bool, bool]) -> Tuple[bool, float]:
|
def normalize_mirror(mirrored: Sequence[bool]) -> Tuple[bool, float]:
|
||||||
"""
|
"""
|
||||||
Converts 0-2 mirror operations `(mirror_across_x_axis, mirror_across_y_axis)`
|
Converts 0-2 mirror operations `(mirror_across_x_axis, mirror_across_y_axis)`
|
||||||
into 0-1 mirror operations and a rotation
|
into 0-1 mirror operations and a rotation
|
||||||
|
Loading…
Reference in New Issue
Block a user