reformat some multiline arg lists and add missing 'None' return types
This commit is contained in:
parent
250107e41b
commit
89f327ba37
@ -167,12 +167,13 @@ class Device(Copyable, Mirrorable):
|
|||||||
_dead: bool
|
_dead: bool
|
||||||
""" If True, plug()/place() are skipped (for debugging)"""
|
""" If True, plug()/place() are skipped (for debugging)"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
pattern: Optional[Pattern] = None,
|
self,
|
||||||
ports: Optional[Dict[str, Port]] = None,
|
pattern: Optional[Pattern] = None,
|
||||||
*,
|
ports: Optional[Dict[str, Port]] = None,
|
||||||
name: Optional[str] = None,
|
*,
|
||||||
) -> None:
|
name: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
If `ports` is `None`, two default ports ('A' and 'B') are created.
|
If `ports` is `None`, two default ports ('A' and 'B') are created.
|
||||||
Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0
|
Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0
|
||||||
@ -218,10 +219,11 @@ class Device(Copyable, Mirrorable):
|
|||||||
else:
|
else:
|
||||||
return {k: self.ports[k] for k in key}
|
return {k: self.ports[k] for k in key}
|
||||||
|
|
||||||
def rename_ports(self: D,
|
def rename_ports(
|
||||||
mapping: Dict[str, Optional[str]],
|
self: D,
|
||||||
overwrite: bool = False,
|
mapping: Dict[str, Optional[str]],
|
||||||
) -> D:
|
overwrite: bool = False,
|
||||||
|
) -> D:
|
||||||
"""
|
"""
|
||||||
Renames ports as specified by `mapping`.
|
Renames ports as specified by `mapping`.
|
||||||
Ports can be explicitly deleted by mapping them to `None`.
|
Ports can be explicitly deleted by mapping them to `None`.
|
||||||
@ -248,11 +250,12 @@ class Device(Copyable, Mirrorable):
|
|||||||
self.ports.update(renamed) # type: ignore
|
self.ports.update(renamed) # type: ignore
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def check_ports(self: D,
|
def check_ports(
|
||||||
other_names: Iterable[str],
|
self: D,
|
||||||
map_in: Optional[Dict[str, str]] = None,
|
other_names: Iterable[str],
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
map_in: Optional[Dict[str, str]] = None,
|
||||||
) -> D:
|
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||||
|
) -> D:
|
||||||
"""
|
"""
|
||||||
Given the provided port mappings, check that:
|
Given the provided port mappings, check that:
|
||||||
- All of the ports specified in the mappings exist
|
- All of the ports specified in the mappings exist
|
||||||
@ -332,12 +335,13 @@ class Device(Copyable, Mirrorable):
|
|||||||
new = Device(pat, ports=self.ports)
|
new = Device(pat, ports=self.ports)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def as_interface(self,
|
def as_interface(
|
||||||
name: str,
|
self,
|
||||||
in_prefix: str = 'in_',
|
name: str,
|
||||||
out_prefix: str = '',
|
in_prefix: str = 'in_',
|
||||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None
|
out_prefix: str = '',
|
||||||
) -> 'Device':
|
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None
|
||||||
|
) -> 'Device':
|
||||||
"""
|
"""
|
||||||
Begin building a new device based on all or some of the ports in the
|
Begin building a new device based on all or some of the ports in the
|
||||||
current device. Do not include the current device; instead use it
|
current device. Do not include the current device; instead use it
|
||||||
@ -406,15 +410,16 @@ class Device(Copyable, Mirrorable):
|
|||||||
new = Device(name=name, ports={**ports_in, **ports_out})
|
new = Device(name=name, ports={**ports_in, **ports_out})
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def plug(self: D,
|
def plug(
|
||||||
other: O,
|
self: D,
|
||||||
map_in: Dict[str, str],
|
other: O,
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
map_in: Dict[str, str],
|
||||||
*,
|
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
*,
|
||||||
inherit_name: bool = True,
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
set_rotation: Optional[bool] = None,
|
inherit_name: bool = True,
|
||||||
) -> D:
|
set_rotation: Optional[bool] = None,
|
||||||
|
) -> D:
|
||||||
"""
|
"""
|
||||||
Instantiate the device `other` into the current device, connecting
|
Instantiate the device `other` into the current device, connecting
|
||||||
the ports specified by `map_in` and renaming the unconnected
|
the ports specified by `map_in` and renaming the unconnected
|
||||||
@ -495,16 +500,17 @@ class Device(Copyable, Mirrorable):
|
|||||||
mirrored=mirrored, port_map=map_out, skip_port_check=True)
|
mirrored=mirrored, port_map=map_out, skip_port_check=True)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def place(self: D,
|
def place(
|
||||||
other: O,
|
self: D,
|
||||||
*,
|
other: O,
|
||||||
offset: vector2 = (0, 0),
|
*,
|
||||||
rotation: float = 0,
|
offset: vector2 = (0, 0),
|
||||||
pivot: vector2 = (0, 0),
|
rotation: float = 0,
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
pivot: vector2 = (0, 0),
|
||||||
port_map: Optional[Dict[str, Optional[str]]] = None,
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
skip_port_check: bool = False,
|
port_map: Optional[Dict[str, Optional[str]]] = None,
|
||||||
) -> D:
|
skip_port_check: bool = False,
|
||||||
|
) -> D:
|
||||||
"""
|
"""
|
||||||
Instantiate the device `other` into the current device, adding its
|
Instantiate the device `other` into the current device, adding its
|
||||||
ports to those of the current device (but not connecting any ports).
|
ports to those of the current device (but not connecting any ports).
|
||||||
@ -572,13 +578,14 @@ class Device(Copyable, Mirrorable):
|
|||||||
self.pattern.subpatterns.append(sp)
|
self.pattern.subpatterns.append(sp)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def find_transform(self: D,
|
def find_transform(
|
||||||
other: O,
|
self: D,
|
||||||
map_in: Dict[str, str],
|
other: O,
|
||||||
*,
|
map_in: Dict[str, str],
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
*,
|
||||||
set_rotation: Optional[bool] = None,
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
) -> Tuple[numpy.ndarray, float, numpy.ndarray]:
|
set_rotation: Optional[bool] = None,
|
||||||
|
) -> Tuple[numpy.ndarray, float, numpy.ndarray]:
|
||||||
"""
|
"""
|
||||||
Given a device `other` and a mapping `map_in` specifying port connections,
|
Given a device `other` and a mapping `map_in` specifying port connections,
|
||||||
find the transform which will correctly align the specified ports.
|
find the transform which will correctly align the specified ports.
|
||||||
@ -745,7 +752,11 @@ class Device(Copyable, Mirrorable):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def rotate_offsets_around(offsets: ArrayLike, pivot: ArrayLike, angle: float) -> numpy.ndarray:
|
def rotate_offsets_around(
|
||||||
|
offsets: ArrayLike,
|
||||||
|
pivot: ArrayLike,
|
||||||
|
angle: float,
|
||||||
|
) -> numpy.ndarray:
|
||||||
offsets -= pivot
|
offsets -= pivot
|
||||||
offsets[:] = (rotation_matrix_2d(angle) @ offsets.T).T
|
offsets[:] = (rotation_matrix_2d(angle) @ offsets.T).T
|
||||||
offsets += pivot
|
offsets += pivot
|
||||||
|
@ -9,7 +9,8 @@ from ..utils import rotation_matrix_2d, vector2
|
|||||||
from ..error import BuildError
|
from ..error import BuildError
|
||||||
|
|
||||||
|
|
||||||
def ell(ports: Dict[str, Port],
|
def ell(
|
||||||
|
ports: Dict[str, Port],
|
||||||
ccw: Optional[bool],
|
ccw: Optional[bool],
|
||||||
bound_type: str,
|
bound_type: str,
|
||||||
bound: Union[float, vector2],
|
bound: Union[float, vector2],
|
||||||
|
@ -28,13 +28,14 @@ logger.warning('DXF support is experimental and only slightly tested!')
|
|||||||
DEFAULT_LAYER = 'DEFAULT'
|
DEFAULT_LAYER = 'DEFAULT'
|
||||||
|
|
||||||
|
|
||||||
def write(pattern: Pattern,
|
def write(
|
||||||
stream: io.TextIOBase,
|
pattern: Pattern,
|
||||||
*,
|
stream: io.TextIOBase,
|
||||||
modify_originals: bool = False,
|
*,
|
||||||
dxf_version='AC1024',
|
modify_originals: bool = False,
|
||||||
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
dxf_version='AC1024',
|
||||||
) -> None:
|
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write a `Pattern` to a DXF file, by first calling `.polygonize()` to change the shapes
|
Write a `Pattern` to a DXF file, by first calling `.polygonize()` to change the shapes
|
||||||
into polygons, and then writing patterns as DXF `Block`s, polygons as `LWPolyline`s,
|
into polygons, and then writing patterns as DXF `Block`s, polygons as `LWPolyline`s,
|
||||||
@ -99,11 +100,12 @@ def write(pattern: Pattern,
|
|||||||
lib.write(stream)
|
lib.write(stream)
|
||||||
|
|
||||||
|
|
||||||
def writefile(pattern: Pattern,
|
def writefile(
|
||||||
filename: Union[str, pathlib.Path],
|
pattern: Pattern,
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> None:
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Wrapper for `dxf.write()` that takes a filename or path instead of a stream.
|
Wrapper for `dxf.write()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -125,10 +127,11 @@ def writefile(pattern: Pattern,
|
|||||||
write(pattern, stream, *args, **kwargs)
|
write(pattern, stream, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def readfile(filename: Union[str, pathlib.Path],
|
def readfile(
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> Tuple[Pattern, Dict[str, Any]]:
|
**kwargs,
|
||||||
|
) -> Tuple[Pattern, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `dxf.read()` that takes a filename or path instead of a stream.
|
Wrapper for `dxf.read()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -150,9 +153,10 @@ def readfile(filename: Union[str, pathlib.Path],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def read(stream: io.TextIOBase,
|
def read(
|
||||||
clean_vertices: bool = True,
|
stream: io.TextIOBase,
|
||||||
) -> Tuple[Pattern, Dict[str, Any]]:
|
clean_vertices: bool = True,
|
||||||
|
) -> Tuple[Pattern, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Read a dxf file and translate it into a dict of `Pattern` objects. DXF `Block`s are
|
Read a dxf file and translate it into a dict of `Pattern` objects. DXF `Block`s are
|
||||||
translated into `Pattern` objects; `LWPolyline`s are translated into polygons, and `Insert`s
|
translated into `Pattern` objects; `LWPolyline`s are translated into polygons, and `Insert`s
|
||||||
@ -273,8 +277,10 @@ def _read_block(block, clean_vertices: bool) -> Pattern:
|
|||||||
return pat
|
return pat
|
||||||
|
|
||||||
|
|
||||||
def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
def _subpatterns_to_refs(
|
||||||
subpatterns: List[SubPattern]) -> None:
|
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||||
|
subpatterns: List[SubPattern],
|
||||||
|
) -> None:
|
||||||
for subpat in subpatterns:
|
for subpat in subpatterns:
|
||||||
if subpat.pattern is None:
|
if subpat.pattern is None:
|
||||||
continue
|
continue
|
||||||
@ -318,9 +324,11 @@ def _subpatterns_to_refs(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.M
|
|||||||
block.add_blockref(encoded_name, subpat.offset + dd, dxfattribs=attribs)
|
block.add_blockref(encoded_name, subpat.offset + dd, dxfattribs=attribs)
|
||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
def _shapes_to_elements(
|
||||||
shapes: List[Shape],
|
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||||
polygonize_paths: bool = False):
|
shapes: List[Shape],
|
||||||
|
polygonize_paths: bool = False,
|
||||||
|
) -> None:
|
||||||
# Add `LWPolyline`s for each shape.
|
# Add `LWPolyline`s for each shape.
|
||||||
# Could set do paths with width setting, but need to consider endcaps.
|
# Could set do paths with width setting, but need to consider endcaps.
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
@ -331,8 +339,10 @@ def _shapes_to_elements(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Mo
|
|||||||
block.add_lwpolyline(xy_closed, dxfattribs=attribs)
|
block.add_lwpolyline(xy_closed, dxfattribs=attribs)
|
||||||
|
|
||||||
|
|
||||||
def _labels_to_texts(block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
def _labels_to_texts(
|
||||||
labels: List[Label]) -> None:
|
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||||
|
labels: List[Label],
|
||||||
|
) -> None:
|
||||||
for label in labels:
|
for label in labels:
|
||||||
attribs = {'layer': _mlayer2dxf(label.layer)}
|
attribs = {'layer': _mlayer2dxf(label.layer)}
|
||||||
xy = label.offset
|
xy = label.offset
|
||||||
@ -349,11 +359,12 @@ def _mlayer2dxf(layer: layer_t) -> str:
|
|||||||
raise PatternError(f'Unknown layer type: {layer} ({type(layer)})')
|
raise PatternError(f'Unknown layer type: {layer} ({type(layer)})')
|
||||||
|
|
||||||
|
|
||||||
def disambiguate_pattern_names(patterns: Iterable[Pattern],
|
def disambiguate_pattern_names(
|
||||||
max_name_length: int = 32,
|
patterns: Iterable[Pattern],
|
||||||
suffix_length: int = 6,
|
max_name_length: int = 32,
|
||||||
dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name
|
suffix_length: int = 6,
|
||||||
) -> None:
|
dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name
|
||||||
|
) -> None:
|
||||||
used_names = []
|
used_names = []
|
||||||
for pat in patterns:
|
for pat in patterns:
|
||||||
sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name)
|
sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name)
|
||||||
|
@ -52,15 +52,16 @@ path_cap_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def write(patterns: Union[Pattern, Sequence[Pattern]],
|
def write(
|
||||||
stream: BinaryIO,
|
patterns: Union[Pattern, Sequence[Pattern]],
|
||||||
meters_per_unit: float,
|
stream: BinaryIO,
|
||||||
logical_units_per_unit: float = 1,
|
meters_per_unit: float,
|
||||||
library_name: str = 'masque-klamath',
|
logical_units_per_unit: float = 1,
|
||||||
*,
|
library_name: str = 'masque-klamath',
|
||||||
modify_originals: bool = False,
|
*,
|
||||||
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
modify_originals: bool = False,
|
||||||
) -> None:
|
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Convert a `Pattern` or list of patterns to a GDSII stream, and then mapping data as follows:
|
Convert a `Pattern` or list of patterns to a GDSII stream, and then mapping data as follows:
|
||||||
Pattern -> GDSII structure
|
Pattern -> GDSII structure
|
||||||
@ -136,11 +137,12 @@ def write(patterns: Union[Pattern, Sequence[Pattern]],
|
|||||||
records.ENDLIB.write(stream, None)
|
records.ENDLIB.write(stream, None)
|
||||||
|
|
||||||
|
|
||||||
def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
def writefile(
|
||||||
filename: Union[str, pathlib.Path],
|
patterns: Union[Sequence[Pattern], Pattern],
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> None:
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Wrapper for `write()` that takes a filename or path instead of a stream.
|
Wrapper for `write()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -162,10 +164,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
|||||||
write(patterns, stream, *args, **kwargs)
|
write(patterns, stream, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def readfile(filename: Union[str, pathlib.Path],
|
def readfile(
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
**kwargs,
|
||||||
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `read()` that takes a filename or path instead of a stream.
|
Wrapper for `read()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -187,9 +190,10 @@ def readfile(filename: Union[str, pathlib.Path],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def read(stream: BinaryIO,
|
def read(
|
||||||
raw_mode: bool = True,
|
stream: BinaryIO,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
raw_mode: bool = True,
|
||||||
|
) -> 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
|
||||||
@ -243,10 +247,11 @@ def _read_header(stream: BinaryIO) -> Dict[str, Any]:
|
|||||||
return library_info
|
return library_info
|
||||||
|
|
||||||
|
|
||||||
def read_elements(stream: BinaryIO,
|
def read_elements(
|
||||||
name: str,
|
stream: BinaryIO,
|
||||||
raw_mode: bool = True,
|
name: str,
|
||||||
) -> Pattern:
|
raw_mode: bool = True,
|
||||||
|
) -> Pattern:
|
||||||
"""
|
"""
|
||||||
Read elements from a GDS structure and build a Pattern from them.
|
Read elements from a GDS structure and build a Pattern from them.
|
||||||
|
|
||||||
@ -296,8 +301,7 @@ def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]:
|
|||||||
return layer, data_type
|
return layer, data_type
|
||||||
|
|
||||||
|
|
||||||
def _ref_to_subpat(ref: klamath.library.Reference,
|
def _ref_to_subpat(ref: klamath.library.Reference) -> SubPattern:
|
||||||
) -> SubPattern:
|
|
||||||
"""
|
"""
|
||||||
Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None
|
Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None
|
||||||
and sets the instance .identifier to (struct_name,).
|
and sets the instance .identifier to (struct_name,).
|
||||||
@ -351,8 +355,7 @@ def _boundary_to_polygon(boundary: klamath.library.Boundary, raw_mode: bool) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _subpatterns_to_refs(subpatterns: List[SubPattern]
|
def _subpatterns_to_refs(subpatterns: List[SubPattern]) -> List[klamath.library.Reference]:
|
||||||
) -> List[klamath.library.Reference]:
|
|
||||||
refs = []
|
refs = []
|
||||||
for subpat in subpatterns:
|
for subpat in subpatterns:
|
||||||
if subpat.pattern is None:
|
if subpat.pattern is None:
|
||||||
@ -427,9 +430,10 @@ def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -
|
|||||||
return props
|
return props
|
||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(shapes: List[Shape],
|
def _shapes_to_elements(
|
||||||
polygonize_paths: bool = False
|
shapes: List[Shape],
|
||||||
) -> List[klamath.elements.Element]:
|
polygonize_paths: bool = False,
|
||||||
|
) -> List[klamath.elements.Element]:
|
||||||
elements: List[klamath.elements.Element] = []
|
elements: List[klamath.elements.Element] = []
|
||||||
# Add a Boundary element for each shape, and Path elements if necessary
|
# Add a Boundary element for each shape, and Path elements if necessary
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
@ -492,11 +496,12 @@ def _labels_to_texts(labels: List[Label]) -> List[klamath.elements.Text]:
|
|||||||
return texts
|
return texts
|
||||||
|
|
||||||
|
|
||||||
def disambiguate_pattern_names(patterns: Sequence[Pattern],
|
def disambiguate_pattern_names(
|
||||||
max_name_length: int = 32,
|
patterns: Sequence[Pattern],
|
||||||
suffix_length: int = 6,
|
max_name_length: int = 32,
|
||||||
dup_warn_filter: Optional[Callable[[str], bool]] = None,
|
suffix_length: int = 6,
|
||||||
):
|
dup_warn_filter: Optional[Callable[[str], bool]] = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
patterns: List of patterns to disambiguate
|
patterns: List of patterns to disambiguate
|
||||||
@ -549,12 +554,13 @@ def disambiguate_pattern_names(patterns: Sequence[Pattern],
|
|||||||
used_names.append(suffixed_name)
|
used_names.append(suffixed_name)
|
||||||
|
|
||||||
|
|
||||||
def load_library(stream: BinaryIO,
|
def load_library(
|
||||||
tag: str,
|
stream: BinaryIO,
|
||||||
is_secondary: Optional[Callable[[str], bool]] = None,
|
tag: str,
|
||||||
*,
|
is_secondary: Optional[Callable[[str], bool]] = None,
|
||||||
full_load: bool = False,
|
*,
|
||||||
) -> Tuple[Library, Dict[str, Any]]:
|
full_load: bool = False,
|
||||||
|
) -> Tuple[Library, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Scan a GDSII stream to determine what structures are present, and create
|
Scan a GDSII stream to determine what structures are present, and create
|
||||||
a library from them. This enables deferred reading of structures
|
a library from them. This enables deferred reading of structures
|
||||||
@ -581,7 +587,7 @@ def load_library(stream: BinaryIO,
|
|||||||
Additional library info (dict, same format as from `read`).
|
Additional library info (dict, same format as from `read`).
|
||||||
"""
|
"""
|
||||||
if is_secondary is None:
|
if is_secondary is None:
|
||||||
def is_secondary(k: str):
|
def is_secondary(k: str) -> bool:
|
||||||
return False
|
return False
|
||||||
assert(is_secondary is not None)
|
assert(is_secondary is not None)
|
||||||
|
|
||||||
@ -611,13 +617,14 @@ def load_library(stream: BinaryIO,
|
|||||||
return lib, library_info
|
return lib, library_info
|
||||||
|
|
||||||
|
|
||||||
def load_libraryfile(filename: Union[str, pathlib.Path],
|
def load_libraryfile(
|
||||||
tag: str,
|
filename: Union[str, pathlib.Path],
|
||||||
is_secondary: Optional[Callable[[str], bool]] = None,
|
tag: str,
|
||||||
*,
|
is_secondary: Optional[Callable[[str], bool]] = None,
|
||||||
use_mmap: bool = True,
|
*,
|
||||||
full_load: bool = False,
|
use_mmap: bool = True,
|
||||||
) -> Tuple[Library, Dict[str, Any]]:
|
full_load: bool = False,
|
||||||
|
) -> Tuple[Library, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `load_library()` that takes a filename or path instead of a stream.
|
Wrapper for `load_library()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
|
@ -47,14 +47,15 @@ path_cap_map = {
|
|||||||
|
|
||||||
#TODO implement more shape types?
|
#TODO implement more shape types?
|
||||||
|
|
||||||
def build(patterns: Union[Pattern, Sequence[Pattern]],
|
def build(
|
||||||
units_per_micron: int,
|
patterns: Union[Pattern, Sequence[Pattern]],
|
||||||
layer_map: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None,
|
units_per_micron: int,
|
||||||
*,
|
layer_map: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None,
|
||||||
modify_originals: bool = False,
|
*,
|
||||||
disambiguate_func: Optional[Callable[[Iterable[Pattern]], None]] = None,
|
modify_originals: bool = False,
|
||||||
annotations: Optional[annotations_t] = None
|
disambiguate_func: Optional[Callable[[Iterable[Pattern]], None]] = None,
|
||||||
) -> fatamorgana.OasisLayout:
|
annotations: Optional[annotations_t] = None,
|
||||||
|
) -> fatamorgana.OasisLayout:
|
||||||
"""
|
"""
|
||||||
Convert a `Pattern` or list of patterns to an OASIS stream, writing patterns
|
Convert a `Pattern` or list of patterns to an OASIS stream, writing patterns
|
||||||
as OASIS cells, subpatterns as Placement records, and other shapes and labels
|
as OASIS cells, subpatterns as Placement records, and other shapes and labels
|
||||||
@ -153,10 +154,12 @@ def build(patterns: Union[Pattern, Sequence[Pattern]],
|
|||||||
return lib
|
return lib
|
||||||
|
|
||||||
|
|
||||||
def write(patterns: Union[Sequence[Pattern], Pattern],
|
def write(
|
||||||
stream: io.BufferedIOBase,
|
patterns: Union[Sequence[Pattern], Pattern],
|
||||||
*args,
|
stream: io.BufferedIOBase,
|
||||||
**kwargs):
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write a `Pattern` or list of patterns to a OASIS file. See `oasis.build()`
|
Write a `Pattern` or list of patterns to a OASIS file. See `oasis.build()`
|
||||||
for details.
|
for details.
|
||||||
@ -171,11 +174,12 @@ def write(patterns: Union[Sequence[Pattern], Pattern],
|
|||||||
lib.write(stream)
|
lib.write(stream)
|
||||||
|
|
||||||
|
|
||||||
def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
def writefile(
|
||||||
filename: Union[str, pathlib.Path],
|
patterns: Union[Sequence[Pattern], Pattern],
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
):
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Wrapper for `oasis.write()` that takes a filename or path instead of a stream.
|
Wrapper for `oasis.write()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -198,10 +202,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def readfile(filename: Union[str, pathlib.Path],
|
def readfile(
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
**kwargs,
|
||||||
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `oasis.read()` that takes a filename or path instead of a stream.
|
Wrapper for `oasis.read()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -223,9 +228,10 @@ def readfile(filename: Union[str, pathlib.Path],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def read(stream: io.BufferedIOBase,
|
def read(
|
||||||
clean_vertices: bool = True,
|
stream: io.BufferedIOBase,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
clean_vertices: bool = True,
|
||||||
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Read a OASIS file and translate it into a dict of Pattern objects. OASIS cells are
|
Read a OASIS file and translate it into a dict of Pattern objects. OASIS cells are
|
||||||
translated into Pattern objects; Polygons are translated into polygons, and Placements
|
translated into Pattern objects; Polygons are translated into polygons, and Placements
|
||||||
@ -496,8 +502,9 @@ def _placement_to_subpat(placement: fatrec.Placement, lib: fatamorgana.OasisLayo
|
|||||||
return subpat
|
return subpat
|
||||||
|
|
||||||
|
|
||||||
def _subpatterns_to_placements(subpatterns: List[SubPattern]
|
def _subpatterns_to_placements(
|
||||||
) -> List[fatrec.Placement]:
|
subpatterns: List[SubPattern],
|
||||||
|
) -> List[fatrec.Placement]:
|
||||||
refs = []
|
refs = []
|
||||||
for subpat in subpatterns:
|
for subpat in subpatterns:
|
||||||
if subpat.pattern is None:
|
if subpat.pattern is None:
|
||||||
@ -523,9 +530,10 @@ def _subpatterns_to_placements(subpatterns: List[SubPattern]
|
|||||||
return refs
|
return refs
|
||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(shapes: List[Shape],
|
def _shapes_to_elements(
|
||||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
shapes: List[Shape],
|
||||||
) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]:
|
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
||||||
|
) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]:
|
||||||
# Add a Polygon record for each shape, and Path elements if necessary
|
# Add a Polygon record for each shape, and Path elements if necessary
|
||||||
elements: List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]] = []
|
elements: List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]] = []
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
@ -576,9 +584,10 @@ def _shapes_to_elements(shapes: List[Shape],
|
|||||||
return elements
|
return elements
|
||||||
|
|
||||||
|
|
||||||
def _labels_to_texts(labels: List[Label],
|
def _labels_to_texts(
|
||||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
labels: List[Label],
|
||||||
) -> List[fatrec.Text]:
|
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
||||||
|
) -> List[fatrec.Text]:
|
||||||
texts = []
|
texts = []
|
||||||
for label in labels:
|
for label in labels:
|
||||||
layer, datatype = layer2oas(label.layer)
|
layer, datatype = layer2oas(label.layer)
|
||||||
@ -595,9 +604,10 @@ def _labels_to_texts(labels: List[Label],
|
|||||||
return texts
|
return texts
|
||||||
|
|
||||||
|
|
||||||
def disambiguate_pattern_names(patterns,
|
def disambiguate_pattern_names(
|
||||||
dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name
|
patterns,
|
||||||
):
|
dup_warn_filter: Callable[[str], bool] = None, # If returns False, don't warn about this name
|
||||||
|
) -> None:
|
||||||
used_names = []
|
used_names = []
|
||||||
for pat in patterns:
|
for pat in patterns:
|
||||||
sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name)
|
sanitized_name = re.compile(r'[^A-Za-z0-9_\?\$]').sub('_', pat.name)
|
||||||
@ -625,8 +635,9 @@ def disambiguate_pattern_names(patterns,
|
|||||||
used_names.append(suffixed_name)
|
used_names.append(suffixed_name)
|
||||||
|
|
||||||
|
|
||||||
def repetition_fata2masq(rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None]
|
def repetition_fata2masq(
|
||||||
) -> Optional[Repetition]:
|
rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None],
|
||||||
|
) -> Optional[Repetition]:
|
||||||
mrep: Optional[Repetition]
|
mrep: Optional[Repetition]
|
||||||
if isinstance(rep, fatamorgana.GridRepetition):
|
if isinstance(rep, fatamorgana.GridRepetition):
|
||||||
mrep = Grid(a_vector=rep.a_vector,
|
mrep = Grid(a_vector=rep.a_vector,
|
||||||
@ -643,11 +654,12 @@ def repetition_fata2masq(rep: Union[fatamorgana.GridRepetition, fatamorgana.Arbi
|
|||||||
return mrep
|
return mrep
|
||||||
|
|
||||||
|
|
||||||
def repetition_masq2fata(rep: Optional[Repetition]
|
def repetition_masq2fata(
|
||||||
) -> Tuple[Union[fatamorgana.GridRepetition,
|
rep: Optional[Repetition],
|
||||||
fatamorgana.ArbitraryRepetition,
|
) -> Tuple[Union[fatamorgana.GridRepetition,
|
||||||
None],
|
fatamorgana.ArbitraryRepetition,
|
||||||
Tuple[int, int]]:
|
None],
|
||||||
|
Tuple[int, int]]:
|
||||||
frep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None]
|
frep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None]
|
||||||
if isinstance(rep, Grid):
|
if isinstance(rep, Grid):
|
||||||
frep = fatamorgana.GridRepetition(
|
frep = fatamorgana.GridRepetition(
|
||||||
@ -678,10 +690,11 @@ def annotations_to_properties(annotations: annotations_t) -> List[fatrec.Propert
|
|||||||
return properties
|
return properties
|
||||||
|
|
||||||
|
|
||||||
def properties_to_annotations(properties: List[fatrec.Property],
|
def properties_to_annotations(
|
||||||
propnames: Dict[int, NString],
|
properties: List[fatrec.Property],
|
||||||
propstrings: Dict[int, AString],
|
propnames: Dict[int, NString],
|
||||||
) -> annotations_t:
|
propstrings: Dict[int, AString],
|
||||||
|
) -> annotations_t:
|
||||||
annotations = {}
|
annotations = {}
|
||||||
for proprec in properties:
|
for proprec in properties:
|
||||||
assert(proprec.name is not None)
|
assert(proprec.name is not None)
|
||||||
|
@ -53,14 +53,15 @@ path_cap_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def build(patterns: Union[Pattern, Sequence[Pattern]],
|
def build(
|
||||||
meters_per_unit: float,
|
patterns: Union[Pattern, Sequence[Pattern]],
|
||||||
logical_units_per_unit: float = 1,
|
meters_per_unit: float,
|
||||||
library_name: str = 'masque-gdsii-write',
|
logical_units_per_unit: float = 1,
|
||||||
*,
|
library_name: str = 'masque-gdsii-write',
|
||||||
modify_originals: bool = False,
|
*,
|
||||||
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
modify_originals: bool = False,
|
||||||
) -> gdsii.library.Library:
|
disambiguate_func: Callable[[Iterable[Pattern]], None] = None,
|
||||||
|
) -> gdsii.library.Library:
|
||||||
"""
|
"""
|
||||||
Convert a `Pattern` or list of patterns to a GDSII stream, by first calling
|
Convert a `Pattern` or list of patterns to a GDSII stream, 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
|
||||||
@ -137,10 +138,12 @@ def build(patterns: Union[Pattern, Sequence[Pattern]],
|
|||||||
return lib
|
return lib
|
||||||
|
|
||||||
|
|
||||||
def write(patterns: Union[Pattern, Sequence[Pattern]],
|
def write(
|
||||||
stream: io.BufferedIOBase,
|
patterns: Union[Pattern, Sequence[Pattern]],
|
||||||
*args,
|
stream: io.BufferedIOBase,
|
||||||
**kwargs):
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write a `Pattern` or list of patterns to a GDSII file.
|
Write a `Pattern` or list of patterns to a GDSII file.
|
||||||
See `masque.file.gdsii.build()` for details.
|
See `masque.file.gdsii.build()` for details.
|
||||||
@ -155,11 +158,12 @@ def write(patterns: Union[Pattern, Sequence[Pattern]],
|
|||||||
lib.save(stream)
|
lib.save(stream)
|
||||||
return
|
return
|
||||||
|
|
||||||
def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
def writefile(
|
||||||
filename: Union[str, pathlib.Path],
|
patterns: Union[Sequence[Pattern], Pattern],
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
):
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Wrapper for `masque.file.gdsii.write()` that takes a filename or path instead of a stream.
|
Wrapper for `masque.file.gdsii.write()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -182,10 +186,11 @@ def writefile(patterns: Union[Sequence[Pattern], Pattern],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def readfile(filename: Union[str, pathlib.Path],
|
def readfile(
|
||||||
*args,
|
filename: Union[str, pathlib.Path],
|
||||||
**kwargs,
|
*args,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
**kwargs,
|
||||||
|
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `masque.file.gdsii.read()` that takes a filename or path instead of a stream.
|
Wrapper for `masque.file.gdsii.read()` that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
@ -207,9 +212,10 @@ def readfile(filename: Union[str, pathlib.Path],
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def read(stream: io.BufferedIOBase,
|
def read(
|
||||||
clean_vertices: bool = True,
|
stream: io.BufferedIOBase,
|
||||||
) -> Tuple[Dict[str, Pattern], Dict[str, Any]]:
|
clean_vertices: bool = True,
|
||||||
|
) -> 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
|
||||||
@ -294,9 +300,10 @@ def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]:
|
|||||||
return layer, data_type
|
return layer, data_type
|
||||||
|
|
||||||
|
|
||||||
def _ref_to_subpat(element: Union[gdsii.elements.SRef,
|
def _ref_to_subpat(
|
||||||
gdsii.elements.ARef]
|
element: Union[gdsii.elements.SRef,
|
||||||
) -> SubPattern:
|
gdsii.elements.ARef]
|
||||||
|
) -> SubPattern:
|
||||||
"""
|
"""
|
||||||
Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None
|
Helper function to create a SubPattern from an SREF or AREF. Sets subpat.pattern to None
|
||||||
and sets the instance .identifier to (struct_name,).
|
and sets the instance .identifier to (struct_name,).
|
||||||
@ -379,8 +386,9 @@ def _boundary_to_polygon(element: gdsii.elements.Boundary, raw_mode: bool) -> Po
|
|||||||
return Polygon(**args)
|
return Polygon(**args)
|
||||||
|
|
||||||
|
|
||||||
def _subpatterns_to_refs(subpatterns: List[SubPattern]
|
def _subpatterns_to_refs(
|
||||||
) -> List[Union[gdsii.elements.ARef, gdsii.elements.SRef]]:
|
subpatterns: List[SubPattern],
|
||||||
|
) -> 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:
|
||||||
@ -450,9 +458,10 @@ def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -
|
|||||||
return props
|
return props
|
||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(shapes: List[Shape],
|
def _shapes_to_elements(
|
||||||
polygonize_paths: bool = False
|
shapes: List[Shape],
|
||||||
) -> List[Union[gdsii.elements.Boundary, gdsii.elements.Path]]:
|
polygonize_paths: bool = False,
|
||||||
|
) -> List[Union[gdsii.elements.Boundary, gdsii.elements.Path]]:
|
||||||
elements: List[Union[gdsii.elements.Boundary, gdsii.elements.Path]] = []
|
elements: List[Union[gdsii.elements.Boundary, gdsii.elements.Path]] = []
|
||||||
# Add a Boundary element for each shape, and Path elements if necessary
|
# Add a Boundary element for each shape, and Path elements if necessary
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
@ -496,11 +505,12 @@ def _labels_to_texts(labels: List[Label]) -> List[gdsii.elements.Text]:
|
|||||||
return texts
|
return texts
|
||||||
|
|
||||||
|
|
||||||
def disambiguate_pattern_names(patterns: Sequence[Pattern],
|
def disambiguate_pattern_names(
|
||||||
max_name_length: int = 32,
|
patterns: Sequence[Pattern],
|
||||||
suffix_length: int = 6,
|
max_name_length: int = 32,
|
||||||
dup_warn_filter: Optional[Callable[[str], bool]] = None,
|
suffix_length: int = 6,
|
||||||
):
|
dup_warn_filter: Optional[Callable[[str], bool]] = None,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
patterns: List of patterns to disambiguate
|
patterns: List of patterns to disambiguate
|
||||||
|
@ -11,10 +11,11 @@ from .utils import mangle_name
|
|||||||
from .. import Pattern
|
from .. import Pattern
|
||||||
|
|
||||||
|
|
||||||
def writefile(pattern: Pattern,
|
def writefile(
|
||||||
filename: str,
|
pattern: Pattern,
|
||||||
custom_attributes: bool = False,
|
filename: str,
|
||||||
) -> None:
|
custom_attributes: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write a Pattern to an SVG file, by first calling .polygonize() on it
|
Write a Pattern to an SVG file, by first calling .polygonize() on it
|
||||||
to change the shapes into polygons, and then writing patterns as SVG
|
to change the shapes into polygons, and then writing patterns as SVG
|
||||||
|
@ -95,8 +95,9 @@ def dtype2dose(pattern: Pattern) -> Pattern:
|
|||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
|
|
||||||
def dose2dtype(patterns: List[Pattern],
|
def dose2dtype(
|
||||||
) -> Tuple[List[Pattern], List[float]]:
|
patterns: List[Pattern],
|
||||||
|
) -> Tuple[List[Pattern], List[float]]:
|
||||||
"""
|
"""
|
||||||
For each shape in each pattern, set shape.layer to the tuple
|
For each shape in each pattern, set shape.layer to the tuple
|
||||||
(base_layer, datatype), where:
|
(base_layer, datatype), where:
|
||||||
|
@ -36,19 +36,20 @@ class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, Annot
|
|||||||
return self._string
|
return self._string
|
||||||
|
|
||||||
@string.setter
|
@string.setter
|
||||||
def string(self, val: str):
|
def string(self, val: str) -> None:
|
||||||
self._string = val
|
self._string = val
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
string: str,
|
self,
|
||||||
*,
|
string: str,
|
||||||
offset: vector2 = (0.0, 0.0),
|
*,
|
||||||
layer: layer_t = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
repetition: Optional[Repetition] = None,
|
layer: layer_t = 0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
identifier: Tuple = (),
|
locked: bool = False,
|
||||||
) -> None:
|
identifier: Tuple = (),
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = identifier
|
self.identifier = identifier
|
||||||
self.string = string
|
self.string = string
|
||||||
|
@ -143,7 +143,13 @@ class Library:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return '<Library with keys ' + repr(list(self.primary.keys())) + '>'
|
return '<Library with keys ' + repr(list(self.primary.keys())) + '>'
|
||||||
|
|
||||||
def set_const(self, key: str, tag: Any, const: 'Pattern', secondary: bool = False) -> None:
|
def set_const(
|
||||||
|
self,
|
||||||
|
key: str,
|
||||||
|
tag: Any,
|
||||||
|
const: 'Pattern',
|
||||||
|
secondary: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Convenience function to avoid having to manually wrap
|
Convenience function to avoid having to manually wrap
|
||||||
constant values into callables.
|
constant values into callables.
|
||||||
@ -162,7 +168,13 @@ class Library:
|
|||||||
else:
|
else:
|
||||||
self.primary[key] = pg
|
self.primary[key] = pg
|
||||||
|
|
||||||
def set_value(self, key: str, tag: str, value: Callable[[], 'Pattern'], secondary: bool = False) -> None:
|
def set_value(
|
||||||
|
self,
|
||||||
|
key: str,
|
||||||
|
tag: str,
|
||||||
|
value: Callable[[], 'Pattern'],
|
||||||
|
secondary: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Convenience function to automatically build a PatternGenerator.
|
Convenience function to automatically build a PatternGenerator.
|
||||||
|
|
||||||
|
@ -53,15 +53,16 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
name: str
|
name: str
|
||||||
""" A name for this pattern """
|
""" A name for this pattern """
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
name: str = '',
|
self,
|
||||||
*,
|
name: str = '',
|
||||||
shapes: Sequence[Shape] = (),
|
*,
|
||||||
labels: Sequence[Label] = (),
|
shapes: Sequence[Shape] = (),
|
||||||
subpatterns: Sequence[SubPattern] = (),
|
labels: Sequence[Label] = (),
|
||||||
annotations: Optional[annotations_t] = None,
|
subpatterns: Sequence[SubPattern] = (),
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
) -> None:
|
locked: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Basic init; arguments get assigned to member variables.
|
Basic init; arguments get assigned to member variables.
|
||||||
Non-list inputs for shapes and subpatterns get converted to lists.
|
Non-list inputs for shapes and subpatterns get converted to lists.
|
||||||
@ -141,12 +142,13 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
self.labels += other_pattern.labels
|
self.labels += other_pattern.labels
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def subset(self,
|
def subset(
|
||||||
shapes_func: Callable[[Shape], bool] = None,
|
self,
|
||||||
labels_func: Callable[[Label], bool] = None,
|
shapes_func: Callable[[Shape], bool] = None,
|
||||||
subpatterns_func: Callable[[SubPattern], bool] = None,
|
labels_func: Callable[[Label], bool] = None,
|
||||||
recursive: bool = False,
|
subpatterns_func: Callable[[SubPattern], bool] = None,
|
||||||
) -> 'Pattern':
|
recursive: bool = False,
|
||||||
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Returns a Pattern containing only the entities (e.g. shapes) for which the
|
Returns a Pattern containing only the entities (e.g. shapes) for which the
|
||||||
given entity_func returns True.
|
given entity_func returns True.
|
||||||
@ -186,10 +188,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
assert(pat is not None)
|
assert(pat is not None)
|
||||||
return pat
|
return pat
|
||||||
|
|
||||||
def apply(self,
|
def apply(
|
||||||
func: Callable[[Optional['Pattern']], Optional['Pattern']],
|
self,
|
||||||
memo: Optional[Dict[int, Optional['Pattern']]] = None,
|
func: Callable[[Optional['Pattern']], Optional['Pattern']],
|
||||||
) -> Optional['Pattern']:
|
memo: Optional[Dict[int, Optional['Pattern']]] = None,
|
||||||
|
) -> 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.
|
||||||
@ -229,7 +232,8 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
pat = memo[pat_id]
|
pat = memo[pat_id]
|
||||||
return pat
|
return pat
|
||||||
|
|
||||||
def dfs(self: P,
|
def dfs(
|
||||||
|
self: P,
|
||||||
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: Union[numpy.ndarray, bool, None] = False,
|
transform: Union[numpy.ndarray, bool, None] = False,
|
||||||
@ -237,7 +241,7 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
hierarchy: Tuple[P, ...] = (),
|
hierarchy: Tuple[P, ...] = (),
|
||||||
) -> P:
|
) -> P:
|
||||||
"""
|
"""
|
||||||
Experimental convenience function.
|
Convenience function.
|
||||||
Performs a depth-first traversal of this pattern and its subpatterns.
|
Performs a depth-first traversal of this pattern and its subpatterns.
|
||||||
At each pattern in the tree, the following sequence is called:
|
At each pattern in the tree, the following sequence is called:
|
||||||
```
|
```
|
||||||
@ -314,10 +318,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform) # type: ignore
|
pat = visit_after(pat, hierarchy=hierarchy, memo=memo, transform=transform) # type: ignore
|
||||||
return pat
|
return pat
|
||||||
|
|
||||||
def polygonize(self: P,
|
def polygonize(
|
||||||
poly_num_points: Optional[int] = None,
|
self: P,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = None,
|
||||||
) -> P:
|
poly_max_arclen: Optional[float] = None,
|
||||||
|
) -> P:
|
||||||
"""
|
"""
|
||||||
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,
|
||||||
replacing them with the returned polygons.
|
replacing them with the returned polygons.
|
||||||
@ -342,10 +347,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
subpat.pattern.polygonize(poly_num_points, poly_max_arclen)
|
subpat.pattern.polygonize(poly_num_points, poly_max_arclen)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def manhattanize(self: P,
|
def manhattanize(
|
||||||
grid_x: ArrayLike,
|
self: P,
|
||||||
grid_y: ArrayLike,
|
grid_x: ArrayLike,
|
||||||
) -> P:
|
grid_y: ArrayLike,
|
||||||
|
) -> P:
|
||||||
"""
|
"""
|
||||||
Calls `.polygonize()` and `.flatten()` on the pattern, then calls `.manhattanize()` on all the
|
Calls `.polygonize()` and `.flatten()` on the pattern, then calls `.manhattanize()` on all the
|
||||||
resulting shapes, replacing them with the returned Manhattan polygons.
|
resulting shapes, replacing them with the returned Manhattan polygons.
|
||||||
@ -364,11 +370,12 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
(shape.manhattanize(grid_x, grid_y) for shape in old_shapes)))
|
(shape.manhattanize(grid_x, grid_y) for shape in old_shapes)))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def subpatternize(self: P,
|
def subpatternize(
|
||||||
recursive: bool = True,
|
self: P,
|
||||||
norm_value: int = int(1e6),
|
recursive: bool = True,
|
||||||
exclude_types: Tuple[Type] = (Polygon,)
|
norm_value: int = int(1e6),
|
||||||
) -> P:
|
exclude_types: Tuple[Type] = (Polygon,)
|
||||||
|
) -> P:
|
||||||
"""
|
"""
|
||||||
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
|
||||||
over all shapes, calling `.normalized_form(norm_value)` on them to retrieve a scale-,
|
over all shapes, calling `.normalized_form(norm_value)` on them to retrieve a scale-,
|
||||||
@ -456,11 +463,12 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
def referenced_patterns_by_id(self, include_none: bool) -> Dict[int, Optional['Pattern']]:
|
def referenced_patterns_by_id(self, include_none: bool) -> Dict[int, Optional['Pattern']]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def referenced_patterns_by_id(self,
|
def referenced_patterns_by_id(
|
||||||
include_none: bool = False,
|
self,
|
||||||
recursive: bool = True,
|
include_none: bool = False,
|
||||||
) -> Union[Dict[int, Optional['Pattern']],
|
recursive: bool = True,
|
||||||
Dict[int, 'Pattern']]:
|
) -> Union[Dict[int, Optional['Pattern']],
|
||||||
|
Dict[int, '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
|
||||||
@ -484,7 +492,10 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
ids.update(pat.referenced_patterns_by_id())
|
ids.update(pat.referenced_patterns_by_id())
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def referenced_patterns_by_name(self, **kwargs: Any) -> List[Tuple[Optional[str], Optional['Pattern']]]:
|
def referenced_patterns_by_name(
|
||||||
|
self,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> 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).
|
||||||
@ -502,10 +513,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
pat_list = [(p.name if p is not None else None, p) for p in pats_by_id.values()]
|
pat_list = [(p.name if p is not None else None, p) for p in pats_by_id.values()]
|
||||||
return pat_list
|
return pat_list
|
||||||
|
|
||||||
def subpatterns_by_id(self,
|
def subpatterns_by_id(
|
||||||
include_none: bool = False,
|
self,
|
||||||
recursive: bool = True,
|
include_none: bool = False,
|
||||||
) -> Dict[int, List[SubPattern]]:
|
recursive: bool = True,
|
||||||
|
) -> Dict[int, List[SubPattern]]:
|
||||||
"""
|
"""
|
||||||
Create a dictionary which maps `{id(referenced_pattern): [subpattern0, ...]}`
|
Create a dictionary which maps `{id(referenced_pattern): [subpattern0, ...]}`
|
||||||
for all SubPattern objects referenced by this Pattern (by default, operates
|
for all SubPattern objects referenced by this Pattern (by default, operates
|
||||||
@ -593,10 +605,11 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
flatten_single(self, set())
|
flatten_single(self, set())
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def wrap_repeated_shapes(self: P,
|
def wrap_repeated_shapes(
|
||||||
name_func: Callable[['Pattern', Union[Shape, Label]], str] = lambda p, s: '_repetition',
|
self: P,
|
||||||
recursive: bool = True,
|
name_func: Callable[['Pattern', Union[Shape, Label]], str] = lambda p, s: '_repetition',
|
||||||
) -> P:
|
recursive: bool = True,
|
||||||
|
) -> P:
|
||||||
"""
|
"""
|
||||||
Wraps all shapes and labels with a non-`None` `repetition` attribute
|
Wraps all shapes and labels with a non-`None` `repetition` attribute
|
||||||
into a `SubPattern`/`Pattern` combination, and applies the `repetition`
|
into a `SubPattern`/`Pattern` combination, and applies the `repetition`
|
||||||
@ -930,12 +943,13 @@ class Pattern(LockableImpl, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
|||||||
pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL)
|
pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def visualize(self,
|
def visualize(
|
||||||
offset: vector2 = (0., 0.),
|
self,
|
||||||
line_color: str = 'k',
|
offset: vector2 = (0., 0.),
|
||||||
fill_color: str = 'none',
|
line_color: str = 'k',
|
||||||
overdraw: bool = False,
|
fill_color: str = 'none',
|
||||||
) -> None:
|
overdraw: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Draw a picture of the Pattern and wait for the user to inspect it
|
Draw a picture of the Pattern and wait for the user to inspect it
|
||||||
|
|
||||||
|
@ -61,12 +61,14 @@ class Grid(LockableImpl, Repetition, metaclass=AutoSlots):
|
|||||||
_b_count: int
|
_b_count: int
|
||||||
""" Number of instances along the direction specified by the `b_vector` """
|
""" Number of instances along the direction specified by the `b_vector` """
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
a_vector: ArrayLike,
|
self,
|
||||||
a_count: int,
|
a_vector: ArrayLike,
|
||||||
b_vector: Optional[ArrayLike] = None,
|
a_count: int,
|
||||||
b_count: Optional[int] = 1,
|
b_vector: Optional[ArrayLike] = None,
|
||||||
locked: bool = False,):
|
b_count: Optional[int] = 1,
|
||||||
|
locked: bool = False,
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
a_vector: First lattice vector, of the form `[x, y]`.
|
a_vector: First lattice vector, of the form `[x, y]`.
|
||||||
|
@ -51,7 +51,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._radii
|
return self._radii
|
||||||
|
|
||||||
@radii.setter
|
@radii.setter
|
||||||
def radii(self, val: vector2):
|
def radii(self, val: vector2) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if not val.size == 2:
|
if not val.size == 2:
|
||||||
raise PatternError('Radii must have length 2')
|
raise PatternError('Radii must have length 2')
|
||||||
@ -64,7 +64,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._radii[0]
|
return self._radii[0]
|
||||||
|
|
||||||
@radius_x.setter
|
@radius_x.setter
|
||||||
def radius_x(self, val: float):
|
def radius_x(self, val: float) -> None:
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
raise PatternError('Radius must be non-negative')
|
raise PatternError('Radius must be non-negative')
|
||||||
self._radii[0] = val
|
self._radii[0] = val
|
||||||
@ -74,7 +74,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._radii[1]
|
return self._radii[1]
|
||||||
|
|
||||||
@radius_y.setter
|
@radius_y.setter
|
||||||
def radius_y(self, val: float):
|
def radius_y(self, val: float) -> None:
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
raise PatternError('Radius must be non-negative')
|
raise PatternError('Radius must be non-negative')
|
||||||
self._radii[1] = val
|
self._radii[1] = val
|
||||||
@ -92,7 +92,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._angles
|
return self._angles
|
||||||
|
|
||||||
@angles.setter
|
@angles.setter
|
||||||
def angles(self, val: vector2):
|
def angles(self, val: vector2) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if not val.size == 2:
|
if not val.size == 2:
|
||||||
raise PatternError('Angles must have length 2')
|
raise PatternError('Angles must have length 2')
|
||||||
@ -103,7 +103,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self.angles[0]
|
return self.angles[0]
|
||||||
|
|
||||||
@start_angle.setter
|
@start_angle.setter
|
||||||
def start_angle(self, val: float):
|
def start_angle(self, val: float) -> None:
|
||||||
self.angles = (val, self.angles[1])
|
self.angles = (val, self.angles[1])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -111,7 +111,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self.angles[1]
|
return self.angles[1]
|
||||||
|
|
||||||
@stop_angle.setter
|
@stop_angle.setter
|
||||||
def stop_angle(self, val: float):
|
def stop_angle(self, val: float) -> None:
|
||||||
self.angles = (self.angles[0], val)
|
self.angles = (self.angles[0], val)
|
||||||
|
|
||||||
# Rotation property
|
# Rotation property
|
||||||
@ -126,7 +126,7 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._rotation
|
return self._rotation
|
||||||
|
|
||||||
@rotation.setter
|
@rotation.setter
|
||||||
def rotation(self, val: float):
|
def rotation(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Rotation must be a scalar')
|
raise PatternError('Rotation must be a scalar')
|
||||||
self._rotation = val % (2 * pi)
|
self._rotation = val % (2 * pi)
|
||||||
@ -143,30 +143,31 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
return self._width
|
return self._width
|
||||||
|
|
||||||
@width.setter
|
@width.setter
|
||||||
def width(self, val: float):
|
def width(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Width must be a scalar')
|
raise PatternError('Width must be a scalar')
|
||||||
if not val > 0:
|
if not val > 0:
|
||||||
raise PatternError('Width must be positive')
|
raise PatternError('Width must be positive')
|
||||||
self._width = val
|
self._width = val
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
radii: vector2,
|
self,
|
||||||
angles: vector2,
|
radii: vector2,
|
||||||
width: float,
|
angles: vector2,
|
||||||
*,
|
width: float,
|
||||||
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
*,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
offset: vector2 = (0.0, 0.0),
|
poly_max_arclen: Optional[float] = None,
|
||||||
rotation: float = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Sequence[bool] = (False, False),
|
rotation: float = 0,
|
||||||
layer: layer_t = 0,
|
mirrored: Sequence[bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
if raw:
|
if raw:
|
||||||
@ -204,10 +205,11 @@ class Arc(Shape, metaclass=AutoSlots):
|
|||||||
new.set_locked(self.locked)
|
new.set_locked(self.locked)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: Optional[int] = None,
|
self,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = None,
|
||||||
) -> List[Polygon]:
|
poly_max_arclen: Optional[float] = None,
|
||||||
|
) -> 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
|
||||||
if poly_max_arclen is None:
|
if poly_max_arclen is None:
|
||||||
|
@ -35,26 +35,27 @@ class Circle(Shape, metaclass=AutoSlots):
|
|||||||
return self._radius
|
return self._radius
|
||||||
|
|
||||||
@radius.setter
|
@radius.setter
|
||||||
def radius(self, val: float):
|
def radius(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Radius must be a scalar')
|
raise PatternError('Radius must be a scalar')
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
raise PatternError('Radius must be non-negative')
|
raise PatternError('Radius must be non-negative')
|
||||||
self._radius = val
|
self._radius = val
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
radius: float,
|
self,
|
||||||
*,
|
radius: float,
|
||||||
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
*,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
offset: vector2 = (0.0, 0.0),
|
poly_max_arclen: Optional[float] = None,
|
||||||
layer: layer_t = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
if raw:
|
if raw:
|
||||||
@ -83,10 +84,11 @@ class Circle(Shape, metaclass=AutoSlots):
|
|||||||
new.set_locked(self.locked)
|
new.set_locked(self.locked)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: Optional[int] = None,
|
self,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = None,
|
||||||
) -> List[Polygon]:
|
poly_max_arclen: Optional[float] = None,
|
||||||
|
) -> 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
|
||||||
if poly_max_arclen is None:
|
if poly_max_arclen is None:
|
||||||
|
@ -41,7 +41,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
return self._radii
|
return self._radii
|
||||||
|
|
||||||
@radii.setter
|
@radii.setter
|
||||||
def radii(self, val: vector2):
|
def radii(self, val: vector2) -> None:
|
||||||
val = numpy.array(val).flatten()
|
val = numpy.array(val).flatten()
|
||||||
if not val.size == 2:
|
if not val.size == 2:
|
||||||
raise PatternError('Radii must have length 2')
|
raise PatternError('Radii must have length 2')
|
||||||
@ -54,7 +54,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
return self.radii[0]
|
return self.radii[0]
|
||||||
|
|
||||||
@radius_x.setter
|
@radius_x.setter
|
||||||
def radius_x(self, val: float):
|
def radius_x(self, val: float) -> None:
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
raise PatternError('Radius must be non-negative')
|
raise PatternError('Radius must be non-negative')
|
||||||
self.radii[0] = val
|
self.radii[0] = val
|
||||||
@ -64,7 +64,7 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
return self.radii[1]
|
return self.radii[1]
|
||||||
|
|
||||||
@radius_y.setter
|
@radius_y.setter
|
||||||
def radius_y(self, val: float):
|
def radius_y(self, val: float) -> None:
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
raise PatternError('Radius must be non-negative')
|
raise PatternError('Radius must be non-negative')
|
||||||
self.radii[1] = val
|
self.radii[1] = val
|
||||||
@ -82,26 +82,27 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
return self._rotation
|
return self._rotation
|
||||||
|
|
||||||
@rotation.setter
|
@rotation.setter
|
||||||
def rotation(self, val: float):
|
def rotation(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Rotation must be a scalar')
|
raise PatternError('Rotation must be a scalar')
|
||||||
self._rotation = val % pi
|
self._rotation = val % pi
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
radii: vector2,
|
self,
|
||||||
*,
|
radii: vector2,
|
||||||
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
*,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = DEFAULT_POLY_NUM_POINTS,
|
||||||
offset: vector2 = (0.0, 0.0),
|
poly_max_arclen: Optional[float] = None,
|
||||||
rotation: float = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Sequence[bool] = (False, False),
|
rotation: float = 0,
|
||||||
layer: layer_t = 0,
|
mirrored: Sequence[bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
if raw:
|
if raw:
|
||||||
@ -134,10 +135,11 @@ class Ellipse(Shape, metaclass=AutoSlots):
|
|||||||
new.set_locked(self.locked)
|
new.set_locked(self.locked)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: Optional[int] = None,
|
self,
|
||||||
poly_max_arclen: Optional[float] = None,
|
poly_num_points: Optional[int] = None,
|
||||||
) -> List[Polygon]:
|
poly_max_arclen: Optional[float] = None,
|
||||||
|
) -> 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
|
||||||
if poly_max_arclen is None:
|
if poly_max_arclen is None:
|
||||||
|
@ -46,7 +46,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self._width
|
return self._width
|
||||||
|
|
||||||
@width.setter
|
@width.setter
|
||||||
def width(self, val: float):
|
def width(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Width must be a scalar')
|
raise PatternError('Width must be a scalar')
|
||||||
if not val >= 0:
|
if not val >= 0:
|
||||||
@ -62,7 +62,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self._cap
|
return self._cap
|
||||||
|
|
||||||
@cap.setter
|
@cap.setter
|
||||||
def cap(self, val: PathCap):
|
def cap(self, val: PathCap) -> None:
|
||||||
# TODO: Document that setting cap can change cap_extensions
|
# TODO: Document that setting cap can change cap_extensions
|
||||||
self._cap = PathCap(val)
|
self._cap = PathCap(val)
|
||||||
if self.cap != PathCap.SquareCustom:
|
if self.cap != PathCap.SquareCustom:
|
||||||
@ -83,7 +83,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self._cap_extensions
|
return self._cap_extensions
|
||||||
|
|
||||||
@cap_extensions.setter
|
@cap_extensions.setter
|
||||||
def cap_extensions(self, vals: Optional[numpy.ndarray]):
|
def cap_extensions(self, vals: Optional[numpy.ndarray]) -> None:
|
||||||
custom_caps = (PathCap.SquareCustom,)
|
custom_caps = (PathCap.SquareCustom,)
|
||||||
if self.cap in custom_caps:
|
if self.cap in custom_caps:
|
||||||
if vals is None:
|
if vals is None:
|
||||||
@ -103,7 +103,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self._vertices
|
return self._vertices
|
||||||
|
|
||||||
@vertices.setter
|
@vertices.setter
|
||||||
def vertices(self, val: ArrayLike):
|
def vertices(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
||||||
if len(val.shape) < 2 or val.shape[1] != 2:
|
if len(val.shape) < 2 or val.shape[1] != 2:
|
||||||
raise PatternError('Vertices must be an Nx2 array')
|
raise PatternError('Vertices must be an Nx2 array')
|
||||||
@ -120,7 +120,7 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self.vertices[:, 0]
|
return self.vertices[:, 0]
|
||||||
|
|
||||||
@xs.setter
|
@xs.setter
|
||||||
def xs(self, val: ArrayLike):
|
def xs(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if val.size != self.vertices.shape[0]:
|
if val.size != self.vertices.shape[0]:
|
||||||
raise PatternError('Wrong number of vertices')
|
raise PatternError('Wrong number of vertices')
|
||||||
@ -135,28 +135,29 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return self.vertices[:, 1]
|
return self.vertices[:, 1]
|
||||||
|
|
||||||
@ys.setter
|
@ys.setter
|
||||||
def ys(self, val: ArrayLike):
|
def ys(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if val.size != self.vertices.shape[0]:
|
if val.size != self.vertices.shape[0]:
|
||||||
raise PatternError('Wrong number of vertices')
|
raise PatternError('Wrong number of vertices')
|
||||||
self.vertices[:, 1] = val
|
self.vertices[:, 1] = val
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
vertices: ArrayLike,
|
self,
|
||||||
width: float = 0.0,
|
vertices: ArrayLike,
|
||||||
*,
|
width: float = 0.0,
|
||||||
cap: PathCap = PathCap.Flush,
|
*,
|
||||||
cap_extensions: Optional[ArrayLike] = None,
|
cap: PathCap = PathCap.Flush,
|
||||||
offset: vector2 = (0.0, 0.0),
|
cap_extensions: Optional[ArrayLike] = None,
|
||||||
rotation: float = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Sequence[bool] = (False, False),
|
rotation: float = 0,
|
||||||
layer: layer_t = 0,
|
mirrored: Sequence[bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self._cap_extensions = None # Since .cap setter might access it
|
self._cap_extensions = None # Since .cap setter might access it
|
||||||
|
|
||||||
@ -197,16 +198,17 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def travel(travel_pairs: Tuple[Tuple[float, float]],
|
def travel(
|
||||||
width: float = 0.0,
|
travel_pairs: Tuple[Tuple[float, float]],
|
||||||
cap: PathCap = PathCap.Flush,
|
width: float = 0.0,
|
||||||
cap_extensions: Optional[Tuple[float, float]] = None,
|
cap: PathCap = PathCap.Flush,
|
||||||
offset: vector2 = (0.0, 0.0),
|
cap_extensions: Optional[Tuple[float, float]] = None,
|
||||||
rotation: float = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Sequence[bool] = (False, False),
|
rotation: float = 0,
|
||||||
layer: layer_t = 0,
|
mirrored: Sequence[bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
) -> 'Path':
|
dose: float = 1.0,
|
||||||
|
) -> 'Path':
|
||||||
"""
|
"""
|
||||||
Build a path by specifying the turn angles and travel distances
|
Build a path by specifying the turn angles and travel distances
|
||||||
rather than setting the distances directly.
|
rather than setting the distances directly.
|
||||||
@ -243,10 +245,11 @@ class Path(Shape, metaclass=AutoSlots):
|
|||||||
offset=offset, rotation=rotation, mirrored=mirrored,
|
offset=offset, rotation=rotation, mirrored=mirrored,
|
||||||
layer=layer, dose=dose)
|
layer=layer, dose=dose)
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: int = None,
|
self,
|
||||||
poly_max_arclen: float = None,
|
poly_num_points: int = None,
|
||||||
) -> List['Polygon']:
|
poly_max_arclen: float = None,
|
||||||
|
) -> List['Polygon']:
|
||||||
extensions = self._calculate_cap_extensions()
|
extensions = self._calculate_cap_extensions()
|
||||||
|
|
||||||
v = remove_colinear_vertices(self.vertices, closed_path=False)
|
v = remove_colinear_vertices(self.vertices, closed_path=False)
|
||||||
|
@ -34,7 +34,7 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return self._vertices
|
return self._vertices
|
||||||
|
|
||||||
@vertices.setter
|
@vertices.setter
|
||||||
def vertices(self, val: ArrayLike):
|
def vertices(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
||||||
if len(val.shape) < 2 or val.shape[1] != 2:
|
if len(val.shape) < 2 or val.shape[1] != 2:
|
||||||
raise PatternError('Vertices must be an Nx2 array')
|
raise PatternError('Vertices must be an Nx2 array')
|
||||||
@ -51,7 +51,7 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return self.vertices[:, 0]
|
return self.vertices[:, 0]
|
||||||
|
|
||||||
@xs.setter
|
@xs.setter
|
||||||
def xs(self, val: ArrayLike):
|
def xs(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if val.size != self.vertices.shape[0]:
|
if val.size != self.vertices.shape[0]:
|
||||||
raise PatternError('Wrong number of vertices')
|
raise PatternError('Wrong number of vertices')
|
||||||
@ -66,25 +66,26 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return self.vertices[:, 1]
|
return self.vertices[:, 1]
|
||||||
|
|
||||||
@ys.setter
|
@ys.setter
|
||||||
def ys(self, val: ArrayLike):
|
def ys(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float).flatten()
|
val = numpy.array(val, dtype=float).flatten()
|
||||||
if val.size != self.vertices.shape[0]:
|
if val.size != self.vertices.shape[0]:
|
||||||
raise PatternError('Wrong number of vertices')
|
raise PatternError('Wrong number of vertices')
|
||||||
self.vertices[:, 1] = val
|
self.vertices[:, 1] = val
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
vertices: ArrayLike,
|
self,
|
||||||
*,
|
vertices: ArrayLike,
|
||||||
offset: vector2 = (0.0, 0.0),
|
*,
|
||||||
rotation: float = 0.0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Sequence[bool] = (False, False),
|
rotation: float = 0.0,
|
||||||
layer: layer_t = 0,
|
mirrored: Sequence[bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
if raw:
|
if raw:
|
||||||
@ -115,14 +116,15 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def square(side_length: float,
|
def square(
|
||||||
*,
|
side_length: float,
|
||||||
rotation: float = 0.0,
|
*,
|
||||||
offset: vector2 = (0.0, 0.0),
|
rotation: float = 0.0,
|
||||||
layer: layer_t = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
) -> 'Polygon':
|
repetition: Optional[Repetition] = None,
|
||||||
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw a square given side_length, centered on the origin.
|
Draw a square given side_length, centered on the origin.
|
||||||
|
|
||||||
@ -148,15 +150,16 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rectangle(lx: float,
|
def rectangle(
|
||||||
ly: float,
|
lx: float,
|
||||||
*,
|
ly: float,
|
||||||
rotation: float = 0,
|
*,
|
||||||
offset: vector2 = (0.0, 0.0),
|
rotation: float = 0,
|
||||||
layer: layer_t = 0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
) -> 'Polygon':
|
repetition: Optional[Repetition] = None,
|
||||||
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw a rectangle with side lengths lx and ly, centered on the origin.
|
Draw a rectangle with side lengths lx and ly, centered on the origin.
|
||||||
|
|
||||||
@ -182,19 +185,20 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rect(*,
|
def rect(
|
||||||
xmin: Optional[float] = None,
|
*,
|
||||||
xctr: Optional[float] = None,
|
xmin: Optional[float] = None,
|
||||||
xmax: Optional[float] = None,
|
xctr: Optional[float] = None,
|
||||||
lx: Optional[float] = None,
|
xmax: Optional[float] = None,
|
||||||
ymin: Optional[float] = None,
|
lx: Optional[float] = None,
|
||||||
yctr: Optional[float] = None,
|
ymin: Optional[float] = None,
|
||||||
ymax: Optional[float] = None,
|
yctr: Optional[float] = None,
|
||||||
ly: Optional[float] = None,
|
ymax: Optional[float] = None,
|
||||||
layer: layer_t = 0,
|
ly: Optional[float] = None,
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
) -> 'Polygon':
|
repetition: Optional[Repetition] = None,
|
||||||
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw a rectangle by specifying side/center positions.
|
Draw a rectangle by specifying side/center positions.
|
||||||
|
|
||||||
@ -282,16 +286,17 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def octagon(*,
|
def octagon(
|
||||||
side_length: Optional[float] = None,
|
*,
|
||||||
inner_radius: Optional[float] = None,
|
side_length: Optional[float] = None,
|
||||||
regular: bool = True,
|
inner_radius: Optional[float] = None,
|
||||||
center: vector2 = (0.0, 0.0),
|
regular: bool = True,
|
||||||
rotation: float = 0.0,
|
center: vector2 = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
rotation: float = 0.0,
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
) -> 'Polygon':
|
repetition: Optional[Repetition] = None,
|
||||||
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw an octagon given one of (side length, inradius, circumradius).
|
Draw an octagon given one of (side length, inradius, circumradius).
|
||||||
|
|
||||||
@ -341,10 +346,11 @@ class Polygon(Shape, metaclass=AutoSlots):
|
|||||||
return poly
|
return poly
|
||||||
|
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: int = None, # unused
|
self,
|
||||||
poly_max_arclen: float = None, # unused
|
poly_num_points: int = None, # unused
|
||||||
) -> List['Polygon']:
|
poly_max_arclen: float = None, # unused
|
||||||
|
) -> List['Polygon']:
|
||||||
return [copy.deepcopy(self)]
|
return [copy.deepcopy(self)]
|
||||||
|
|
||||||
def get_bounds(self) -> numpy.ndarray:
|
def get_bounds(self) -> numpy.ndarray:
|
||||||
|
@ -47,10 +47,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
|
|||||||
--- Abstract methods
|
--- Abstract methods
|
||||||
'''
|
'''
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
num_vertices: Optional[int] = None,
|
self,
|
||||||
max_arclen: Optional[float] = None,
|
num_vertices: Optional[int] = None,
|
||||||
) -> List['Polygon']:
|
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.
|
||||||
|
|
||||||
@ -93,10 +94,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
|
|||||||
'''
|
'''
|
||||||
---- Non-abstract methods
|
---- Non-abstract methods
|
||||||
'''
|
'''
|
||||||
def manhattanize_fast(self,
|
def manhattanize_fast(
|
||||||
grid_x: ArrayLike,
|
self,
|
||||||
grid_y: ArrayLike,
|
grid_x: ArrayLike,
|
||||||
) -> List['Polygon']:
|
grid_y: ArrayLike,
|
||||||
|
) -> List['Polygon']:
|
||||||
"""
|
"""
|
||||||
Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape.
|
Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape.
|
||||||
|
|
||||||
@ -200,10 +202,11 @@ class Shape(PositionableImpl, LayerableImpl, DoseableImpl, Rotatable, Mirrorable
|
|||||||
|
|
||||||
return manhattan_polygons
|
return manhattan_polygons
|
||||||
|
|
||||||
def manhattanize(self,
|
def manhattanize(
|
||||||
grid_x: ArrayLike,
|
self,
|
||||||
grid_y: ArrayLike,
|
grid_x: ArrayLike,
|
||||||
) -> List['Polygon']:
|
grid_y: ArrayLike,
|
||||||
|
) -> List['Polygon']:
|
||||||
"""
|
"""
|
||||||
Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape.
|
Returns a list of polygons with grid-aligned ("Manhattan") edges approximating the shape.
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
return self._string
|
return self._string
|
||||||
|
|
||||||
@string.setter
|
@string.setter
|
||||||
def string(self, val: str):
|
def string(self, val: str) -> None:
|
||||||
self._string = val
|
self._string = val
|
||||||
|
|
||||||
# Height property
|
# Height property
|
||||||
@ -44,7 +44,7 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
return self._height
|
return self._height
|
||||||
|
|
||||||
@height.setter
|
@height.setter
|
||||||
def height(self, val: float):
|
def height(self, val: float) -> None:
|
||||||
if not is_scalar(val):
|
if not is_scalar(val):
|
||||||
raise PatternError('Height must be a scalar')
|
raise PatternError('Height must be a scalar')
|
||||||
self._height = val
|
self._height = val
|
||||||
@ -55,26 +55,27 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
return self._mirrored
|
return self._mirrored
|
||||||
|
|
||||||
@mirrored.setter
|
@mirrored.setter
|
||||||
def mirrored(self, val: Sequence[bool]):
|
def mirrored(self, val: Sequence[bool]) -> None:
|
||||||
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)
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
string: str,
|
self,
|
||||||
height: float,
|
string: str,
|
||||||
font_path: str,
|
height: float,
|
||||||
*,
|
font_path: str,
|
||||||
offset: vector2 = (0.0, 0.0),
|
*,
|
||||||
rotation: float = 0.0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
rotation: float = 0.0,
|
||||||
layer: layer_t = 0,
|
mirrored: Tuple[bool, bool] = (False, False),
|
||||||
dose: float = 1.0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
dose: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
raw: bool = False,
|
locked: bool = False,
|
||||||
):
|
raw: bool = False,
|
||||||
|
) -> None:
|
||||||
LockableImpl.unlock(self)
|
LockableImpl.unlock(self)
|
||||||
self.identifier = ()
|
self.identifier = ()
|
||||||
if raw:
|
if raw:
|
||||||
@ -109,10 +110,11 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
new.set_locked(self.locked)
|
new.set_locked(self.locked)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def to_polygons(self,
|
def to_polygons(
|
||||||
poly_num_points: Optional[int] = None, # unused
|
self,
|
||||||
poly_max_arclen: Optional[float] = None, # unused
|
poly_num_points: Optional[int] = None, # unused
|
||||||
) -> List[Polygon]:
|
poly_max_arclen: Optional[float] = None, # unused
|
||||||
|
) -> List[Polygon]:
|
||||||
all_polygons = []
|
all_polygons = []
|
||||||
total_advance = 0.0
|
total_advance = 0.0
|
||||||
for char in self.string:
|
for char in self.string:
|
||||||
@ -166,10 +168,11 @@ class Text(RotatableImpl, Shape, metaclass=AutoSlots):
|
|||||||
return bounds
|
return bounds
|
||||||
|
|
||||||
|
|
||||||
def get_char_as_polygons(font_path: str,
|
def get_char_as_polygons(
|
||||||
char: str,
|
font_path: str,
|
||||||
resolution: float = 48 * 64,
|
char: str,
|
||||||
) -> Tuple[List[List[List[float]]], float]:
|
resolution: float = 48 * 64,
|
||||||
|
) -> Tuple[List[List[List[float]]], float]:
|
||||||
from freetype import Face # type: ignore
|
from freetype import Face # type: ignore
|
||||||
from matplotlib.path import Path # type: ignore
|
from matplotlib.path import Path # type: ignore
|
||||||
|
|
||||||
|
@ -46,19 +46,20 @@ class SubPattern(PositionableImpl, DoseableImpl, RotatableImpl, ScalableImpl, Mi
|
|||||||
identifier: Tuple[Any, ...]
|
identifier: Tuple[Any, ...]
|
||||||
""" Arbitrary identifier, used internally by some `masque` functions. """
|
""" Arbitrary identifier, used internally by some `masque` functions. """
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(
|
||||||
pattern: Optional['Pattern'],
|
self,
|
||||||
*,
|
pattern: Optional['Pattern'],
|
||||||
offset: vector2 = (0.0, 0.0),
|
*,
|
||||||
rotation: float = 0.0,
|
offset: vector2 = (0.0, 0.0),
|
||||||
mirrored: Optional[Sequence[bool]] = None,
|
rotation: float = 0.0,
|
||||||
dose: float = 1.0,
|
mirrored: Optional[Sequence[bool]] = None,
|
||||||
scale: float = 1.0,
|
dose: float = 1.0,
|
||||||
repetition: Optional[Repetition] = None,
|
scale: float = 1.0,
|
||||||
annotations: Optional[annotations_t] = None,
|
repetition: Optional[Repetition] = None,
|
||||||
locked: bool = False,
|
annotations: Optional[annotations_t] = None,
|
||||||
identifier: Tuple[Any, ...] = (),
|
locked: bool = False,
|
||||||
) -> None:
|
identifier: Tuple[Any, ...] = (),
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
pattern: Pattern to reference.
|
pattern: Pattern to reference.
|
||||||
|
Loading…
Reference in New Issue
Block a user