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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user