Compare commits
4 Commits
2229ee5d25
...
73ce794fec
Author | SHA1 | Date | |
---|---|---|---|
73ce794fec | |||
3a6807707b | |||
1bdb998085 | |||
668d4b5d8b |
30
README.md
30
README.md
@ -33,9 +33,12 @@ pip install 'masque[oasis,dxf,svg,visualization,text]'
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
A layout consists of a hierarchy of `Pattern`s stored in a single `Library`.
|
A layout consists of a hierarchy of `Pattern`s stored in a single `Library`.
|
||||||
Each `Pattern` can contain `Ref`s pointing at other patterns, `Shape`s, and `Label`s.
|
Each `Pattern` can contain `Ref`s pointing at other patterns, `Shape`s, `Label`s, and `Port`s.
|
||||||
|
|
||||||
|
|
||||||
`masque` departs from several "classic" GDSII paradigms:
|
`masque` departs from several "classic" GDSII paradigms:
|
||||||
|
- A `Pattern` object does not store its own name. A name is only assigned when the pattern is placed
|
||||||
|
into a `Library`, which is effectively a name->`Pattern` mapping.
|
||||||
- Layer info for `Shape`ss and `Label`s is not stored in the individual shape and label objects.
|
- Layer info for `Shape`ss and `Label`s is not stored in the individual shape and label objects.
|
||||||
Instead, the layer is determined by the key for the container dict (e.g. `pattern.shapes[layer]`).
|
Instead, the layer is determined by the key for the container dict (e.g. `pattern.shapes[layer]`).
|
||||||
* This simplifies many common tasks: filtering `Shape`s by layer, remapping layers, and checking if
|
* This simplifies many common tasks: filtering `Shape`s by layer, remapping layers, and checking if
|
||||||
@ -70,6 +73,31 @@ Each `Pattern` can contain `Ref`s pointing at other patterns, `Shape`s, and `Lab
|
|||||||
* Ports can be exported into/imported from `Label`s stored directly in the layout,
|
* Ports can be exported into/imported from `Label`s stored directly in the layout,
|
||||||
editable from standard tools (e.g. KLayout). A default format is provided.
|
editable from standard tools (e.g. KLayout). A default format is provided.
|
||||||
|
|
||||||
|
In one important way, `masque` stays very orthodox:
|
||||||
|
References are accomplished by listing the target's name, not its `Pattern` object.
|
||||||
|
|
||||||
|
- The main downside of this is that any operations that traverse the hierarchy require
|
||||||
|
both the `Pattern` and the `Library` which is contains its reference targets.
|
||||||
|
- This guarantees that names within a `Library` remain unique at all times.
|
||||||
|
* Since this can be tedious in cases where you don't actually care about the name of a
|
||||||
|
pattern, patterns whose names start with `SINGLE_USE_PREFIX` (default: an underscore)
|
||||||
|
may be silently renamed in order to maintain uniqueness.
|
||||||
|
See `masque.library.SINGLE_USE_PREFIX`, `masque.library._rename_patterns()`,
|
||||||
|
and `ILibrary.add()` for more details.
|
||||||
|
- Having all patterns accessible through the `Library` avoids having to perform a
|
||||||
|
tree traversal for every operation which needs to touch all `Pattern` objects
|
||||||
|
(e.g. deleting a layer everywhere or scaling all patterns).
|
||||||
|
- Since `Pattern` doesn't know its own name, you can't create a reference by passing in
|
||||||
|
a `Pattern` object -- you need to know its name.
|
||||||
|
- You *can* reference a `Pattern` before it is created, so long as you have already decided
|
||||||
|
on its name.
|
||||||
|
- Functions like `Pattern.place()` and `Pattern.plug()` need to receive a pattern's name
|
||||||
|
in order to create a reference, but they also need to access the pattern's ports.
|
||||||
|
* One way to provide this data is through an `Abstract`, generated via
|
||||||
|
`Library.abstract()` or through a `Library.abstract_view()`.
|
||||||
|
* Another way is use `Builder.place()` or `Builder.plug()`, which automatically creates
|
||||||
|
an `Abstract` from its internally-referenced `Library`.
|
||||||
|
|
||||||
|
|
||||||
## Glossary
|
## Glossary
|
||||||
- `Library`: A collection of named cells. OASIS or GDS "library" or file.
|
- `Library`: A collection of named cells. OASIS or GDS "library" or file.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
masque 2D CAD library
|
masque 2D CAD library
|
||||||
|
|
||||||
masque is an attempt to make a relatively small library for designing lithography
|
masque is an attempt to make a relatively compact library for designing lithography
|
||||||
masks. The general idea is to implement something resembling the GDSII and OASIS file-formats,
|
masks. The general idea is to implement something resembling the GDSII and OASIS file-formats,
|
||||||
but with some additional vectorized element types (eg. ellipses, not just polygons), and the
|
but with some additional vectorized element types (eg. ellipses, not just polygons), and the
|
||||||
ability to interface with multiple file formats.
|
ability to interface with multiple file formats.
|
||||||
@ -20,11 +20,12 @@
|
|||||||
NOTES ON INTERNALS
|
NOTES ON INTERNALS
|
||||||
==========================
|
==========================
|
||||||
- Many of `masque`'s classes make use of `__slots__` to make them faster / smaller.
|
- Many of `masque`'s classes make use of `__slots__` to make them faster / smaller.
|
||||||
Since `__slots__` doesn't play well with multiple inheritance, the `masque.utils.AutoSlots`
|
Since `__slots__` doesn't play well with multiple inheritance, often they are left
|
||||||
metaclass is used to auto-generate slots based on superclass type annotations.
|
empty for superclasses and it is the subclass's responsibility to set them correctly.
|
||||||
- File I/O submodules are imported by `masque.file` to avoid creating hard dependencies on
|
- File I/O submodules are not imported by `masque.file` to avoid creating hard dependencies
|
||||||
external file-format reader/writers
|
on external file-format reader/writers
|
||||||
|
- Try to accept the broadest-possible inputs: e.g., don't demand an `ILibraryView` if you
|
||||||
|
can accept a `Mapping[str, Pattern]` and wrap it in a `LibraryView` internally.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .utils import layer_t, annotations_t, SupportsBool
|
from .utils import layer_t, annotations_t, SupportsBool
|
||||||
|
@ -11,7 +11,7 @@ from numpy import pi
|
|||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
from ..library import ILibrary
|
from ..library import ILibrary, SINGLE_USE_PREFIX
|
||||||
from ..error import PortError, BuildError
|
from ..error import PortError, BuildError
|
||||||
from ..ports import PortList, Port
|
from ..ports import PortList, Port
|
||||||
from ..abstract import Abstract
|
from ..abstract import Abstract
|
||||||
@ -422,7 +422,7 @@ class Pather(Builder):
|
|||||||
set_rotation: float | None = None,
|
set_rotation: float | None = None,
|
||||||
tool_port_names: tuple[str, str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = SINGLE_USE_PREFIX + 'mpath',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
|
@ -15,7 +15,7 @@ from ..utils import SupportsBool, rotation_matrix_2d, layer_t
|
|||||||
from ..ports import Port
|
from ..ports import Port
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
from ..abstract import Abstract
|
from ..abstract import Abstract
|
||||||
from ..library import ILibrary, Library
|
from ..library import ILibrary, Library, SINGLE_USE_PREFIX
|
||||||
from ..error import BuildError
|
from ..error import BuildError
|
||||||
|
|
||||||
|
|
||||||
@ -288,13 +288,13 @@ class BasicTool(Tool, metaclass=ABCMeta):
|
|||||||
)
|
)
|
||||||
|
|
||||||
gen_straight, sport_in, sport_out = self.straight
|
gen_straight, sport_in, sport_out = self.straight
|
||||||
tree, pat = Library.mktree('_path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.add_port_pair(names=port_names)
|
pat.add_port_pair(names=port_names)
|
||||||
if data.in_transition:
|
if data.in_transition:
|
||||||
ipat, iport_theirs, _iport_ours = data.in_transition
|
ipat, iport_theirs, _iport_ours = data.in_transition
|
||||||
pat.plug(ipat, {port_names[1]: iport_theirs})
|
pat.plug(ipat, {port_names[1]: iport_theirs})
|
||||||
if not numpy.isclose(data.straight_length, 0):
|
if not numpy.isclose(data.straight_length, 0):
|
||||||
straight = tree <= {'_straight': gen_straight(data.straight_length)}
|
straight = tree <= {SINGLE_USE_PREFIX + 'straight': gen_straight(data.straight_length)}
|
||||||
pat.plug(straight, {port_names[1]: sport_in})
|
pat.plug(straight, {port_names[1]: sport_in})
|
||||||
if data.ccw is not None:
|
if data.ccw is not None:
|
||||||
bend, bport_in, bport_out = self.bend
|
bend, bport_in, bport_out = self.bend
|
||||||
@ -391,7 +391,7 @@ class BasicTool(Tool, metaclass=ABCMeta):
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
) -> ILibrary:
|
) -> ILibrary:
|
||||||
|
|
||||||
tree, pat = Library.mktree('_path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.add_port_pair(names=(port_names[0], port_names[1]))
|
pat.add_port_pair(names=(port_names[0], port_names[1]))
|
||||||
|
|
||||||
gen_straight, sport_in, _sport_out = self.straight
|
gen_straight, sport_in, _sport_out = self.straight
|
||||||
@ -408,7 +408,7 @@ class BasicTool(Tool, metaclass=ABCMeta):
|
|||||||
if append:
|
if append:
|
||||||
pat.plug(straight_pat, {port_names[1]: sport_in}, append=True)
|
pat.plug(straight_pat, {port_names[1]: sport_in}, append=True)
|
||||||
else:
|
else:
|
||||||
straight = tree <= {'_straight': straight_pat}
|
straight = tree <= {SINGLE_USE_PREFIX + 'straight': straight_pat}
|
||||||
pat.plug(straight, {port_names[1]: sport_in}, append=True)
|
pat.plug(straight, {port_names[1]: sport_in}, append=True)
|
||||||
if ccw is not None:
|
if ccw is not None:
|
||||||
bend, bport_in, bport_out = self.bend
|
bend, bport_in, bport_out = self.bend
|
||||||
@ -463,7 +463,7 @@ class PathTool(Tool, metaclass=ABCMeta):
|
|||||||
out_ptype=out_ptype,
|
out_ptype=out_ptype,
|
||||||
)
|
)
|
||||||
|
|
||||||
tree, pat = Library.mktree('_path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.path(layer=self.layer, width=self.width, vertices=[(0, 0), (length, 0)])
|
pat.path(layer=self.layer, width=self.width, vertices=[(0, 0), (length, 0)])
|
||||||
|
|
||||||
if ccw is None:
|
if ccw is None:
|
||||||
@ -543,7 +543,7 @@ class PathTool(Tool, metaclass=ABCMeta):
|
|||||||
# If the path ends in a bend, we need to add the final vertex
|
# If the path ends in a bend, we need to add the final vertex
|
||||||
path_vertices.append(batch[-1].end_port.offset)
|
path_vertices.append(batch[-1].end_port.offset)
|
||||||
|
|
||||||
tree, pat = Library.mktree('_path')
|
tree, pat = Library.mktree(SINGLE_USE_PREFIX + 'path')
|
||||||
pat.path(layer=self.layer, width=self.width, vertices=path_vertices)
|
pat.path(layer=self.layer, width=self.width, vertices=path_vertices)
|
||||||
pat.ports = {
|
pat.ports = {
|
||||||
port_names[0]: batch[0].start_port.copy().rotate(pi),
|
port_names[0]: batch[0].start_port.copy().rotate(pi),
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
Library classes for managing unique name->pattern mappings and
|
Library classes for managing unique name->pattern mappings and deferred loading or execution.
|
||||||
deferred loading or creation.
|
|
||||||
|
|
||||||
# TODO documennt all library classes
|
Classes include:
|
||||||
# TODO toplevel documentation of library, classes, and abstracts
|
- `ILibraryView`: Defines a general interface for read-only name->pattern mappings.
|
||||||
|
- `LibraryView`: An implementation of `ILibraryView` backed by an arbitrary `Mapping`.
|
||||||
|
Can be used to wrap any arbitrary `Mapping` to give it all the functionality in `ILibraryView`
|
||||||
|
- `ILibrary`: Defines a general interface for mutable name->pattern mappings.
|
||||||
|
- `Library`: An implementation of `ILibrary` backed by an arbitrary `MutableMapping`.
|
||||||
|
Can be used to wrap any arbitrary `MutableMapping` to give it all the functionality in `ILibrary`.
|
||||||
|
By default, uses a `dict` as the underylingmapping.
|
||||||
|
- `LazyLibrary`: An implementation of `ILibrary` which enables on-demand loading or generation
|
||||||
|
of patterns.
|
||||||
|
- `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked
|
||||||
|
library. Generated with `ILibraryView.abstract_view()`.
|
||||||
"""
|
"""
|
||||||
from typing import Callable, Self, Type, TYPE_CHECKING, cast
|
from typing import Callable, Self, Type, TYPE_CHECKING, cast
|
||||||
from typing import Iterator, Mapping, MutableMapping, Sequence
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
||||||
@ -35,15 +44,24 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
visitor_function_t = Callable[..., 'Pattern']
|
visitor_function_t = Callable[..., 'Pattern']
|
||||||
|
|
||||||
|
SINGLE_USE_PREFIX = '_'
|
||||||
|
"""
|
||||||
|
Names starting with this prefix are assumed to refer to single-use patterns,
|
||||||
|
which may be renamed automatically by `ILibrary.add()` (via
|
||||||
|
`rename_theirs=_rename_patterns()` )
|
||||||
|
"""
|
||||||
|
# TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
|
||||||
|
|
||||||
|
|
||||||
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
||||||
"""
|
"""
|
||||||
The default `rename_theirs` function for `ILibrary.add`.
|
The default `rename_theirs` function for `ILibrary.add`.
|
||||||
|
|
||||||
Treats names starting with an underscore as "one-offs" for which name conflicts
|
Treats names starting with `SINGLE_USE_PREFIX` (default: one underscore) as
|
||||||
should be automatically resolved. Conflicts are resolved by calling
|
"one-offs" for which name conflicts should be automatically resolved.
|
||||||
`lib.get_name(name.split('$')[0])`.
|
Conflicts are resolved by calling `lib.get_name(SINGLE_USE_PREFIX + stem)`
|
||||||
Names without a leading underscore are directly returned.
|
where `stem = name.removeprefix(SINGLE_USE_PREFIX).split('$')[0]`.
|
||||||
|
Names lacking the prefix are directly returned (not renamed).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
lib: The library into which `name` is to be added (but is presumed to conflict)
|
lib: The library into which `name` is to be added (but is presumed to conflict)
|
||||||
@ -52,11 +70,11 @@ def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The new name, not guaranteed to be conflict-free!
|
The new name, not guaranteed to be conflict-free!
|
||||||
"""
|
"""
|
||||||
if not name.startswith('_'): # TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
|
if not name.startswith(SINGLE_USE_PREFIX):
|
||||||
return name
|
return name
|
||||||
|
|
||||||
stem = name.split('$')[0]
|
stem = name.removeprefix(SINGLE_USE_PREFIX).split('$')[0]
|
||||||
return lib.get_name(stem)
|
return lib.get_name(SINGLE_USE_PREFIX + stem)
|
||||||
|
|
||||||
|
|
||||||
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
@ -284,7 +302,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def get_name(
|
def get_name(
|
||||||
self,
|
self,
|
||||||
name: str = '__',
|
name: str = SINGLE_USE_PREFIX * 2,
|
||||||
sanitize: bool = True,
|
sanitize: bool = True,
|
||||||
max_length: int = 32,
|
max_length: int = 32,
|
||||||
quiet: bool | None = None,
|
quiet: bool | None = None,
|
||||||
@ -295,17 +313,17 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
This function may be overridden in a subclass or monkey-patched to fit the caller's requirements.
|
This function may be overridden in a subclass or monkey-patched to fit the caller's requirements.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: Preferred name for the pattern. Default '__'.
|
name: Preferred name for the pattern. Default is `SINGLE_USE_PREFIX * 2`.
|
||||||
sanitize: Allows only alphanumeric charaters and _?$. Replaces invalid characters with underscores.
|
sanitize: Allows only alphanumeric charaters and _?$. Replaces invalid characters with underscores.
|
||||||
max_length: Names longer than this will be truncated.
|
max_length: Names longer than this will be truncated.
|
||||||
quiet: If `True`, suppress log messages. Default `None` suppresses messages only if
|
quiet: If `True`, suppress log messages. Default `None` suppresses messages only if
|
||||||
the name starts with an underscore.
|
the name starts with `SINGLE_USE_PREFIX`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Name, unique within this library.
|
Name, unique within this library.
|
||||||
"""
|
"""
|
||||||
if quiet is None:
|
if quiet is None:
|
||||||
quiet = name.startswith('_')
|
quiet = name.startswith(SINGLE_USE_PREFIX)
|
||||||
|
|
||||||
if sanitize:
|
if sanitize:
|
||||||
# Remove invalid characters
|
# Remove invalid characters
|
||||||
@ -316,7 +334,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
ii = 0
|
ii = 0
|
||||||
suffixed_name = sanitized_name
|
suffixed_name = sanitized_name
|
||||||
while suffixed_name in self or suffixed_name == '':
|
while suffixed_name in self or suffixed_name == '':
|
||||||
suffix = base64.b64encode(struct.pack('>Q', ii), b'$?').decode('ASCII')
|
suffix = base64.b64encode(struct.pack('>Q', ii), altchars=b'$?').decode('ASCII')
|
||||||
|
|
||||||
suffixed_name = sanitized_name + '$' + suffix[:-1].lstrip('A')
|
suffixed_name = sanitized_name + '$' + suffix[:-1].lstrip('A')
|
||||||
ii += 1
|
ii += 1
|
||||||
@ -602,16 +620,14 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
If `mutate_other=False` (default), all changes are made to a deepcopy of `other`.
|
If `mutate_other=False` (default), all changes are made to a deepcopy of `other`.
|
||||||
|
|
||||||
By default, `rename_theirs` makes no changes to the name (causing a `LibraryError`) unless the
|
By default, `rename_theirs` makes no changes to the name (causing a `LibraryError`) unless the
|
||||||
name starts with an underscore. Underscored names are truncated to before their first '$'
|
name starts with `SINGLE_USE_PREFIX`. Prefixed names are truncated to before their first
|
||||||
and then passed to `self.get_name()` to create a new unique name.
|
non-prefix '$' and then passed to `self.get_name()` to create a new unique name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: The library to insert keys from.
|
other: The library to insert keys from.
|
||||||
rename_theirs: Called as rename_theirs(self, name) for each duplicate name
|
rename_theirs: Called as rename_theirs(self, name) for each duplicate name
|
||||||
encountered in `other`. Should return the new name for the pattern in
|
encountered in `other`. Should return the new name for the pattern in
|
||||||
`other`.
|
`other`. See above for default behavior.
|
||||||
Default is effectively
|
|
||||||
`self.get_name(name.split('$')[0]) if name.startswith('_') else name`
|
|
||||||
mutate_other: If `True`, modify the original library and its contained patterns
|
mutate_other: If `True`, modify the original library and its contained patterns
|
||||||
(e.g. when renaming patterns and updating refs). Otherwise, operate on a deepcopy
|
(e.g. when renaming patterns and updating refs). Otherwise, operate on a deepcopy
|
||||||
(default).
|
(default).
|
||||||
@ -703,7 +719,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
exclude_types: Shape types passed in this argument are always left untouched, for
|
exclude_types: Shape types passed in this argument are always left untouched, for
|
||||||
speed or convenience. Default: `(shapes.Polygon,)`
|
speed or convenience. Default: `(shapes.Polygon,)`
|
||||||
label2name: Given a label tuple as returned by `shape.normalized_form(...)`, pick
|
label2name: Given a label tuple as returned by `shape.normalized_form(...)`, pick
|
||||||
a name for the generated pattern. Default `self.get_name('_shape')`.
|
a name for the generated pattern.
|
||||||
|
Default `self.get_name(SINGLE_USE_PREIX + 'shape')`.
|
||||||
threshold: Only replace shapes with refs if there will be at least this many
|
threshold: Only replace shapes with refs if there will be at least this many
|
||||||
instances.
|
instances.
|
||||||
|
|
||||||
@ -720,8 +737,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
if label2name is None:
|
if label2name is None:
|
||||||
def label2name(label):
|
def label2name(label):
|
||||||
return self.get_name('_shape')
|
return self.get_name(SINGLE_USE_PREFIX + 'shape')
|
||||||
#label2name = lambda label: self.get_name('_shape')
|
|
||||||
|
|
||||||
shape_counts: MutableMapping[tuple, int] = defaultdict(int)
|
shape_counts: MutableMapping[tuple, int] = defaultdict(int)
|
||||||
shape_funcs = {}
|
shape_funcs = {}
|
||||||
@ -801,7 +817,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
name_func: Function f(this_pattern, shape) which generates a name for the
|
name_func: Function f(this_pattern, shape) which generates a name for the
|
||||||
wrapping pattern. Default is `self.get_name('_rep')`.
|
wrapping pattern.
|
||||||
|
Default is `self.get_name(SINGLE_USE_PREFIX + 'rep')`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
@ -810,8 +827,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
if name_func is None:
|
if name_func is None:
|
||||||
def name_func(_pat, _shape):
|
def name_func(_pat, _shape):
|
||||||
return self.get_name('_rep')
|
return self.get_name(SINGLE_USE_PREFIX = 'rep')
|
||||||
#name_func = lambda _pat, _shape: self.get_name('_rep')
|
|
||||||
|
|
||||||
for pat in tuple(self.values()):
|
for pat in tuple(self.values()):
|
||||||
for layer in pat.shapes:
|
for layer in pat.shapes:
|
||||||
|
@ -15,4 +15,4 @@ from .transform import rotation_matrix_2d, normalize_mirror, rotate_offsets_arou
|
|||||||
|
|
||||||
from . import ports2data
|
from . import ports2data
|
||||||
|
|
||||||
#from . import pack2d
|
from . import pack2d
|
||||||
|
Loading…
x
Reference in New Issue
Block a user