more fixes and improvements
This commit is contained in:
parent
6eb4af3203
commit
c2ce9ed547
@ -3,11 +3,13 @@ from typing import Dict, TypeVar
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import numpy
|
||||
from numpy.typing import ArrayLike
|
||||
|
||||
#from .pattern import Pattern
|
||||
from .ref import Ref
|
||||
from .ports import PortList, Port
|
||||
from .utils import rotation_matrix_2d, normalize_mirror
|
||||
|
||||
#if TYPE_CHECKING:
|
||||
# from .builder import Builder, Tool
|
||||
@ -203,7 +205,7 @@ class Abstract(PortList):
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
mirror_across_x, angle = normalize_mirror(ref.mirrored)
|
||||
mirrored_across_x, angle = normalize_mirror(ref.mirrored)
|
||||
if mirrored_across_x:
|
||||
self.mirror(across_axis=0)
|
||||
self.rotate_ports(angle + ref.rotation)
|
||||
@ -224,7 +226,7 @@ class Abstract(PortList):
|
||||
|
||||
# TODO test undo_ref_transform
|
||||
"""
|
||||
mirror_across_x, angle = normalize_mirror(ref.mirrored)
|
||||
mirrored_across_x, angle = normalize_mirror(ref.mirrored)
|
||||
self.translate_ports(-ref.offset)
|
||||
self.rotate_port_offsets(-angle - ref.rotation)
|
||||
self.rotate_ports(-angle - ref.rotation)
|
||||
|
@ -13,6 +13,7 @@ from ..library import MutableLibrary
|
||||
from ..error import PortError, BuildError
|
||||
from ..ports import PortList, Port
|
||||
from ..abstract import Abstract
|
||||
from ..utils import SupportsBool
|
||||
from .tools import Tool
|
||||
from .utils import ell
|
||||
|
||||
@ -148,7 +149,7 @@ class Builder(PortList):
|
||||
@classmethod
|
||||
def interface(
|
||||
cls,
|
||||
source: Union[PortList, Mapping[str, Port]],
|
||||
source: Union[PortList, Mapping[str, Port], str],
|
||||
*,
|
||||
library: Optional[MutableLibrary] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
@ -215,7 +216,9 @@ class Builder(PortList):
|
||||
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
|
||||
tools = source.tools
|
||||
|
||||
if isinstance(source, PortList):
|
||||
if isinstance(source, str):
|
||||
orig_ports = library.abstract(source).ports
|
||||
elif isinstance(source, PortList):
|
||||
orig_ports = source.ports
|
||||
elif isinstance(source, dict):
|
||||
orig_ports = source
|
||||
@ -250,7 +253,7 @@ class Builder(PortList):
|
||||
|
||||
def plug(
|
||||
self: BB,
|
||||
other: Abstract,
|
||||
other: Union[Abstract, str],
|
||||
map_in: Dict[str, str],
|
||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||
*,
|
||||
@ -280,7 +283,7 @@ class Builder(PortList):
|
||||
having to provide `map_out` each time `plug` is called.
|
||||
|
||||
Args:
|
||||
other: A `DeviceRef` describing the device to be instatiated.
|
||||
other: An `Abstract` describing the device to be instatiated.
|
||||
map_in: Dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||
port connections between the two devices.
|
||||
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||
@ -315,6 +318,9 @@ class Builder(PortList):
|
||||
logger.error('Skipping plug() since device is dead')
|
||||
return self
|
||||
|
||||
if isinstance(other, str):
|
||||
other = self.library.abstract(other)
|
||||
|
||||
if (inherit_name
|
||||
and not map_out
|
||||
and len(map_in) == 1
|
||||
@ -345,7 +351,7 @@ class Builder(PortList):
|
||||
|
||||
def place(
|
||||
self: BB,
|
||||
other: Abstract,
|
||||
other: Union[Abstract, str],
|
||||
*,
|
||||
offset: ArrayLike = (0, 0),
|
||||
rotation: float = 0,
|
||||
@ -369,7 +375,7 @@ class Builder(PortList):
|
||||
rather than the port name on the original `pad` device.
|
||||
|
||||
Args:
|
||||
other: A `DeviceRef` describing the device to be instatiated.
|
||||
other: An `Abstract` describing the device to be instatiated.
|
||||
offset: Offset at which to place the instance. Default (0, 0).
|
||||
rotation: Rotation applied to the instance before placement. Default 0.
|
||||
pivot: Rotation is applied around this pivot point (default (0, 0)).
|
||||
@ -395,6 +401,9 @@ class Builder(PortList):
|
||||
logger.error('Skipping place() since device is dead')
|
||||
return self
|
||||
|
||||
if isinstance(other, str):
|
||||
other = self.library.abstract(other)
|
||||
|
||||
if port_map is None:
|
||||
port_map = {}
|
||||
|
||||
@ -508,7 +517,7 @@ class Builder(PortList):
|
||||
def path(
|
||||
self: BB,
|
||||
portspec: str,
|
||||
ccw: Optional[bool],
|
||||
ccw: Optional[SupportsBool],
|
||||
length: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -529,7 +538,7 @@ class Builder(PortList):
|
||||
def path_to(
|
||||
self: BB,
|
||||
portspec: str,
|
||||
ccw: Optional[bool],
|
||||
ccw: Optional[SupportsBool],
|
||||
position: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -563,7 +572,7 @@ class Builder(PortList):
|
||||
def mpath(
|
||||
self: BB,
|
||||
portspec: Union[str, Sequence[str]],
|
||||
ccw: Optional[bool],
|
||||
ccw: Optional[SupportsBool],
|
||||
*,
|
||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
||||
set_rotation: Optional[float] = None,
|
||||
@ -611,4 +620,13 @@ class Builder(PortList):
|
||||
|
||||
# TODO def path_join() and def bus_join()?
|
||||
|
||||
def flatten(self: BB) -> BB:
|
||||
"""
|
||||
Flatten the contained pattern, using the contained library to resolve references.
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
self.pattern.flatten(self.library)
|
||||
return self
|
||||
|
||||
|
@ -5,15 +5,14 @@ Functions for writing port data into a Pattern (`dev2pat`) and retrieving it (`p
|
||||
the port locations. This particular approach is just a sensible default; feel free to
|
||||
to write equivalent functions for your own format or alternate storage methods.
|
||||
"""
|
||||
from typing import Sequence, Optional, Mapping, Tuple, Dict
|
||||
from typing import Sequence, Optional, Mapping
|
||||
import logging
|
||||
|
||||
import numpy
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..pattern import Pattern
|
||||
from ..label import Label
|
||||
from ..utils import rotation_matrix_2d, layer_t
|
||||
from ..utils import layer_t
|
||||
from ..ports import Port
|
||||
from ..error import PatternError
|
||||
from ..library import Library, WrapROLibrary
|
||||
@ -79,7 +78,6 @@ def pat2dev(
|
||||
to contain their own port info without interfering with supercells'
|
||||
port data.
|
||||
Default True.
|
||||
blacklist: If a cell name appears in the blacklist, do not ea
|
||||
|
||||
Returns:
|
||||
The updated `pattern`. Port labels are not removed.
|
||||
@ -99,6 +97,8 @@ def pat2dev(
|
||||
# Load ports for all subpatterns, and use any we find
|
||||
found_ports = False
|
||||
for target in set(rr.target for rr in pattern.refs):
|
||||
if target is None:
|
||||
continue
|
||||
pp = pat2dev(
|
||||
layers=layers,
|
||||
library=library,
|
||||
@ -106,7 +106,6 @@ def pat2dev(
|
||||
name=target,
|
||||
max_depth=max_depth - 1,
|
||||
skip_subcells=skip_subcells,
|
||||
blacklist=blacklist + {name},
|
||||
)
|
||||
found_ports |= bool(pp.ports)
|
||||
|
||||
@ -114,6 +113,8 @@ def pat2dev(
|
||||
return pattern
|
||||
|
||||
for ref in pattern.refs:
|
||||
if ref.target is None:
|
||||
continue
|
||||
aa = library.abstract(ref.target)
|
||||
if not aa.ports:
|
||||
continue
|
||||
|
@ -3,6 +3,8 @@ Tools are objects which dynamically generate simple single-use devices (e.g. wir
|
||||
"""
|
||||
from typing import TYPE_CHECKING, Optional, Sequence
|
||||
|
||||
from ..utils import SupportsBool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..pattern import Pattern
|
||||
|
||||
@ -10,7 +12,7 @@ if TYPE_CHECKING:
|
||||
class Tool:
|
||||
def path(
|
||||
self,
|
||||
ccw: Optional[bool],
|
||||
ccw: Optional[SupportsBool],
|
||||
length: float,
|
||||
*,
|
||||
in_ptype: Optional[str] = None,
|
||||
|
@ -6,7 +6,7 @@ import numpy
|
||||
from numpy import pi
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
||||
from ..utils import rotation_matrix_2d
|
||||
from ..utils import rotation_matrix_2d, SupportsBool
|
||||
from ..error import BuildError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
||||
|
||||
def ell(
|
||||
ports: Mapping[str, 'Port'],
|
||||
ccw: Optional[bool],
|
||||
ccw: Optional[SupportsBool],
|
||||
bound_type: str,
|
||||
bound: Union[float, ArrayLike],
|
||||
*,
|
||||
|
@ -571,7 +571,7 @@ def load_libraryfile(
|
||||
*,
|
||||
use_mmap: bool = True,
|
||||
full_load: bool = False,
|
||||
postprocess: Optional[Callable[[Library, str], Pattern]] = None
|
||||
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
||||
"""
|
||||
Wrapper for `load_library()` that takes a filename or path instead of a stream.
|
||||
|
@ -5,7 +5,7 @@ Library classes for managing unique name->pattern mappings and
|
||||
# TODO documentn all library classes
|
||||
# TODO toplevel documentation of library, classes, and abstracts
|
||||
"""
|
||||
from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING
|
||||
from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING, cast
|
||||
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
|
||||
import logging
|
||||
import base64
|
||||
@ -396,14 +396,14 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
if pattern is not original_pattern:
|
||||
name = hierarchy[-1]
|
||||
if not isintance(self, MutableLibrary):
|
||||
if not isinstance(self, MutableLibrary):
|
||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||
' but the library is immutable')
|
||||
if name is None:
|
||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||
' but no top-level name was provided in `hierarchy`')
|
||||
|
||||
self.set_const(name, pattern)
|
||||
cast(MutableLibrary, self).set_const(name, pattern)
|
||||
|
||||
return self
|
||||
|
||||
|
@ -13,9 +13,9 @@ from numpy.typing import NDArray, ArrayLike
|
||||
# .visualize imports matplotlib and matplotlib.collections
|
||||
|
||||
from .ref import Ref
|
||||
from .shapes import Shape
|
||||
from .shapes import Shape, Polygon
|
||||
from .label import Label
|
||||
from .utils import rotation_matrix_2d, AutoSlots, annotations_t
|
||||
from .utils import rotation_matrix_2d, annotations_t
|
||||
from .error import PatternError
|
||||
from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable
|
||||
from .ports import Port, PortList
|
||||
@ -24,7 +24,7 @@ from .ports import Port, PortList
|
||||
P = TypeVar('P', bound='Pattern')
|
||||
|
||||
|
||||
class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
"""
|
||||
2D layout consisting of some set of shapes, labels, and references to other Pattern objects
|
||||
(via Ref). Shapes are assumed to inherit from masque.shapes.Shape or provide equivalent functions.
|
||||
@ -534,6 +534,21 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
self.refs.append(Ref(*args, **kwargs))
|
||||
return self
|
||||
|
||||
def rect(self: P, *args: Any, **kwargs: Any) -> P:
|
||||
"""
|
||||
Convenience function which calls `Polygon.rect` to construct a
|
||||
rectangle and adds it to this pattern.
|
||||
|
||||
Args:
|
||||
*args: Passed to `Polygon.rect()`
|
||||
**kwargs: Passed to `Polygon.rect()`
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
self.shapes.append(Polygon.rect(*args, **kwargs))
|
||||
return self
|
||||
|
||||
def flatten(
|
||||
self: P,
|
||||
library: Mapping[str, P],
|
||||
|
@ -11,7 +11,7 @@ from numpy import pi
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
||||
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
|
||||
from .utils import AutoSlots, rotate_offsets_around
|
||||
from .utils import rotate_offsets_around
|
||||
from .error import PortError
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ PL = TypeVar('PL', bound='PortList')
|
||||
PL2 = TypeVar('PL2', bound='PortList')
|
||||
|
||||
|
||||
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, metaclass=AutoSlots):
|
||||
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
||||
"""
|
||||
A point at which a `Device` can be snapped to another `Device`.
|
||||
|
||||
@ -37,7 +37,11 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, met
|
||||
|
||||
The `ptype` is an arbitrary string, default of `unk` (unknown).
|
||||
"""
|
||||
__slots__ = ('ptype', '_rotation')
|
||||
__slots__ = (
|
||||
'ptype', '_rotation',
|
||||
# inherited:
|
||||
'_offset',
|
||||
)
|
||||
|
||||
_rotation: Optional[float]
|
||||
""" radians counterclockwise from +x, pointing into device body.
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Various helper functions, type definitions, etc.
|
||||
"""
|
||||
from .types import layer_t, annotations_t
|
||||
from .types import layer_t, annotations_t, SupportsBool
|
||||
|
||||
from .array import is_scalar
|
||||
from .autoslots import AutoSlots
|
||||
|
@ -1,8 +1,13 @@
|
||||
"""
|
||||
Type definitions
|
||||
"""
|
||||
from typing import Union, Tuple, Dict, List
|
||||
from typing import Union, Tuple, Dict, List, Protocol
|
||||
|
||||
|
||||
layer_t = Union[int, Tuple[int, int], str]
|
||||
annotations_t = Dict[str, List[Union[int, float, str]]]
|
||||
|
||||
|
||||
class SupportsBool(Protocol):
|
||||
def __bool__(self) -> bool:
|
||||
...
|
||||
|
Loading…
Reference in New Issue
Block a user