modernize type annotations
This commit is contained in:
parent
dfd745a76b
commit
c9402500e2
@ -1,4 +1,4 @@
|
|||||||
from typing import Tuple, Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# TODO update tutorials
|
# TODO update tutorials
|
||||||
from typing import Tuple, Sequence, Dict, Mapping
|
from typing import Sequence, Mapping
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
@ -43,7 +43,7 @@ def perturbed_l3(
|
|||||||
trench_layer: layer_t = (1, 0),
|
trench_layer: layer_t = (1, 0),
|
||||||
shifts_a: Sequence[float] = (0.15, 0, 0.075),
|
shifts_a: Sequence[float] = (0.15, 0, 0.075),
|
||||||
shifts_r: Sequence[float] = (1.0, 1.0, 1.0),
|
shifts_r: Sequence[float] = (1.0, 1.0, 1.0),
|
||||||
xy_size: Tuple[int, int] = (10, 10),
|
xy_size: tuple[int, int] = (10, 10),
|
||||||
perturbed_radius: float = 1.1,
|
perturbed_radius: float = 1.1,
|
||||||
trench_width: float = 1200,
|
trench_width: float = 1200,
|
||||||
) -> Pattern:
|
) -> Pattern:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Tuple, Sequence, Callable
|
from typing import Sequence, Callable
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -116,12 +116,12 @@ if __name__ == '__main__':
|
|||||||
# other: Pattern,
|
# other: Pattern,
|
||||||
# label_layer: layer_t = 'WATLAYER',
|
# label_layer: layer_t = 'WATLAYER',
|
||||||
# *,
|
# *,
|
||||||
# port_map: Optional[Dict[str, Optional[str]]] = None,
|
# port_map: Dict[str, str | None] | None = None,
|
||||||
# **kwargs,
|
# **kwargs,
|
||||||
# ) -> 'prout':
|
# ) -> 'prout':
|
||||||
#
|
#
|
||||||
# Pattern.place(self, other, port_map=port_map, **kwargs)
|
# Pattern.place(self, other, port_map=port_map, **kwargs)
|
||||||
# name: Optional[str]
|
# name: str | None
|
||||||
# for name in other.ports:
|
# for name in other.ports:
|
||||||
# if port_map:
|
# if port_map:
|
||||||
# assert(name is not None)
|
# assert(name is not None)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Routines for creating normalized 2D lattices and common photonic crystal
|
Routines for creating normalized 2D lattices and common photonic crystal
|
||||||
cavity designs.
|
cavity designs.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Tuple
|
from typing import Sequence
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike, NDArray
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
from typing import Dict, TypeVar
|
from typing import TypeVar
|
||||||
#from typing import Union, Optional, MutableMapping, TYPE_CHECKING
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
#from .pattern import Pattern
|
|
||||||
from .ref import Ref
|
from .ref import Ref
|
||||||
from .ports import PortList, Port
|
from .ports import PortList, Port
|
||||||
from .utils import rotation_matrix_2d, normalize_mirror
|
from .utils import rotation_matrix_2d, normalize_mirror
|
||||||
@ -28,21 +26,21 @@ class Abstract(PortList):
|
|||||||
name: str
|
name: str
|
||||||
""" Name of the pattern this device references """
|
""" Name of the pattern this device references """
|
||||||
|
|
||||||
_ports: Dict[str, Port]
|
_ports: dict[str, Port]
|
||||||
""" Uniquely-named ports which can be used to instances together"""
|
""" Uniquely-named ports which can be used to instances together"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ports(self) -> Dict[str, Port]:
|
def ports(self) -> dict[str, Port]:
|
||||||
return self._ports
|
return self._ports
|
||||||
|
|
||||||
@ports.setter
|
@ports.setter
|
||||||
def ports(self, value: Dict[str, Port]) -> None:
|
def ports(self, value: dict[str, Port]) -> None:
|
||||||
self._ports = value
|
self._ports = value
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
ports: Dict[str, Port],
|
ports: dict[str, Port],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.ports = copy.deepcopy(ports)
|
self.ports = copy.deepcopy(ports)
|
||||||
@ -50,7 +48,7 @@ class Abstract(PortList):
|
|||||||
# def build(
|
# def build(
|
||||||
# self,
|
# self,
|
||||||
# library: 'MutableLibrary',
|
# library: 'MutableLibrary',
|
||||||
# tools: Union[None, 'Tool', MutableMapping[Optional[str], 'Tool']] = None,
|
# tools: None | 'Tool' | MutableMapping[str | None, 'Tool'] = None,
|
||||||
# ) -> 'Builder':
|
# ) -> 'Builder':
|
||||||
# """
|
# """
|
||||||
# Begin building a new device around an instance of the current device
|
# Begin building a new device around an instance of the current device
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from typing import Dict, Tuple, Union, TypeVar, Optional, Sequence
|
from typing import TypeVar, Sequence, MutableMapping, Mapping
|
||||||
from typing import MutableMapping, Mapping
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ class Builder(PortList):
|
|||||||
pattern: Pattern
|
pattern: Pattern
|
||||||
""" Layout of this device """
|
""" Layout of this device """
|
||||||
|
|
||||||
library: Optional[MutableLibrary]
|
library: MutableLibrary | None
|
||||||
"""
|
"""
|
||||||
Library from which existing patterns should be referenced, and to which
|
Library from which existing patterns should be referenced, and to which
|
||||||
new ones should be added
|
new ones should be added
|
||||||
@ -94,20 +93,20 @@ class Builder(PortList):
|
|||||||
""" If True, plug()/place() are skipped (for debugging)"""
|
""" If True, plug()/place() are skipped (for debugging)"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ports(self) -> Dict[str, Port]:
|
def ports(self) -> dict[str, Port]:
|
||||||
return self.pattern.ports
|
return self.pattern.ports
|
||||||
|
|
||||||
@ports.setter
|
@ports.setter
|
||||||
def ports(self, value: Dict[str, Port]) -> None:
|
def ports(self, value: dict[str, Port]) -> None:
|
||||||
self.pattern.ports = value
|
self.pattern.ports = value
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: MutableLibrary | None = None,
|
||||||
*,
|
*,
|
||||||
pattern: Optional[Pattern] = None,
|
pattern: Pattern | None = None,
|
||||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
ports: str | Mapping[str, Port] | None = None,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
# TODO documentation for Builder() constructor
|
||||||
@ -143,13 +142,13 @@ class Builder(PortList):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def interface(
|
def interface(
|
||||||
cls,
|
cls,
|
||||||
source: Union[PortList, Mapping[str, Port], str],
|
source: PortList | Mapping[str, Port] | str,
|
||||||
*,
|
*,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: MutableLibrary | None = None,
|
||||||
in_prefix: str = 'in_',
|
in_prefix: str = 'in_',
|
||||||
out_prefix: str = '',
|
out_prefix: str = '',
|
||||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
) -> 'Builder':
|
) -> 'Builder':
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -241,13 +240,13 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def plug(
|
def plug(
|
||||||
self: BB,
|
self: BB,
|
||||||
other: Union[Abstract, str, NamedPattern],
|
other: Abstract | str | NamedPattern,
|
||||||
map_in: Dict[str, str],
|
map_in: dict[str, str],
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
map_out: dict[str, str | None] | None = None,
|
||||||
*,
|
*,
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
inherit_name: bool = True,
|
inherit_name: bool = True,
|
||||||
set_rotation: Optional[bool] = None,
|
set_rotation: bool | None = None,
|
||||||
) -> BB:
|
) -> BB:
|
||||||
"""
|
"""
|
||||||
Instantiate a device `library[name]` into the current device, connecting
|
Instantiate a device `library[name]` into the current device, connecting
|
||||||
@ -272,9 +271,9 @@ class Builder(PortList):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: An `Abstract` 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
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
port connections between the two devices.
|
port connections between the two devices.
|
||||||
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for ports in `other`.
|
new names for ports in `other`.
|
||||||
mirrored: Enables mirroring `other` across the x or y axes prior
|
mirrored: Enables mirroring `other` across the x or y axes prior
|
||||||
to connecting any ports.
|
to connecting any ports.
|
||||||
@ -342,13 +341,13 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def place(
|
def place(
|
||||||
self: BB,
|
self: BB,
|
||||||
other: Union[Abstract, str, NamedPattern],
|
other: Abstract | str | NamedPattern,
|
||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
pivot: ArrayLike = (0, 0),
|
pivot: ArrayLike = (0, 0),
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
port_map: Optional[Dict[str, Optional[str]]] = None,
|
port_map: dict[str, str | None] | None = None,
|
||||||
skip_port_check: bool = False,
|
skip_port_check: bool = False,
|
||||||
) -> BB:
|
) -> BB:
|
||||||
"""
|
"""
|
||||||
@ -373,7 +372,7 @@ class Builder(PortList):
|
|||||||
Rotation is applied prior to translation (`offset`).
|
Rotation is applied prior to translation (`offset`).
|
||||||
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
||||||
Mirroring is applied before translation and rotation.
|
Mirroring is applied before translation and rotation.
|
||||||
port_map: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for ports in the instantiated device. New names can be
|
new names for ports in the instantiated device. New names can be
|
||||||
`None`, which will delete those ports.
|
`None`, which will delete those ports.
|
||||||
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
||||||
@ -554,7 +553,7 @@ class Pather(Builder):
|
|||||||
new ones should be added
|
new ones should be added
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tools: Dict[Optional[str], Tool]
|
tools: dict[str | None, Tool]
|
||||||
"""
|
"""
|
||||||
Tool objects are used to dynamically generate new single-use Devices
|
Tool objects are used to dynamically generate new single-use Devices
|
||||||
(e.g wires or waveguides) to be plugged into this device.
|
(e.g wires or waveguides) to be plugged into this device.
|
||||||
@ -564,10 +563,10 @@ class Pather(Builder):
|
|||||||
self,
|
self,
|
||||||
library: MutableLibrary,
|
library: MutableLibrary,
|
||||||
*,
|
*,
|
||||||
pattern: Optional[Pattern] = None,
|
pattern: Pattern | None = None,
|
||||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
ports: str | Mapping[str, Port] | None = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
# TODO documentation for Builder() constructor
|
||||||
@ -609,9 +608,9 @@ class Pather(Builder):
|
|||||||
library: MutableLibrary,
|
library: MutableLibrary,
|
||||||
base_name: str,
|
base_name: str,
|
||||||
*,
|
*,
|
||||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
ports: str | Mapping[str, Port] | None= None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
) -> Tuple['Pather', str]:
|
) -> tuple['Pather', str]:
|
||||||
""" Name-and-make combination """
|
""" Name-and-make combination """
|
||||||
pat = library.create(base_name)
|
pat = library.create(base_name)
|
||||||
pather = Pather(library, pattern=pat, ports=ports, tools=tools)
|
pather = Pather(library, pattern=pat, ports=ports, tools=tools)
|
||||||
@ -622,8 +621,8 @@ class Pather(Builder):
|
|||||||
cls,
|
cls,
|
||||||
builder: Builder,
|
builder: Builder,
|
||||||
*,
|
*,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: MutableLibrary | None = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
) -> 'Pather':
|
) -> 'Pather':
|
||||||
"""TODO from_builder docs"""
|
"""TODO from_builder docs"""
|
||||||
library = library if library is not None else builder.library
|
library = library if library is not None else builder.library
|
||||||
@ -635,14 +634,14 @@ class Pather(Builder):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def interface(
|
def interface(
|
||||||
cls,
|
cls,
|
||||||
source: Union[PortList, Mapping[str, Port], str],
|
source: PortList | Mapping[str, Port] | str,
|
||||||
*,
|
*,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: MutableLibrary | None = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
in_prefix: str = 'in_',
|
in_prefix: str = 'in_',
|
||||||
out_prefix: str = '',
|
out_prefix: str = '',
|
||||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
) -> 'Pather':
|
) -> 'Pather':
|
||||||
"""
|
"""
|
||||||
TODO doc pather.interface
|
TODO doc pather.interface
|
||||||
@ -676,7 +675,7 @@ class Pather(Builder):
|
|||||||
def retool(
|
def retool(
|
||||||
self: PP,
|
self: PP,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
keys: str | Sequence[str | None] | None = None,
|
||||||
) -> PP:
|
) -> PP:
|
||||||
if keys is None or isinstance(keys, str):
|
if keys is None or isinstance(keys, str):
|
||||||
self.tools[keys] = tool
|
self.tools[keys] = tool
|
||||||
@ -688,7 +687,7 @@ class Pather(Builder):
|
|||||||
def path(
|
def path(
|
||||||
self: PP,
|
self: PP,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -709,7 +708,7 @@ class Pather(Builder):
|
|||||||
def path_to(
|
def path_to(
|
||||||
self: PP,
|
self: PP,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
position: float,
|
position: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -742,11 +741,11 @@ class Pather(Builder):
|
|||||||
|
|
||||||
def mpath(
|
def mpath(
|
||||||
self: PP,
|
self: PP,
|
||||||
portspec: Union[str, Sequence[str]],
|
portspec: str | Sequence[str],
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
*,
|
*,
|
||||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
spacing: float | ArrayLike | None = None,
|
||||||
set_rotation: Optional[float] = None,
|
set_rotation: float | None = None,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from typing import Dict, Tuple, Union, TypeVar, Optional, Sequence
|
from typing import TypeVar, Sequence, MutableMapping, Mapping
|
||||||
from typing import MutableMapping, Mapping
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -30,7 +29,7 @@ class FlatBuilder(PortList):
|
|||||||
pattern: Pattern
|
pattern: Pattern
|
||||||
""" Layout of this device """
|
""" Layout of this device """
|
||||||
|
|
||||||
tools: Dict[Optional[str], Tool]
|
tools: dict[str | None, Tool]
|
||||||
"""
|
"""
|
||||||
Tool objects are used to dynamically generate new single-use Devices
|
Tool objects are used to dynamically generate new single-use Devices
|
||||||
(e.g wires or waveguides) to be plugged into this device.
|
(e.g wires or waveguides) to be plugged into this device.
|
||||||
@ -40,19 +39,19 @@ class FlatBuilder(PortList):
|
|||||||
""" If True, plug()/place() are skipped (for debugging)"""
|
""" If True, plug()/place() are skipped (for debugging)"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ports(self) -> Dict[str, Port]:
|
def ports(self) -> dict[str, Port]:
|
||||||
return self.pattern.ports
|
return self.pattern.ports
|
||||||
|
|
||||||
@ports.setter
|
@ports.setter
|
||||||
def ports(self, value: Dict[str, Port]) -> None:
|
def ports(self, value: dict[str, Port]) -> None:
|
||||||
self.pattern.ports = value
|
self.pattern.ports = value
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
pattern: Optional[Pattern] = None,
|
pattern: Pattern | None = None,
|
||||||
ports: Union[None, Mapping[str, Port]] = None,
|
ports: Mapping[str, Port] | None = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for FlatBuilder() constructor
|
# TODO documentation for FlatBuilder() constructor
|
||||||
@ -79,12 +78,12 @@ class FlatBuilder(PortList):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def interface(
|
def interface(
|
||||||
cls,
|
cls,
|
||||||
source: Union[PortList, Mapping[str, Port]],
|
source: PortList | Mapping[str, Port],
|
||||||
*,
|
*,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||||
in_prefix: str = 'in_',
|
in_prefix: str = 'in_',
|
||||||
out_prefix: str = '',
|
out_prefix: str = '',
|
||||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||||
) -> 'FlatBuilder':
|
) -> 'FlatBuilder':
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -176,12 +175,12 @@ class FlatBuilder(PortList):
|
|||||||
def plug(
|
def plug(
|
||||||
self: BB,
|
self: BB,
|
||||||
other: Pattern,
|
other: Pattern,
|
||||||
map_in: Dict[str, str],
|
map_in: dict[str, str],
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
map_out: dict[str, str | None] | None = None,
|
||||||
*,
|
*,
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
inherit_name: bool = True,
|
inherit_name: bool = True,
|
||||||
set_rotation: Optional[bool] = None,
|
set_rotation: bool | None = None,
|
||||||
) -> BB:
|
) -> BB:
|
||||||
"""
|
"""
|
||||||
Instantiate another pattern into the current device, connecting
|
Instantiate another pattern into the current device, connecting
|
||||||
@ -206,9 +205,9 @@ class FlatBuilder(PortList):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: An `Abstract` 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
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
port connections between the two devices.
|
port connections between the two devices.
|
||||||
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for ports in `other`.
|
new names for ports in `other`.
|
||||||
mirrored: Enables mirroring `other` across the x or y axes prior
|
mirrored: Enables mirroring `other` across the x or y axes prior
|
||||||
to connecting any ports.
|
to connecting any ports.
|
||||||
@ -276,8 +275,8 @@ class FlatBuilder(PortList):
|
|||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
pivot: ArrayLike = (0, 0),
|
pivot: ArrayLike = (0, 0),
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
port_map: Optional[Dict[str, Optional[str]]] = None,
|
port_map: dict[str, str | None] | None = None,
|
||||||
skip_port_check: bool = False,
|
skip_port_check: bool = False,
|
||||||
) -> BB:
|
) -> BB:
|
||||||
"""
|
"""
|
||||||
@ -302,7 +301,7 @@ class FlatBuilder(PortList):
|
|||||||
Rotation is applied prior to translation (`offset`).
|
Rotation is applied prior to translation (`offset`).
|
||||||
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
||||||
Mirroring is applied before translation and rotation.
|
Mirroring is applied before translation and rotation.
|
||||||
port_map: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for ports in the instantiated device. New names can be
|
new names for ports in the instantiated device. New names can be
|
||||||
`None`, which will delete those ports.
|
`None`, which will delete those ports.
|
||||||
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
||||||
@ -420,7 +419,7 @@ class FlatBuilder(PortList):
|
|||||||
def retool(
|
def retool(
|
||||||
self: BB,
|
self: BB,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
keys: str | Sequence[str | None] | None = None,
|
||||||
) -> BB:
|
) -> BB:
|
||||||
if keys is None or isinstance(keys, str):
|
if keys is None or isinstance(keys, str):
|
||||||
self.tools[keys] = tool
|
self.tools[keys] = tool
|
||||||
@ -432,7 +431,7 @@ class FlatBuilder(PortList):
|
|||||||
def path(
|
def path(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -451,7 +450,7 @@ class FlatBuilder(PortList):
|
|||||||
def path_to(
|
def path_to(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
position: float,
|
position: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -484,11 +483,11 @@ class FlatBuilder(PortList):
|
|||||||
|
|
||||||
def mpath(
|
def mpath(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: Union[str, Sequence[str]],
|
portspec: str | Sequence[str],
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
*,
|
*,
|
||||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
spacing: float | ArrayLike | None = None,
|
||||||
set_rotation: Optional[float] = None,
|
set_rotation: float | None = None,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
||||||
"""
|
"""
|
||||||
from typing import TYPE_CHECKING, Optional, Sequence
|
from typing import TYPE_CHECKING, Sequence
|
||||||
|
|
||||||
from ..utils import SupportsBool
|
from ..utils import SupportsBool
|
||||||
|
|
||||||
@ -12,11 +12,11 @@ if TYPE_CHECKING:
|
|||||||
class Tool:
|
class Tool:
|
||||||
def path(
|
def path(
|
||||||
self,
|
self,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
in_ptype: Optional[str] = None,
|
in_ptype: str | None = None,
|
||||||
out_ptype: Optional[str] = None,
|
out_ptype: str | None = None,
|
||||||
port_names: Sequence[str] = ('A', 'B'),
|
port_names: Sequence[str] = ('A', 'B'),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from typing import Dict, Mapping, Sequence, SupportsFloat
|
from typing import Mapping, Sequence, SupportsFloat, cast, TYPE_CHECKING
|
||||||
from typing import Optional, Union, cast, TYPE_CHECKING
|
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -15,13 +14,13 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
def ell(
|
def ell(
|
||||||
ports: Mapping[str, 'Port'],
|
ports: Mapping[str, 'Port'],
|
||||||
ccw: Optional[SupportsBool],
|
ccw: SupportsBool | None,
|
||||||
bound_type: str,
|
bound_type: str,
|
||||||
bound: Union[float, ArrayLike],
|
bound: float | ArrayLike,
|
||||||
*,
|
*,
|
||||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
spacing: float | ArrayLike | None = None,
|
||||||
set_rotation: Optional[float] = None,
|
set_rotation: float | None = None,
|
||||||
) -> Dict[str, float]:
|
) -> dict[str, float]:
|
||||||
"""
|
"""
|
||||||
Calculate extension for each port in order to build a 90-degree bend with the provided
|
Calculate extension for each port in order to build a 90-degree bend with the provided
|
||||||
channel spacing:
|
channel spacing:
|
||||||
|
@ -6,8 +6,7 @@ Notes:
|
|||||||
* ezdxf sets creation time, write time, $VERSIONGUID, and $FINGERPRINTGUID
|
* ezdxf sets creation time, write time, $VERSIONGUID, and $FINGERPRINTGUID
|
||||||
to unique values, so byte-for-byte reproducibility is not achievable for now
|
to unique values, so byte-for-byte reproducibility is not achievable for now
|
||||||
"""
|
"""
|
||||||
from typing import List, Any, Dict, Tuple, Callable, Union, Mapping
|
from typing import Any, Callable, Mapping, cast, TextIO, IO
|
||||||
from typing import cast, TextIO, IO
|
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
@ -107,7 +106,7 @@ def write(
|
|||||||
def writefile(
|
def writefile(
|
||||||
library: Mapping[str, Pattern],
|
library: Mapping[str, Pattern],
|
||||||
top_name: str,
|
top_name: str,
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -128,7 +127,7 @@ def writefile(
|
|||||||
|
|
||||||
gz_stream: IO[bytes]
|
gz_stream: IO[bytes]
|
||||||
with tmpfile(path) as base_stream:
|
with tmpfile(path) as base_stream:
|
||||||
streams: Tuple[Any, ...] = (base_stream,)
|
streams: tuple[Any, ...] = (base_stream,)
|
||||||
if path.suffix == '.gz':
|
if path.suffix == '.gz':
|
||||||
gz_stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
gz_stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||||
streams = (gz_stream,) + streams
|
streams = (gz_stream,) + streams
|
||||||
@ -145,10 +144,10 @@ def writefile(
|
|||||||
|
|
||||||
|
|
||||||
def readfile(
|
def readfile(
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, 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.
|
||||||
|
|
||||||
@ -172,7 +171,7 @@ def readfile(
|
|||||||
|
|
||||||
def read(
|
def read(
|
||||||
stream: TextIO,
|
stream: TextIO,
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, 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
|
||||||
@ -204,7 +203,7 @@ def read(
|
|||||||
return mlib, library_info
|
return mlib, library_info
|
||||||
|
|
||||||
|
|
||||||
def _read_block(block) -> Tuple[str, Pattern]:
|
def _read_block(block) -> tuple[str, Pattern]:
|
||||||
name = block.name
|
name = block.name
|
||||||
pat = Pattern()
|
pat = Pattern()
|
||||||
for element in block:
|
for element in block:
|
||||||
@ -230,7 +229,7 @@ def _read_block(block) -> Tuple[str, Pattern]:
|
|||||||
if width == 0:
|
if width == 0:
|
||||||
width = attr.get('const_width', 0)
|
width = attr.get('const_width', 0)
|
||||||
|
|
||||||
shape: Union[Path, Polygon]
|
shape: Path | Polygon
|
||||||
if width == 0 and len(points) > 2 and numpy.array_equal(points[0], points[-1]):
|
if width == 0 and len(points) > 2 and numpy.array_equal(points[0], points[-1]):
|
||||||
shape = Polygon(layer=layer, vertices=points[:-1, :2])
|
shape = Polygon(layer=layer, vertices=points[:-1, :2])
|
||||||
else:
|
else:
|
||||||
@ -285,8 +284,8 @@ def _read_block(block) -> Tuple[str, Pattern]:
|
|||||||
|
|
||||||
|
|
||||||
def _mrefs_to_drefs(
|
def _mrefs_to_drefs(
|
||||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||||
refs: List[Ref],
|
refs: list[Ref],
|
||||||
) -> None:
|
) -> None:
|
||||||
for ref in refs:
|
for ref in refs:
|
||||||
if ref.target is None:
|
if ref.target is None:
|
||||||
@ -332,8 +331,8 @@ def _mrefs_to_drefs(
|
|||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(
|
def _shapes_to_elements(
|
||||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||||
shapes: List[Shape],
|
shapes: list[Shape],
|
||||||
polygonize_paths: bool = False,
|
polygonize_paths: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Add `LWPolyline`s for each shape.
|
# Add `LWPolyline`s for each shape.
|
||||||
@ -353,8 +352,8 @@ def _shapes_to_elements(
|
|||||||
|
|
||||||
|
|
||||||
def _labels_to_texts(
|
def _labels_to_texts(
|
||||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||||
labels: List[Label],
|
labels: list[Label],
|
||||||
) -> None:
|
) -> None:
|
||||||
for label in labels:
|
for label in labels:
|
||||||
attribs = dict(layer=_mlayer2dxf(label.layer))
|
attribs = dict(layer=_mlayer2dxf(label.layer))
|
||||||
|
@ -19,8 +19,7 @@ Notes:
|
|||||||
* GDS creation/modification/access times are set to 1900-01-01 for reproducibility.
|
* GDS creation/modification/access times are set to 1900-01-01 for reproducibility.
|
||||||
* Gzip modification time is set to 0 (start of current epoch, usually 1970-01-01)
|
* Gzip modification time is set to 0 (start of current epoch, usually 1970-01-01)
|
||||||
"""
|
"""
|
||||||
from typing import List, Dict, Tuple, Callable, Union, Iterable, Mapping
|
from typing import Callable, Iterable, Mapping, IO, cast, Any
|
||||||
from typing import IO, cast, Optional, Any
|
|
||||||
import io
|
import io
|
||||||
import mmap
|
import mmap
|
||||||
import logging
|
import logging
|
||||||
@ -114,7 +113,7 @@ def write(
|
|||||||
|
|
||||||
# Now create a structure for each pattern, and add in any Boundary and SREF elements
|
# Now create a structure for each pattern, and add in any Boundary and SREF elements
|
||||||
for name, pat in library.items():
|
for name, pat in library.items():
|
||||||
elements: List[klamath.elements.Element] = []
|
elements: list[klamath.elements.Element] = []
|
||||||
elements += _shapes_to_elements(pat.shapes)
|
elements += _shapes_to_elements(pat.shapes)
|
||||||
elements += _labels_to_texts(pat.labels)
|
elements += _labels_to_texts(pat.labels)
|
||||||
elements += _mrefs_to_grefs(pat.refs)
|
elements += _mrefs_to_grefs(pat.refs)
|
||||||
@ -125,7 +124,7 @@ def write(
|
|||||||
|
|
||||||
def writefile(
|
def writefile(
|
||||||
library: Mapping[str, Pattern],
|
library: Mapping[str, Pattern],
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -143,7 +142,7 @@ def writefile(
|
|||||||
path = pathlib.Path(filename)
|
path = pathlib.Path(filename)
|
||||||
|
|
||||||
with tmpfile(path) as base_stream:
|
with tmpfile(path) as base_stream:
|
||||||
streams: Tuple[Any, ...] = (base_stream,)
|
streams: tuple[Any, ...] = (base_stream,)
|
||||||
if path.suffix == '.gz':
|
if path.suffix == '.gz':
|
||||||
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||||
streams = (stream,) + streams
|
streams = (stream,) + streams
|
||||||
@ -158,10 +157,10 @@ def writefile(
|
|||||||
|
|
||||||
|
|
||||||
def readfile(
|
def readfile(
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, 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.
|
||||||
|
|
||||||
@ -186,7 +185,7 @@ def readfile(
|
|||||||
def read(
|
def read(
|
||||||
stream: IO[bytes],
|
stream: IO[bytes],
|
||||||
raw_mode: bool = True,
|
raw_mode: bool = True,
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
# TODO check GDSII file for cycles!
|
# TODO check GDSII file for cycles!
|
||||||
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
|
||||||
@ -204,8 +203,8 @@ def read(
|
|||||||
raw_mode: If True, constructs shapes in raw mode, bypassing most data validation, Default True.
|
raw_mode: If True, constructs shapes in raw mode, bypassing most data validation, Default True.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
- Dict of pattern_name:Patterns generated from GDSII structures
|
- dict of pattern_name:Patterns generated from GDSII structures
|
||||||
- Dict of GDSII library info
|
- dict of GDSII library info
|
||||||
"""
|
"""
|
||||||
library_info = _read_header(stream)
|
library_info = _read_header(stream)
|
||||||
|
|
||||||
@ -220,7 +219,7 @@ def read(
|
|||||||
return mlib, library_info
|
return mlib, library_info
|
||||||
|
|
||||||
|
|
||||||
def _read_header(stream: IO[bytes]) -> Dict[str, Any]:
|
def _read_header(stream: IO[bytes]) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Read the file header and create the library_info dict.
|
Read the file header and create the library_info dict.
|
||||||
"""
|
"""
|
||||||
@ -272,7 +271,7 @@ def read_elements(
|
|||||||
return pat
|
return pat
|
||||||
|
|
||||||
|
|
||||||
def _mlayer2gds(mlayer: layer_t) -> Tuple[int, int]:
|
def _mlayer2gds(mlayer: layer_t) -> tuple[int, int]:
|
||||||
""" Helper to turn a layer tuple-or-int into a layer and datatype"""
|
""" Helper to turn a layer tuple-or-int into a layer and datatype"""
|
||||||
if isinstance(mlayer, int):
|
if isinstance(mlayer, int):
|
||||||
layer = mlayer
|
layer = mlayer
|
||||||
@ -344,7 +343,7 @@ def _boundary_to_polygon(boundary: klamath.library.Boundary, raw_mode: bool) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _mrefs_to_grefs(refs: List[Ref]) -> List[klamath.library.Reference]:
|
def _mrefs_to_grefs(refs: list[Ref]) -> list[klamath.library.Reference]:
|
||||||
grefs = []
|
grefs = []
|
||||||
for ref in refs:
|
for ref in refs:
|
||||||
if ref.target is None:
|
if ref.target is None:
|
||||||
@ -402,11 +401,11 @@ def _mrefs_to_grefs(refs: List[Ref]) -> List[klamath.library.Reference]:
|
|||||||
return grefs
|
return grefs
|
||||||
|
|
||||||
|
|
||||||
def _properties_to_annotations(properties: Dict[int, bytes]) -> annotations_t:
|
def _properties_to_annotations(properties: dict[int, bytes]) -> annotations_t:
|
||||||
return {str(k): [v.decode()] for k, v in properties.items()}
|
return {str(k): [v.decode()] for k, v in properties.items()}
|
||||||
|
|
||||||
|
|
||||||
def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -> Dict[int, bytes]:
|
def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -> dict[int, bytes]:
|
||||||
cum_len = 0
|
cum_len = 0
|
||||||
props = {}
|
props = {}
|
||||||
for key, vals in annotations.items():
|
for key, vals in annotations.items():
|
||||||
@ -429,10 +428,10 @@ def _annotations_to_properties(annotations: annotations_t, max_len: int = 126) -
|
|||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(
|
def _shapes_to_elements(
|
||||||
shapes: List[Shape],
|
shapes: list[Shape],
|
||||||
polygonize_paths: bool = False,
|
polygonize_paths: bool = False,
|
||||||
) -> List[klamath.elements.Element]:
|
) -> 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:
|
||||||
if shape.repetition is not None:
|
if shape.repetition is not None:
|
||||||
@ -446,7 +445,7 @@ def _shapes_to_elements(
|
|||||||
width = rint_cast(shape.width)
|
width = rint_cast(shape.width)
|
||||||
path_type = next(k for k, v in path_cap_map.items() if v == shape.cap) # reverse lookup
|
path_type = next(k for k, v in path_cap_map.items() if v == shape.cap) # reverse lookup
|
||||||
|
|
||||||
extension: Tuple[int, int]
|
extension: tuple[int, int]
|
||||||
if shape.cap == Path.Cap.SquareCustom and shape.cap_extensions is not None:
|
if shape.cap == Path.Cap.SquareCustom and shape.cap_extensions is not None:
|
||||||
extension = tuple(shape.cap_extensions) # type: ignore
|
extension = tuple(shape.cap_extensions) # type: ignore
|
||||||
else:
|
else:
|
||||||
@ -486,7 +485,7 @@ def _shapes_to_elements(
|
|||||||
return elements
|
return elements
|
||||||
|
|
||||||
|
|
||||||
def _labels_to_texts(labels: List[Label]) -> List[klamath.elements.Text]:
|
def _labels_to_texts(labels: list[Label]) -> list[klamath.elements.Text]:
|
||||||
texts = []
|
texts = []
|
||||||
for label in labels:
|
for label in labels:
|
||||||
properties = _annotations_to_properties(label.annotations, 128)
|
properties = _annotations_to_properties(label.annotations, 128)
|
||||||
@ -512,8 +511,8 @@ def load_library(
|
|||||||
stream: IO[bytes],
|
stream: IO[bytes],
|
||||||
*,
|
*,
|
||||||
full_load: bool = False,
|
full_load: bool = False,
|
||||||
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
postprocess: Callable[[Library, str, Pattern], Pattern] | None = None
|
||||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
) -> tuple[LazyLibrary, 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
|
||||||
@ -568,12 +567,12 @@ def load_library(
|
|||||||
|
|
||||||
|
|
||||||
def load_libraryfile(
|
def load_libraryfile(
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*,
|
*,
|
||||||
use_mmap: bool = True,
|
use_mmap: bool = True,
|
||||||
full_load: bool = False,
|
full_load: bool = False,
|
||||||
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
postprocess: Callable[[Library, str, Pattern], Pattern] | None = None
|
||||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
) -> tuple[LazyLibrary, 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.
|
||||||
|
|
||||||
|
@ -14,8 +14,7 @@ Note that OASIS references follow the same convention as `masque`,
|
|||||||
Notes:
|
Notes:
|
||||||
* Gzip modification time is set to 0 (start of current epoch, usually 1970-01-01)
|
* Gzip modification time is set to 0 (start of current epoch, usually 1970-01-01)
|
||||||
"""
|
"""
|
||||||
from typing import List, Any, Dict, Tuple, Callable, Union, Iterable
|
from typing import Any, Callable, Iterable, IO, Mapping, cast, Sequence
|
||||||
from typing import IO, Mapping, Optional, cast, Sequence
|
|
||||||
import logging
|
import logging
|
||||||
import pathlib
|
import pathlib
|
||||||
import gzip
|
import gzip
|
||||||
@ -57,9 +56,9 @@ def rint_cast(val: ArrayLike) -> NDArray[numpy.int64]:
|
|||||||
def build(
|
def build(
|
||||||
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
||||||
units_per_micron: int,
|
units_per_micron: int,
|
||||||
layer_map: Optional[Dict[str, Union[int, Tuple[int, int]]]] = None,
|
layer_map: dict[str, int | tuple[int, int]] | None = None,
|
||||||
*,
|
*,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
) -> fatamorgana.OasisLayout:
|
) -> fatamorgana.OasisLayout:
|
||||||
"""
|
"""
|
||||||
Convert a collection of {name: Pattern} pairs to an OASIS stream, writing patterns
|
Convert a collection of {name: Pattern} pairs to an OASIS stream, writing patterns
|
||||||
@ -86,7 +85,7 @@ def build(
|
|||||||
library: A {name: Pattern} mapping of patterns to write.
|
library: A {name: Pattern} mapping of patterns to write.
|
||||||
units_per_micron: Written into the OASIS file, number of grid steps per micrometer.
|
units_per_micron: Written into the OASIS file, number of grid steps per micrometer.
|
||||||
All distances are assumed to be an integer multiple of the grid step, and are stored as such.
|
All distances are assumed to be an integer multiple of the grid step, and are stored as such.
|
||||||
layer_map: Dictionary which translates layer names into layer numbers. If this argument is
|
layer_map: dictionary which translates layer names into layer numbers. If this argument is
|
||||||
provided, input shapes and labels are allowed to have layer names instead of numbers.
|
provided, input shapes and labels are allowed to have layer names instead of numbers.
|
||||||
It is assumed that geometry and text share the same layer names, and each name is
|
It is assumed that geometry and text share the same layer names, and each name is
|
||||||
assigned only to a single layer (not a range).
|
assigned only to a single layer (not a range).
|
||||||
@ -127,7 +126,7 @@ def build(
|
|||||||
)
|
)
|
||||||
for tt in (True, False)]
|
for tt in (True, False)]
|
||||||
|
|
||||||
def layer2oas(mlayer: layer_t) -> Tuple[int, int]:
|
def layer2oas(mlayer: layer_t) -> tuple[int, int]:
|
||||||
assert layer_map is not None
|
assert layer_map is not None
|
||||||
layer_num = layer_map[mlayer] if isinstance(mlayer, str) else mlayer
|
layer_num = layer_map[mlayer] if isinstance(mlayer, str) else mlayer
|
||||||
return _mlayer2oas(layer_num)
|
return _mlayer2oas(layer_num)
|
||||||
@ -170,7 +169,7 @@ def write(
|
|||||||
|
|
||||||
def writefile(
|
def writefile(
|
||||||
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -188,7 +187,7 @@ def writefile(
|
|||||||
path = pathlib.Path(filename)
|
path = pathlib.Path(filename)
|
||||||
|
|
||||||
with tmpfile(path) as base_stream:
|
with tmpfile(path) as base_stream:
|
||||||
streams: Tuple[Any, ...] = (base_stream,)
|
streams: tuple[Any, ...] = (base_stream,)
|
||||||
if path.suffix == '.gz':
|
if path.suffix == '.gz':
|
||||||
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||||
streams += (stream,)
|
streams += (stream,)
|
||||||
@ -203,10 +202,10 @@ def writefile(
|
|||||||
|
|
||||||
|
|
||||||
def readfile(
|
def readfile(
|
||||||
filename: Union[str, pathlib.Path],
|
filename: str | pathlib.Path,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, 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.
|
||||||
|
|
||||||
@ -230,7 +229,7 @@ def readfile(
|
|||||||
|
|
||||||
def read(
|
def read(
|
||||||
stream: IO[bytes],
|
stream: IO[bytes],
|
||||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
) -> tuple[WrapLibrary, 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
|
||||||
@ -245,13 +244,13 @@ def read(
|
|||||||
stream: Stream to read from.
|
stream: Stream to read from.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
- Dict of `pattern_name`:`Pattern`s generated from OASIS cells
|
- dict of `pattern_name`:`Pattern`s generated from OASIS cells
|
||||||
- Dict of OASIS library info
|
- dict of OASIS library info
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lib = fatamorgana.OasisLayout.read(stream)
|
lib = fatamorgana.OasisLayout.read(stream)
|
||||||
|
|
||||||
library_info: Dict[str, Any] = {
|
library_info: dict[str, Any] = {
|
||||||
'units_per_micrometer': lib.unit,
|
'units_per_micrometer': lib.unit,
|
||||||
'annotations': properties_to_annotations(lib.properties, lib.propnames, lib.propstrings),
|
'annotations': properties_to_annotations(lib.properties, lib.propnames, lib.propstrings),
|
||||||
}
|
}
|
||||||
@ -304,7 +303,7 @@ def read(
|
|||||||
raise Exception('masque does not support multiple cap types on a single path.') # TODO handle multiple cap types
|
raise Exception('masque does not support multiple cap types on a single path.') # TODO handle multiple cap types
|
||||||
cap = cap_start
|
cap = cap_start
|
||||||
|
|
||||||
path_args: Dict[str, Any] = {}
|
path_args: dict[str, Any] = {}
|
||||||
if cap == Path.Cap.SquareCustom:
|
if cap == Path.Cap.SquareCustom:
|
||||||
path_args['cap_extensions'] = numpy.array((
|
path_args['cap_extensions'] = numpy.array((
|
||||||
element.get_extension_start()[1],
|
element.get_extension_start()[1],
|
||||||
@ -468,7 +467,7 @@ def read(
|
|||||||
return mlib, library_info
|
return mlib, library_info
|
||||||
|
|
||||||
|
|
||||||
def _mlayer2oas(mlayer: layer_t) -> Tuple[int, int]:
|
def _mlayer2oas(mlayer: layer_t) -> tuple[int, int]:
|
||||||
""" Helper to turn a layer tuple-or-int into a layer and datatype"""
|
""" Helper to turn a layer tuple-or-int into a layer and datatype"""
|
||||||
if isinstance(mlayer, int):
|
if isinstance(mlayer, int):
|
||||||
layer = mlayer
|
layer = mlayer
|
||||||
@ -494,7 +493,7 @@ def _placement_to_ref(placement: fatrec.Placement, lib: fatamorgana.OasisLayout)
|
|||||||
mag = placement.magnification if placement.magnification is not None else 1
|
mag = placement.magnification if placement.magnification is not None else 1
|
||||||
|
|
||||||
pname = placement.get_name()
|
pname = placement.get_name()
|
||||||
name: Union[int, str] = pname if isinstance(pname, int) else pname.string # TODO deal with referenced names
|
name: int | str = pname if isinstance(pname, int) else pname.string # TODO deal with referenced names
|
||||||
|
|
||||||
annotations = properties_to_annotations(placement.properties, lib.propnames, lib.propstrings)
|
annotations = properties_to_annotations(placement.properties, lib.propnames, lib.propstrings)
|
||||||
if placement.angle is None:
|
if placement.angle is None:
|
||||||
@ -514,8 +513,8 @@ def _placement_to_ref(placement: fatrec.Placement, lib: fatamorgana.OasisLayout)
|
|||||||
|
|
||||||
|
|
||||||
def _refs_to_placements(
|
def _refs_to_placements(
|
||||||
refs: List[Ref],
|
refs: list[Ref],
|
||||||
) -> List[fatrec.Placement]:
|
) -> list[fatrec.Placement]:
|
||||||
placements = []
|
placements = []
|
||||||
for ref in refs:
|
for ref in refs:
|
||||||
if ref.target is None:
|
if ref.target is None:
|
||||||
@ -543,11 +542,11 @@ def _refs_to_placements(
|
|||||||
|
|
||||||
|
|
||||||
def _shapes_to_elements(
|
def _shapes_to_elements(
|
||||||
shapes: List[Shape],
|
shapes: list[Shape],
|
||||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
layer2oas: Callable[[layer_t], tuple[int, int]],
|
||||||
) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]:
|
) -> list[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[fatrec.Polygon | fatrec.Path | fatrec.Circle] = []
|
||||||
for shape in shapes:
|
for shape in shapes:
|
||||||
layer, datatype = layer2oas(shape.layer)
|
layer, datatype = layer2oas(shape.layer)
|
||||||
repetition, rep_offset = repetition_masq2fata(shape.repetition)
|
repetition, rep_offset = repetition_masq2fata(shape.repetition)
|
||||||
@ -594,7 +593,7 @@ def _shapes_to_elements(
|
|||||||
datatype=datatype,
|
datatype=datatype,
|
||||||
x=xy[0],
|
x=xy[0],
|
||||||
y=xy[1],
|
y=xy[1],
|
||||||
point_list=cast(List[List[int]], points),
|
point_list=cast(list[list[int]], points),
|
||||||
properties=properties,
|
properties=properties,
|
||||||
repetition=repetition,
|
repetition=repetition,
|
||||||
))
|
))
|
||||||
@ -602,9 +601,9 @@ def _shapes_to_elements(
|
|||||||
|
|
||||||
|
|
||||||
def _labels_to_texts(
|
def _labels_to_texts(
|
||||||
labels: List[Label],
|
labels: list[Label],
|
||||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
layer2oas: Callable[[layer_t], tuple[int, int]],
|
||||||
) -> List[fatrec.Text]:
|
) -> list[fatrec.Text]:
|
||||||
texts = []
|
texts = []
|
||||||
for label in labels:
|
for label in labels:
|
||||||
layer, datatype = layer2oas(label.layer)
|
layer, datatype = layer2oas(label.layer)
|
||||||
@ -624,9 +623,9 @@ def _labels_to_texts(
|
|||||||
|
|
||||||
|
|
||||||
def repetition_fata2masq(
|
def repetition_fata2masq(
|
||||||
rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None],
|
rep: fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None,
|
||||||
) -> Optional[Repetition]:
|
) -> Repetition | None:
|
||||||
mrep: Optional[Repetition]
|
mrep: Repetition | None
|
||||||
if isinstance(rep, fatamorgana.GridRepetition):
|
if isinstance(rep, fatamorgana.GridRepetition):
|
||||||
mrep = Grid(a_vector=rep.a_vector,
|
mrep = Grid(a_vector=rep.a_vector,
|
||||||
b_vector=rep.b_vector,
|
b_vector=rep.b_vector,
|
||||||
@ -645,22 +644,22 @@ def repetition_fata2masq(
|
|||||||
|
|
||||||
|
|
||||||
def repetition_masq2fata(
|
def repetition_masq2fata(
|
||||||
rep: Optional[Repetition],
|
rep: Repetition | None,
|
||||||
) -> Tuple[Union[fatamorgana.GridRepetition,
|
) -> tuple[
|
||||||
fatamorgana.ArbitraryRepetition,
|
fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None,
|
||||||
None],
|
tuple[int, int]
|
||||||
Tuple[int, int]]:
|
]:
|
||||||
frep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None]
|
frep: fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None
|
||||||
if isinstance(rep, Grid):
|
if isinstance(rep, Grid):
|
||||||
a_vector = rint_cast(rep.a_vector)
|
a_vector = rint_cast(rep.a_vector)
|
||||||
b_vector = rint_cast(rep.b_vector) if rep.b_vector is not None else None
|
b_vector = rint_cast(rep.b_vector) if rep.b_vector is not None else None
|
||||||
a_count = rint_cast(rep.a_count)
|
a_count = rint_cast(rep.a_count)
|
||||||
b_count = rint_cast(rep.b_count) if rep.b_count is not None else None
|
b_count = rint_cast(rep.b_count) if rep.b_count is not None else None
|
||||||
frep = fatamorgana.GridRepetition(
|
frep = fatamorgana.GridRepetition(
|
||||||
a_vector=cast(List[int], a_vector),
|
a_vector=cast(list[int], a_vector),
|
||||||
b_vector=cast(Optional[List[int]], b_vector),
|
b_vector=cast(list[int] | None, b_vector),
|
||||||
a_count=cast(int, a_count),
|
a_count=cast(int, a_count),
|
||||||
b_count=cast(Optional[int], b_count),
|
b_count=cast(int | None, b_count),
|
||||||
)
|
)
|
||||||
offset = (0, 0)
|
offset = (0, 0)
|
||||||
elif isinstance(rep, Arbitrary):
|
elif isinstance(rep, Arbitrary):
|
||||||
@ -675,7 +674,7 @@ def repetition_masq2fata(
|
|||||||
return frep, offset
|
return frep, offset
|
||||||
|
|
||||||
|
|
||||||
def annotations_to_properties(annotations: annotations_t) -> List[fatrec.Property]:
|
def annotations_to_properties(annotations: annotations_t) -> list[fatrec.Property]:
|
||||||
#TODO determine is_standard based on key?
|
#TODO determine is_standard based on key?
|
||||||
properties = []
|
properties = []
|
||||||
for key, values in annotations.items():
|
for key, values in annotations.items():
|
||||||
@ -686,9 +685,9 @@ def annotations_to_properties(annotations: annotations_t) -> List[fatrec.Propert
|
|||||||
|
|
||||||
|
|
||||||
def properties_to_annotations(
|
def properties_to_annotations(
|
||||||
properties: List[fatrec.Property],
|
properties: list[fatrec.Property],
|
||||||
propnames: Dict[int, NString],
|
propnames: dict[int, NString],
|
||||||
propstrings: Dict[int, AString],
|
propstrings: dict[int, AString],
|
||||||
) -> annotations_t:
|
) -> annotations_t:
|
||||||
annotations = {}
|
annotations = {}
|
||||||
for proprec in properties:
|
for proprec in properties:
|
||||||
@ -697,7 +696,7 @@ def properties_to_annotations(
|
|||||||
key = propnames[proprec.name].string
|
key = propnames[proprec.name].string
|
||||||
else:
|
else:
|
||||||
key = proprec.name.string
|
key = proprec.name.string
|
||||||
values: List[Union[str, float, int]] = []
|
values: list[str | float | int] = []
|
||||||
|
|
||||||
assert proprec.values is not None
|
assert proprec.values is not None
|
||||||
for value in proprec.values:
|
for value in proprec.values:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Helper functions for file reading and writing
|
Helper functions for file reading and writing
|
||||||
"""
|
"""
|
||||||
from typing import Union, IO, Iterator
|
from typing import IO, Iterator
|
||||||
import re
|
import re
|
||||||
import pathlib
|
import pathlib
|
||||||
import logging
|
import logging
|
||||||
@ -62,7 +62,7 @@ def is_gzipped(path: pathlib.Path) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def tmpfile(path: Union[str, pathlib.Path]) -> Iterator[IO[bytes]]:
|
def tmpfile(path: str | pathlib.Path) -> Iterator[IO[bytes]]:
|
||||||
"""
|
"""
|
||||||
Context manager which allows you to write to a temporary file,
|
Context manager which allows you to write to a temporary file,
|
||||||
and move that file into its final location only after the write
|
and move that file into its final location only after the write
|
||||||
@ -77,5 +77,3 @@ def tmpfile(path: Union[str, pathlib.Path]) -> Iterator[IO[bytes]]:
|
|||||||
shutil.move(tmp_stream.name, path)
|
shutil.move(tmp_stream.name, path)
|
||||||
finally:
|
finally:
|
||||||
pathlib.Path(tmp_stream.name).unlink(missing_ok=True)
|
pathlib.Path(tmp_stream.name).unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import Dict, Optional, TypeVar
|
from typing import TypeVar
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -44,8 +44,8 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
|
|||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.string = string
|
self.string = string
|
||||||
self.offset = numpy.array(offset, dtype=float, copy=True)
|
self.offset = numpy.array(offset, dtype=float, copy=True)
|
||||||
@ -61,7 +61,7 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
|
|||||||
repetition=self.repetition,
|
repetition=self.repetition,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __deepcopy__(self: L, memo: Optional[Dict] = None) -> L:
|
def __deepcopy__(self: L, memo: dict | None = None) -> L:
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
|
@ -5,8 +5,8 @@ Library classes for managing unique name->pattern mappings and
|
|||||||
# TODO documentn all library classes
|
# TODO documentn all library classes
|
||||||
# TODO toplevel documentation of library, classes, and abstracts
|
# TODO toplevel documentation of library, classes, and abstracts
|
||||||
"""
|
"""
|
||||||
from typing import List, Dict, Callable, TypeVar, Type, TYPE_CHECKING, cast
|
from typing import Callable, TypeVar, Type, TYPE_CHECKING, cast
|
||||||
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
import struct
|
import struct
|
||||||
@ -75,8 +75,8 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def dangling_refs(
|
def dangling_refs(
|
||||||
self,
|
self,
|
||||||
tops: Union[None, str, Sequence[str]] = None,
|
tops: str | Sequence[str] | None = None,
|
||||||
) -> Set[Optional[str]]:
|
) -> set[str | None]:
|
||||||
"""
|
"""
|
||||||
Get the set of all pattern names not present in the library but referenced
|
Get the set of all pattern names not present in the library but referenced
|
||||||
by `tops`, recursively traversing any refs.
|
by `tops`, recursively traversing any refs.
|
||||||
@ -99,9 +99,9 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def referenced_patterns(
|
def referenced_patterns(
|
||||||
self,
|
self,
|
||||||
tops: Union[None, str, Sequence[str]] = None,
|
tops: str | Sequence[str] | None = None,
|
||||||
skip: Optional[Set[Optional[str]]] = None,
|
skip: set[str | None] | None = None,
|
||||||
) -> Set[Optional[str]]:
|
) -> set[str | None]:
|
||||||
"""
|
"""
|
||||||
Get the set of all pattern names referenced by `tops`. Recursively traverses into any refs.
|
Get the set of all pattern names referenced by `tops`. Recursively traverses into any refs.
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def subtree(
|
def subtree(
|
||||||
self,
|
self,
|
||||||
tops: Union[str, Sequence[str]],
|
tops: str | Sequence[str],
|
||||||
) -> 'Library':
|
) -> 'Library':
|
||||||
"""
|
"""
|
||||||
Return a new `Library`, containing only the specified patterns and the patterns they
|
Return a new `Library`, containing only the specified patterns and the patterns they
|
||||||
@ -155,7 +155,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
if isinstance(tops, str):
|
if isinstance(tops, str):
|
||||||
tops = (tops,)
|
tops = (tops,)
|
||||||
|
|
||||||
keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
|
keep: set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
|
||||||
keep |= set(tops)
|
keep |= set(tops)
|
||||||
|
|
||||||
filtered = {kk: vv for kk, vv in self.items() if kk in keep}
|
filtered = {kk: vv for kk, vv in self.items() if kk in keep}
|
||||||
@ -164,25 +164,25 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def polygonize(
|
def polygonize(
|
||||||
self: L,
|
self: L,
|
||||||
poly_num_points: Optional[int] = None,
|
num_vertices: int | None = None,
|
||||||
poly_max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> L:
|
) -> L:
|
||||||
"""
|
"""
|
||||||
Calls `.polygonize(...)` on each pattern in this library.
|
Calls `.polygonize(...)` on each pattern in this library.
|
||||||
Arguments are passed on to `shape.to_polygons(...)`.
|
Arguments are passed on to `shape.to_polygons(...)`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
poly_num_points: Number of points to use for each polygon. Can be overridden by
|
num_vertices: Number of points to use for each polygon. Can be overridden by
|
||||||
`poly_max_arclen` if that results in more points. Optional, defaults to shapes'
|
`max_arclen` if that results in more points. Optional, defaults to shapes'
|
||||||
internal defaults.
|
internal defaults.
|
||||||
poly_max_arclen: Maximum arclength which can be approximated by a single line
|
max_arclen: Maximum arclength which can be approximated by a single line
|
||||||
segment. Optional, defaults to shapes' internal defaults.
|
segment. Optional, defaults to shapes' internal defaults.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
for pat in self.values():
|
for pat in self.values():
|
||||||
pat.polygonize(poly_num_points, poly_max_arclen)
|
pat.polygonize(num_vertices, max_arclen)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def manhattanize(
|
def manhattanize(
|
||||||
@ -206,9 +206,9 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
def flatten(
|
def flatten(
|
||||||
self,
|
self,
|
||||||
tops: Union[str, Sequence[str]],
|
tops: str | Sequence[str],
|
||||||
flatten_ports: bool = False, # TODO document
|
flatten_ports: bool = False, # TODO document
|
||||||
) -> Dict[str, 'Pattern']:
|
) -> dict[str, 'Pattern']:
|
||||||
"""
|
"""
|
||||||
Removes all refs and adds equivalent shapes.
|
Removes all refs and adds equivalent shapes.
|
||||||
Also flattens all referenced patterns.
|
Also flattens all referenced patterns.
|
||||||
@ -222,7 +222,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
if isinstance(tops, str):
|
if isinstance(tops, str):
|
||||||
tops = (tops,)
|
tops = (tops,)
|
||||||
|
|
||||||
flattened: Dict[str, Optional['Pattern']] = {}
|
flattened: dict[str, 'Pattern' | None] = {}
|
||||||
|
|
||||||
def flatten_single(name) -> None:
|
def flatten_single(name) -> None:
|
||||||
flattened[name] = None
|
flattened[name] = None
|
||||||
@ -261,7 +261,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
name: str = '__',
|
name: str = '__',
|
||||||
sanitize: bool = True,
|
sanitize: bool = True,
|
||||||
max_length: int = 32,
|
max_length: int = 32,
|
||||||
quiet: Optional[bool] = None,
|
quiet: bool | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Find a unique name for the pattern.
|
Find a unique name for the pattern.
|
||||||
@ -308,7 +308,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
return cropped_name
|
return cropped_name
|
||||||
|
|
||||||
def find_toplevel(self) -> List[str]:
|
def find_toplevel(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Return the list of all patterns that are not referenced by any other pattern in the library.
|
Return the list of all patterns that are not referenced by any other pattern in the library.
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
A list of pattern names in which no pattern is referenced by any other pattern.
|
A list of pattern names in which no pattern is referenced by any other pattern.
|
||||||
"""
|
"""
|
||||||
names = set(self.keys())
|
names = set(self.keys())
|
||||||
not_toplevel: Set[Optional[str]] = set()
|
not_toplevel: set[str | None] = set()
|
||||||
for name in names:
|
for name in names:
|
||||||
not_toplevel |= set(sp.target for sp in self[name].refs)
|
not_toplevel |= set(sp.target for sp in self[name].refs)
|
||||||
|
|
||||||
@ -326,12 +326,12 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
def dfs(
|
def dfs(
|
||||||
self: L,
|
self: L,
|
||||||
pattern: 'Pattern',
|
pattern: 'Pattern',
|
||||||
visit_before: Optional[visitor_function_t] = None,
|
visit_before: visitor_function_t | None = None,
|
||||||
visit_after: Optional[visitor_function_t] = None,
|
visit_after: visitor_function_t | None = None,
|
||||||
*,
|
*,
|
||||||
hierarchy: Tuple[Optional[str], ...] = (None,),
|
hierarchy: tuple[str | None, ...] = (None,),
|
||||||
transform: Union[ArrayLike, bool, None] = False,
|
transform: ArrayLike | bool | None = False,
|
||||||
memo: Optional[Dict] = None,
|
memo: dict | None = None,
|
||||||
) -> L:
|
) -> L:
|
||||||
"""
|
"""
|
||||||
Convenience function.
|
Convenience function.
|
||||||
@ -434,14 +434,14 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
#def __getitem__(self, key: str) -> 'Pattern':
|
#def __getitem__(self, key: str) -> 'Pattern':
|
||||||
#def __iter__(self) -> Iterator[str]:
|
#def __iter__(self) -> Iterator[str]:
|
||||||
#def __len__(self) -> int:
|
#def __len__(self) -> int:
|
||||||
#def __setitem__(self, key: str, value: Union['Pattern', Callable[[], 'Pattern']]) -> None:
|
#def __setitem__(self, key: str, value: 'Pattern' | Callable[[], 'Pattern']) -> None:
|
||||||
#def __delitem__(self, key: str) -> None:
|
#def __delitem__(self, key: str) -> None:
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __setitem__(
|
def __setitem__(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -510,31 +510,11 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
self[name] = npat
|
self[name] = npat
|
||||||
return npat
|
return npat
|
||||||
|
|
||||||
def set(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
Convenience method which finds a suitable name for the provided
|
|
||||||
pattern, adds it with that name, and returns the name.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
base_name: Prefix used when naming the pattern
|
|
||||||
value: The pattern (or callable used to generate it)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The name of the pattern.
|
|
||||||
"""
|
|
||||||
#name = self.get_name(base_name)
|
|
||||||
self[name] = value
|
|
||||||
return name
|
|
||||||
|
|
||||||
def add(
|
def add(
|
||||||
self,
|
self,
|
||||||
other: Mapping[str, 'Pattern'],
|
other: Mapping[str, 'Pattern'],
|
||||||
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
||||||
) -> Dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Add keys from another library into this one.
|
Add keys from another library into this one.
|
||||||
|
|
||||||
@ -581,7 +561,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
def add_tree(
|
def add_tree(
|
||||||
self,
|
self,
|
||||||
tree: 'Tree',
|
tree: 'Tree',
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
@ -623,8 +603,8 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
def dedup(
|
def dedup(
|
||||||
self: ML,
|
self: ML,
|
||||||
norm_value: int = int(1e6),
|
norm_value: int = int(1e6),
|
||||||
exclude_types: Tuple[Type] = (Polygon,),
|
exclude_types: tuple[Type] = (Polygon,),
|
||||||
label2name: Optional[Callable[[Tuple], str]] = None,
|
label2name: Callable[[tuple], str] | None = None,
|
||||||
threshold: int = 2,
|
threshold: int = 2,
|
||||||
) -> ML:
|
) -> ML:
|
||||||
"""
|
"""
|
||||||
@ -664,7 +644,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
return self.get_name('_shape')
|
return self.get_name('_shape')
|
||||||
#label2name = lambda label: self.get_name('_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 = {}
|
||||||
|
|
||||||
# ## First pass ##
|
# ## First pass ##
|
||||||
@ -692,7 +672,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
# are to be replaced.
|
# are to be replaced.
|
||||||
# The `values` are `(offset, scale, rotation)`.
|
# The `values` are `(offset, scale, rotation)`.
|
||||||
|
|
||||||
shape_table: MutableMapping[Tuple, List] = defaultdict(list)
|
shape_table: MutableMapping[tuple, list] = defaultdict(list)
|
||||||
for i, shape in enumerate(pat.shapes):
|
for i, shape in enumerate(pat.shapes):
|
||||||
if any(isinstance(shape, t) for t in exclude_types):
|
if any(isinstance(shape, t) for t in exclude_types):
|
||||||
continue
|
continue
|
||||||
@ -727,7 +707,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
|
|
||||||
def wrap_repeated_shapes(
|
def wrap_repeated_shapes(
|
||||||
self: ML,
|
self: ML,
|
||||||
name_func: Optional[Callable[['Pattern', Union[Shape, Label]], str]] = None,
|
name_func: Callable[['Pattern', Shape | Label], str] | None = None,
|
||||||
) -> ML:
|
) -> ML:
|
||||||
"""
|
"""
|
||||||
Wraps all shapes and labels with a non-`None` `repetition` attribute
|
Wraps all shapes and labels with a non-`None` `repetition` attribute
|
||||||
@ -776,7 +756,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
|
|
||||||
def subtree(
|
def subtree(
|
||||||
self: ML,
|
self: ML,
|
||||||
tops: Union[str, Sequence[str]],
|
tops: str | Sequence[str],
|
||||||
) -> ML:
|
) -> ML:
|
||||||
"""
|
"""
|
||||||
Return a new `Library`, containing only the specified patterns and the patterns they
|
Return a new `Library`, containing only the specified patterns and the patterns they
|
||||||
@ -791,7 +771,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
if isinstance(tops, str):
|
if isinstance(tops, str):
|
||||||
tops = (tops,)
|
tops = (tops,)
|
||||||
|
|
||||||
keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
|
keep: set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
|
||||||
keep |= set(tops)
|
keep |= set(tops)
|
||||||
|
|
||||||
new = type(self)()
|
new = type(self)()
|
||||||
@ -802,7 +782,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
|||||||
def prune_empty(
|
def prune_empty(
|
||||||
self,
|
self,
|
||||||
repeat: bool = True,
|
repeat: bool = True,
|
||||||
) -> Set[str]:
|
) -> set[str]:
|
||||||
# TODO doc prune_empty
|
# TODO doc prune_empty
|
||||||
trimmed = set()
|
trimmed = set()
|
||||||
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
||||||
@ -859,7 +839,7 @@ class WrapLibrary(MutableLibrary):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
mapping: Optional[MutableMapping[str, 'Pattern']] = None,
|
mapping: MutableMapping[str, 'Pattern'] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if mapping is None:
|
if mapping is None:
|
||||||
self.mapping = {}
|
self.mapping = {}
|
||||||
@ -881,7 +861,7 @@ class WrapLibrary(MutableLibrary):
|
|||||||
def __setitem__(
|
def __setitem__(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||||
) -> None:
|
) -> None:
|
||||||
if key in self.mapping:
|
if key in self.mapping:
|
||||||
raise LibraryError(f'"{key}" already exists in the library. Overwriting is not allowed!')
|
raise LibraryError(f'"{key}" already exists in the library. Overwriting is not allowed!')
|
||||||
@ -909,21 +889,21 @@ class LazyLibrary(MutableLibrary):
|
|||||||
This class is usually used to create a library of Patterns by mapping names to
|
This class is usually used to create a library of Patterns by mapping names to
|
||||||
functions which generate or load the relevant `Pattern` object as-needed.
|
functions which generate or load the relevant `Pattern` object as-needed.
|
||||||
"""
|
"""
|
||||||
dict: Dict[str, Callable[[], 'Pattern']]
|
mapping: dict[str, Callable[[], 'Pattern']]
|
||||||
cache: Dict[str, 'Pattern']
|
cache: dict[str, 'Pattern']
|
||||||
_lookups_in_progress: Set[str]
|
_lookups_in_progress: set[str]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.dict = {}
|
self.mapping = {}
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self._lookups_in_progress = set()
|
self._lookups_in_progress = set()
|
||||||
|
|
||||||
def __setitem__(
|
def __setitem__(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||||
) -> None:
|
) -> None:
|
||||||
if key in self.dict:
|
if key in self.mapping:
|
||||||
raise LibraryError(f'"{key}" already exists in the library. Overwriting is not allowed!')
|
raise LibraryError(f'"{key}" already exists in the library. Overwriting is not allowed!')
|
||||||
|
|
||||||
if callable(value):
|
if callable(value):
|
||||||
@ -931,12 +911,12 @@ class LazyLibrary(MutableLibrary):
|
|||||||
else:
|
else:
|
||||||
value_func = lambda: cast('Pattern', value) # noqa: E731
|
value_func = lambda: cast('Pattern', value) # noqa: E731
|
||||||
|
|
||||||
self.dict[key] = value_func
|
self.mapping[key] = value_func
|
||||||
if key in self.cache:
|
if key in self.cache:
|
||||||
del self.cache[key]
|
del self.cache[key]
|
||||||
|
|
||||||
def __delitem__(self, key: str) -> None:
|
def __delitem__(self, key: str) -> None:
|
||||||
del self.dict[key]
|
del self.mapping[key]
|
||||||
if key in self.cache:
|
if key in self.cache:
|
||||||
del self.cache[key]
|
del self.cache[key]
|
||||||
|
|
||||||
@ -954,24 +934,24 @@ class LazyLibrary(MutableLibrary):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self._lookups_in_progress.add(key)
|
self._lookups_in_progress.add(key)
|
||||||
func = self.dict[key]
|
func = self.mapping[key]
|
||||||
pat = func()
|
pat = func()
|
||||||
self._lookups_in_progress.remove(key)
|
self._lookups_in_progress.remove(key)
|
||||||
self.cache[key] = pat
|
self.cache[key] = pat
|
||||||
return pat
|
return pat
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[str]:
|
def __iter__(self) -> Iterator[str]:
|
||||||
return iter(self.dict)
|
return iter(self.mapping)
|
||||||
|
|
||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self.dict)
|
return len(self.mapping)
|
||||||
|
|
||||||
def __contains__(self, key: object) -> bool:
|
def __contains__(self, key: object) -> bool:
|
||||||
return key in self.dict
|
return key in self.mapping
|
||||||
|
|
||||||
def _merge(self, key_self: str, other: Mapping[str, 'Pattern'], key_other: str) -> None:
|
def _merge(self, key_self: str, other: Mapping[str, 'Pattern'], key_other: str) -> None:
|
||||||
if isinstance(other, LazyLibrary):
|
if isinstance(other, LazyLibrary):
|
||||||
self.dict[key_self] = other.dict[key_other]
|
self.mapping[key_self] = other.mapping[key_other]
|
||||||
if key_other in other.cache:
|
if key_other in other.cache:
|
||||||
self.cache[key_self] = other.cache[key_other]
|
self.cache[key_self] = other.cache[key_other]
|
||||||
else:
|
else:
|
||||||
@ -999,7 +979,7 @@ class LazyLibrary(MutableLibrary):
|
|||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
self[new_name] = self.dict[old_name] # copy over function
|
self[new_name] = self.mapping[old_name] # copy over function
|
||||||
if old_name in self.cache:
|
if old_name in self.cache:
|
||||||
self.cache[new_name] = self.cache[old_name]
|
self.cache[new_name] = self.cache[old_name]
|
||||||
del self[old_name]
|
del self[old_name]
|
||||||
@ -1034,11 +1014,11 @@ class LazyLibrary(MutableLibrary):
|
|||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
for key in self.dict:
|
for key in self.mapping:
|
||||||
_ = self[key] # want to trigger our own __getitem__
|
_ = self[key] # want to trigger our own __getitem__
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'LazyLibrary':
|
def __deepcopy__(self, memo: dict | None = None) -> 'LazyLibrary':
|
||||||
raise LibraryError('LazyLibrary cannot be deepcopied (deepcopy doesn\'t descend into closures)')
|
raise LibraryError('LazyLibrary cannot be deepcopied (deepcopy doesn\'t descend into closures)')
|
||||||
|
|
||||||
|
|
||||||
@ -1068,14 +1048,14 @@ class Tree(MutableLibrary):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
top: Union[str, 'NamedPattern'],
|
top: str | 'NamedPattern',
|
||||||
library: Optional[MutableLibrary] = None
|
library: MutableLibrary | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
self.top = top if isinstance(top, str) else top.name
|
self.top = top if isinstance(top, str) else top.name
|
||||||
self.library = library if library is not None else WrapLibrary()
|
self.library = library if library is not None else WrapLibrary()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mk(cls, top: str) -> Tuple['Tree', 'Pattern']:
|
def mk(cls, top: str) -> tuple['Tree', 'Pattern']:
|
||||||
from .pattern import Pattern
|
from .pattern import Pattern
|
||||||
tree = cls(top=top)
|
tree = cls(top=top)
|
||||||
pat = Pattern()
|
pat = Pattern()
|
||||||
@ -1091,7 +1071,7 @@ class Tree(MutableLibrary):
|
|||||||
def __len__(self) -> int:
|
def __len__(self) -> int:
|
||||||
return len(self.library)
|
return len(self.library)
|
||||||
|
|
||||||
def __setitem__(self, key: str, value: Union['Pattern', Callable[[], 'Pattern']]) -> None:
|
def __setitem__(self, key: str, value: 'Pattern' | Callable[[], 'Pattern']) -> None:
|
||||||
self.library[key] = value
|
self.library[key] = value
|
||||||
|
|
||||||
def __delitem__(self, key: str) -> None:
|
def __delitem__(self, key: str) -> None:
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
Base object representing a lithography mask.
|
Base object representing a lithography mask.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List, Callable, Dict, Union, Set, Sequence, Optional, cast
|
from typing import Callable, Sequence, cast, Mapping, TypeVar, Any
|
||||||
from typing import Mapping, TypeVar, Any
|
|
||||||
import copy
|
import copy
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
@ -35,29 +34,29 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
'_offset', '_annotations',
|
'_offset', '_annotations',
|
||||||
)
|
)
|
||||||
|
|
||||||
shapes: List[Shape]
|
shapes: list[Shape]
|
||||||
""" List of all shapes in this Pattern.
|
""" List of all shapes in this Pattern.
|
||||||
Elements in this list are assumed to inherit from Shape or provide equivalent functions.
|
Elements in this list are assumed to inherit from Shape or provide equivalent functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
labels: List[Label]
|
labels: list[Label]
|
||||||
""" List of all labels in this Pattern. """
|
""" List of all labels in this Pattern. """
|
||||||
|
|
||||||
refs: List[Ref]
|
refs: list[Ref]
|
||||||
""" List of all references to other patterns (`Ref`s) in this `Pattern`.
|
""" List of all references to other patterns (`Ref`s) in this `Pattern`.
|
||||||
Multiple objects in this list may reference the same Pattern object
|
Multiple objects in this list may reference the same Pattern object
|
||||||
(i.e. multiple instances of the same object).
|
(i.e. multiple instances of the same object).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ports: Dict[str, Port]
|
_ports: dict[str, Port]
|
||||||
""" Uniquely-named ports which can be used to snap to other Pattern instances"""
|
""" Uniquely-named ports which can be used to snap to other Pattern instances"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ports(self) -> Dict[str, Port]:
|
def ports(self) -> dict[str, Port]:
|
||||||
return self._ports
|
return self._ports
|
||||||
|
|
||||||
@ports.setter
|
@ports.setter
|
||||||
def ports(self, value: Dict[str, Port]) -> None:
|
def ports(self, value: dict[str, Port]) -> None:
|
||||||
self._ports = value
|
self._ports = value
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -66,8 +65,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
shapes: Sequence[Shape] = (),
|
shapes: Sequence[Shape] = (),
|
||||||
labels: Sequence[Label] = (),
|
labels: Sequence[Label] = (),
|
||||||
refs: Sequence[Ref] = (),
|
refs: Sequence[Ref] = (),
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
ports: Optional[Mapping[str, 'Port']] = None
|
ports: Mapping[str, 'Port'] | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Basic init; arguments get assigned to member variables.
|
Basic init; arguments get assigned to member variables.
|
||||||
@ -118,7 +117,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
ports=copy.deepcopy(self.ports),
|
ports=copy.deepcopy(self.ports),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Pattern':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Pattern':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = Pattern(
|
new = Pattern(
|
||||||
shapes=copy.deepcopy(self.shapes, memo),
|
shapes=copy.deepcopy(self.shapes, memo),
|
||||||
@ -158,11 +157,11 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
|
|
||||||
def subset(
|
def subset(
|
||||||
self,
|
self,
|
||||||
shapes: Optional[Callable[[Shape], bool]] = None,
|
shapes: Callable[[Shape], bool] | None = None,
|
||||||
labels: Optional[Callable[[Label], bool]] = None,
|
labels: Callable[[Label], bool] | None = None,
|
||||||
refs: Optional[Callable[[Ref], bool]] = None,
|
refs: Callable[[Ref], bool] | None = None,
|
||||||
annotations: Optional[Callable[[str, List[Union[int, float, str]]], bool]] = None,
|
annotations: Callable[[str, list[int | float | str]], bool] | None = None,
|
||||||
ports: Optional[Callable[[str, Port], bool]] = None,
|
ports: Callable[[str, Port], bool] | None = None,
|
||||||
default_keep: bool = False
|
default_keep: bool = False
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
@ -214,8 +213,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
|
|
||||||
def polygonize(
|
def polygonize(
|
||||||
self: P,
|
self: P,
|
||||||
num_points: Optional[int] = None,
|
num_points: int | None = None,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> P:
|
) -> P:
|
||||||
"""
|
"""
|
||||||
Calls `.to_polygons(...)` on all the shapes in this Pattern, replacing them with the returned polygons.
|
Calls `.to_polygons(...)` on all the shapes in this Pattern, replacing them with the returned polygons.
|
||||||
@ -260,7 +259,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
(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 as_polygons(self, library: Mapping[str, 'Pattern']) -> List[NDArray[numpy.float64]]:
|
def as_polygons(self, library: Mapping[str, 'Pattern']) -> list[NDArray[numpy.float64]]:
|
||||||
"""
|
"""
|
||||||
Represents the pattern as a list of polygons.
|
Represents the pattern as a list of polygons.
|
||||||
|
|
||||||
@ -274,7 +273,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
pat = self.deepcopy().polygonize().flatten(library=library)
|
pat = self.deepcopy().polygonize().flatten(library=library)
|
||||||
return [shape.vertices + shape.offset for shape in pat.shapes] # type: ignore # mypy can't figure out that shapes are all Polygons now
|
return [shape.vertices + shape.offset for shape in pat.shapes] # type: ignore # mypy can't figure out that shapes are all Polygons now
|
||||||
|
|
||||||
def referenced_patterns(self) -> Set[Optional[str]]:
|
def referenced_patterns(self) -> set[str | None]:
|
||||||
"""
|
"""
|
||||||
Get all pattern namers referenced by this pattern. Non-recursive.
|
Get all pattern namers referenced by this pattern. Non-recursive.
|
||||||
|
|
||||||
@ -285,9 +284,9 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
|
|
||||||
def get_bounds(
|
def get_bounds(
|
||||||
self,
|
self,
|
||||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
library: Mapping[str, 'Pattern'] | None = None,
|
||||||
recurse: bool = True,
|
recurse: bool = True,
|
||||||
) -> Optional[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64] | None:
|
||||||
"""
|
"""
|
||||||
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
||||||
extent of the Pattern's contents in each dimension.
|
extent of the Pattern's contents in each dimension.
|
||||||
@ -330,7 +329,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
|
|
||||||
def get_bounds_nonempty(
|
def get_bounds_nonempty(
|
||||||
self,
|
self,
|
||||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
library: Mapping[str, 'Pattern'] | None = None,
|
||||||
recurse: bool = True,
|
recurse: bool = True,
|
||||||
) -> NDArray[numpy.float64]:
|
) -> NDArray[numpy.float64]:
|
||||||
"""
|
"""
|
||||||
@ -574,10 +573,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
flattened: Dict[Optional[str], Optional[P]] = {}
|
flattened: dict[str | None, P | None] = {}
|
||||||
|
|
||||||
# TODO both Library and Pattern have flatten()... pattern is in-place?
|
# TODO both Library and Pattern have flatten()... pattern is in-place?
|
||||||
def flatten_single(name: Optional[str]) -> None:
|
def flatten_single(name: str | None) -> None:
|
||||||
if name is None:
|
if name is None:
|
||||||
pat = self
|
pat = self
|
||||||
else:
|
else:
|
||||||
@ -611,7 +610,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
|
|
||||||
def visualize(
|
def visualize(
|
||||||
self: P,
|
self: P,
|
||||||
library: Optional[Mapping[str, P]] = None,
|
library: Mapping[str, P] | None = None,
|
||||||
offset: ArrayLike = (0., 0.),
|
offset: ArrayLike = (0., 0.),
|
||||||
line_color: str = 'k',
|
line_color: str = 'k',
|
||||||
fill_color: str = 'none',
|
fill_color: str = 'none',
|
||||||
@ -710,7 +709,7 @@ class NamedPattern(Pattern):
|
|||||||
def __copy__(self) -> Pattern:
|
def __copy__(self) -> Pattern:
|
||||||
return Pattern.__copy__(self)
|
return Pattern.__copy__(self)
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> Pattern:
|
def __deepcopy__(self, memo: dict | None = None) -> Pattern:
|
||||||
return Pattern.__deepcopy__(self, memo)
|
return Pattern.__deepcopy__(self, memo)
|
||||||
|
|
||||||
def as_pattern(self) -> Pattern:
|
def as_pattern(self) -> Pattern:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from typing import Dict, Iterable, List, Tuple, KeysView, ValuesView
|
from typing import Iterable, KeysView, ValuesView, overload, TypeVar
|
||||||
from typing import overload, Union, Optional, TypeVar
|
|
||||||
import warnings
|
import warnings
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
@ -43,7 +42,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
|||||||
'_offset',
|
'_offset',
|
||||||
)
|
)
|
||||||
|
|
||||||
_rotation: Optional[float]
|
_rotation: float | None
|
||||||
""" radians counterclockwise from +x, pointing into device body.
|
""" radians counterclockwise from +x, pointing into device body.
|
||||||
Can be `None` to signify undirected port """
|
Can be `None` to signify undirected port """
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
offset: ArrayLike,
|
offset: ArrayLike,
|
||||||
rotation: Optional[float],
|
rotation: float | None,
|
||||||
ptype: str = 'unk',
|
ptype: str = 'unk',
|
||||||
) -> None:
|
) -> None:
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
@ -61,7 +60,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
|||||||
self.ptype = ptype
|
self.ptype = ptype
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rotation(self) -> Optional[float]:
|
def rotation(self) -> float | None:
|
||||||
""" Rotation, radians counterclockwise, pointing into device body. Can be None. """
|
""" Rotation, radians counterclockwise, pointing into device body. Can be None. """
|
||||||
return self._rotation
|
return self._rotation
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
|||||||
self.rotation += rotation
|
self.rotation += rotation
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_rotation(self: P, rotation: Optional[float]) -> P:
|
def set_rotation(self: P, rotation: float | None) -> P:
|
||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -111,13 +110,13 @@ class PortList(metaclass=ABCMeta):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def ports(self) -> Dict[str, Port]:
|
def ports(self) -> dict[str, Port]:
|
||||||
""" Uniquely-named ports which can be used to snap to other Device instances"""
|
""" Uniquely-named ports which can be used to snap to other Device instances"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ports.setter
|
@ports.setter
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def ports(self, value: Dict[str, Port]) -> None:
|
def ports(self, value: dict[str, Port]) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -125,10 +124,10 @@ class PortList(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __getitem__(self, key: Union[List[str], Tuple[str, ...], KeysView[str], ValuesView[str]]) -> Dict[str, Port]:
|
def __getitem__(self, key: list[str] | tuple[str, ...] | KeysView[str] | ValuesView[str]) -> dict[str, Port]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __getitem__(self, key: Union[str, Iterable[str]]) -> Union[Port, Dict[str, Port]]:
|
def __getitem__(self, key: str | Iterable[str]) -> Port | dict[str, Port]:
|
||||||
"""
|
"""
|
||||||
For convenience, ports can be read out using square brackets:
|
For convenience, ports can be read out using square brackets:
|
||||||
- `pattern['A'] == Port((0, 0), 0)`
|
- `pattern['A'] == Port((0, 0), 0)`
|
||||||
@ -150,7 +149,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
|
|
||||||
def rename_ports(
|
def rename_ports(
|
||||||
self: PL,
|
self: PL,
|
||||||
mapping: Dict[str, Optional[str]],
|
mapping: dict[str, str | None],
|
||||||
overwrite: bool = False,
|
overwrite: bool = False,
|
||||||
) -> PL:
|
) -> PL:
|
||||||
"""
|
"""
|
||||||
@ -158,7 +157,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
Ports can be explicitly deleted by mapping them to `None`.
|
Ports can be explicitly deleted by mapping them to `None`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
mapping: Dict of `{'old_name': 'new_name'}` pairs. Names can be mapped
|
mapping: dict of `{'old_name': 'new_name'}` pairs. Names can be mapped
|
||||||
to `None` to perform an explicit deletion. `'new_name'` can also
|
to `None` to perform an explicit deletion. `'new_name'` can also
|
||||||
overwrite an existing non-renamed port to implicitly delete it if
|
overwrite an existing non-renamed port to implicitly delete it if
|
||||||
`overwrite` is set to `True`.
|
`overwrite` is set to `True`.
|
||||||
@ -183,7 +182,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
self: PL,
|
self: PL,
|
||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
names: Tuple[str, str] = ('A', 'B'),
|
names: tuple[str, str] = ('A', 'B'),
|
||||||
ptype: str = 'unk',
|
ptype: str = 'unk',
|
||||||
) -> PL:
|
) -> PL:
|
||||||
"""
|
"""
|
||||||
@ -210,8 +209,8 @@ class PortList(metaclass=ABCMeta):
|
|||||||
def check_ports(
|
def check_ports(
|
||||||
self: PL,
|
self: PL,
|
||||||
other_names: Iterable[str],
|
other_names: Iterable[str],
|
||||||
map_in: Optional[Dict[str, str]] = None,
|
map_in: dict[str, str] | None = None,
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
map_out: dict[str, str | None] | None = None,
|
||||||
) -> PL:
|
) -> PL:
|
||||||
"""
|
"""
|
||||||
Given the provided port mappings, check that:
|
Given the provided port mappings, check that:
|
||||||
@ -221,9 +220,9 @@ class PortList(metaclass=ABCMeta):
|
|||||||
Args:
|
Args:
|
||||||
other_names: List of port names being considered for inclusion into
|
other_names: List of port names being considered for inclusion into
|
||||||
`self.ports` (before mapping)
|
`self.ports` (before mapping)
|
||||||
map_in: Dict of `{'self_port': 'other_port'}` mappings, specifying
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
port connections between the two devices.
|
port connections between the two devices.
|
||||||
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for unconnected `other_names` ports.
|
new names for unconnected `other_names` ports.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -279,18 +278,18 @@ class PortList(metaclass=ABCMeta):
|
|||||||
def find_transform(
|
def find_transform(
|
||||||
self: PL,
|
self: PL,
|
||||||
other: PL2,
|
other: PL2,
|
||||||
map_in: Dict[str, str],
|
map_in: dict[str, str],
|
||||||
*,
|
*,
|
||||||
mirrored: Tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
set_rotation: Optional[bool] = None,
|
set_rotation: bool | None = None,
|
||||||
) -> Tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||||
"""
|
"""
|
||||||
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.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: a device
|
other: a device
|
||||||
map_in: Dict of `{'self_port': 'other_port'}` mappings, specifying
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
port connections between the two devices.
|
port connections between the two devices.
|
||||||
mirrored: Mirrors `other` across the x or y axes prior to
|
mirrored: Mirrors `other` across the x or y axes prior to
|
||||||
connecting any ports.
|
connecting any ports.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"""
|
"""
|
||||||
#TODO more top-level documentation
|
#TODO more top-level documentation
|
||||||
|
|
||||||
from typing import Dict, Optional, Sequence, Mapping, Union, TYPE_CHECKING, Any, TypeVar, cast
|
from typing import Sequence, Mapping, TYPE_CHECKING, Any, TypeVar, cast
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -41,7 +41,7 @@ class Ref(
|
|||||||
'_offset', '_rotation', 'scale', '_repetition', '_annotations',
|
'_offset', '_rotation', 'scale', '_repetition', '_annotations',
|
||||||
)
|
)
|
||||||
|
|
||||||
_target: Optional[str]
|
_target: str | None
|
||||||
""" The name of the `Pattern` being instanced """
|
""" The name of the `Pattern` being instanced """
|
||||||
|
|
||||||
_mirrored: NDArray[numpy.bool_]
|
_mirrored: NDArray[numpy.bool_]
|
||||||
@ -49,14 +49,14 @@ class Ref(
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
target: Union[None, str, 'NamedPattern'],
|
target: str | 'NamedPattern' | None,
|
||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: Optional[Sequence[bool]] = None,
|
mirrored: Sequence[bool] | None = None,
|
||||||
scale: float = 1.0,
|
scale: float = 1.0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -91,7 +91,7 @@ class Ref(
|
|||||||
)
|
)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Ref':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Ref':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new.repetition = copy.deepcopy(self.repetition, memo)
|
new.repetition = copy.deepcopy(self.repetition, memo)
|
||||||
@ -100,11 +100,11 @@ class Ref(
|
|||||||
|
|
||||||
# target property
|
# target property
|
||||||
@property
|
@property
|
||||||
def target(self) -> Optional[str]:
|
def target(self) -> str | None:
|
||||||
return self._target
|
return self._target
|
||||||
|
|
||||||
@target.setter
|
@target.setter
|
||||||
def target(self, val: Optional[str]) -> None:
|
def target(self, val: str | None) -> None:
|
||||||
if val is not None and not isinstance(val, str):
|
if val is not None and not isinstance(val, str):
|
||||||
raise PatternError(f'Provided target {val} is not a str or None!')
|
raise PatternError(f'Provided target {val} is not a str or None!')
|
||||||
self._target = val
|
self._target = val
|
||||||
@ -123,8 +123,8 @@ class Ref(
|
|||||||
def as_pattern(
|
def as_pattern(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
pattern: Optional['Pattern'] = None,
|
pattern: 'Pattern' | None = None,
|
||||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
library: Mapping[str, 'Pattern'] | None = None,
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -180,9 +180,9 @@ class Ref(
|
|||||||
def get_bounds(
|
def get_bounds(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
pattern: Optional['Pattern'] = None,
|
pattern: 'Pattern' | None = None,
|
||||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
library: Mapping[str, 'Pattern'] | None = None,
|
||||||
) -> Optional[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64] | None:
|
||||||
"""
|
"""
|
||||||
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
||||||
extent of the `Ref` in each dimension.
|
extent of the `Ref` in each dimension.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
instances of an object .
|
instances of an object .
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Union, Dict, Optional, Any, Type
|
from typing import Any, Type
|
||||||
import copy
|
import copy
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class Grid(Repetition):
|
|||||||
_a_count: int
|
_a_count: int
|
||||||
""" Number of instances along the direction specified by the `a_vector` """
|
""" Number of instances along the direction specified by the `a_vector` """
|
||||||
|
|
||||||
_b_vector: Optional[NDArray[numpy.float64]]
|
_b_vector: NDArray[numpy.float64] | None
|
||||||
""" Vector `[x, y]` specifying a second lattice vector for the grid.
|
""" Vector `[x, y]` specifying a second lattice vector for the grid.
|
||||||
Specifies center-to-center spacing between adjacent elements.
|
Specifies center-to-center spacing between adjacent elements.
|
||||||
Can be `None` for a 1D array.
|
Can be `None` for a 1D array.
|
||||||
@ -65,8 +65,8 @@ class Grid(Repetition):
|
|||||||
self,
|
self,
|
||||||
a_vector: ArrayLike,
|
a_vector: ArrayLike,
|
||||||
a_count: int,
|
a_count: int,
|
||||||
b_vector: Optional[ArrayLike] = None,
|
b_vector: ArrayLike | None = None,
|
||||||
b_count: Optional[int] = 1,
|
b_count: int | None = 1,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -133,7 +133,7 @@ class Grid(Repetition):
|
|||||||
)
|
)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Grid':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Grid':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
return new
|
return new
|
||||||
@ -154,7 +154,7 @@ class Grid(Repetition):
|
|||||||
|
|
||||||
# b_vector property
|
# b_vector property
|
||||||
@property
|
@property
|
||||||
def b_vector(self) -> Optional[NDArray[numpy.float64]]:
|
def b_vector(self) -> NDArray[numpy.float64] | None:
|
||||||
return self._b_vector
|
return self._b_vector
|
||||||
|
|
||||||
@b_vector.setter
|
@b_vector.setter
|
||||||
@ -228,7 +228,7 @@ class Grid(Repetition):
|
|||||||
self.b_vector[1 - axis] *= -1
|
self.b_vector[1 - axis] *= -1
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_bounds(self) -> Optional[NDArray[numpy.float64]]:
|
def get_bounds(self) -> NDArray[numpy.float64] | None:
|
||||||
"""
|
"""
|
||||||
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
||||||
extent of the `Grid` in each dimension.
|
extent of the `Grid` in each dimension.
|
||||||
@ -237,7 +237,7 @@ class Grid(Repetition):
|
|||||||
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
||||||
"""
|
"""
|
||||||
a_extent = self.a_vector * self.a_count
|
a_extent = self.a_vector * self.a_count
|
||||||
b_extent = self.b_vector * self.b_count if (self.b_vector is not None) else 0 # type: Union[NDArray[numpy.float64], float]
|
b_extent = self.b_vector * self.b_count if (self.b_vector is not None) else 0 # type: NDArray[numpy.float64] | float
|
||||||
|
|
||||||
corners = numpy.stack(((0, 0), a_extent, b_extent, a_extent + b_extent))
|
corners = numpy.stack(((0, 0), a_extent, b_extent, a_extent + b_extent))
|
||||||
xy_min = numpy.min(corners, axis=0)
|
xy_min = numpy.min(corners, axis=0)
|
||||||
@ -350,7 +350,7 @@ class Arbitrary(Repetition):
|
|||||||
self.displacements[1 - axis] *= -1
|
self.displacements[1 - axis] *= -1
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_bounds(self) -> Optional[NDArray[numpy.float64]]:
|
def get_bounds(self) -> NDArray[numpy.float64] | None:
|
||||||
"""
|
"""
|
||||||
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
||||||
extent of the `displacements` in each dimension.
|
extent of the `displacements` in each dimension.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Dict, Optional, Sequence, Any
|
from typing import Sequence, Any
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
|
|
||||||
@ -157,8 +157,8 @@ class Arc(Shape):
|
|||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if raw:
|
if raw:
|
||||||
@ -184,7 +184,7 @@ class Arc(Shape):
|
|||||||
self.layer = layer
|
self.layer = layer
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Arc':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Arc':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -195,9 +195,9 @@ class Arc(Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> List[Polygon]:
|
) -> list[Polygon]:
|
||||||
if (num_vertices is None) and (max_arclen is None):
|
if (num_vertices is None) and (max_arclen is None):
|
||||||
raise PatternError('Max number of points and arclength left unspecified'
|
raise PatternError('Max number of points and arclength left unspecified'
|
||||||
+ ' (default was also overridden)')
|
+ ' (default was also overridden)')
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
from typing import List, Dict, Optional
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -46,8 +45,8 @@ class Circle(Shape):
|
|||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if raw:
|
if raw:
|
||||||
@ -64,7 +63,7 @@ class Circle(Shape):
|
|||||||
self.annotations = annotations if annotations is not None else {}
|
self.annotations = annotations if annotations is not None else {}
|
||||||
self.layer = layer
|
self.layer = layer
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Circle':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Circle':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -73,14 +72,14 @@ class Circle(Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> List[Polygon]:
|
) -> list[Polygon]:
|
||||||
if (num_vertices is None) and (max_arclen is None):
|
if (num_vertices is None) and (max_arclen is None):
|
||||||
raise PatternError('Number of points and arclength left '
|
raise PatternError('Number of points and arclength left '
|
||||||
'unspecified (default was also overridden)')
|
'unspecified (default was also overridden)')
|
||||||
|
|
||||||
n: List[float] = []
|
n: list[float] = []
|
||||||
if num_vertices is not None:
|
if num_vertices is not None:
|
||||||
n += [num_vertices]
|
n += [num_vertices]
|
||||||
if max_arclen is not None:
|
if max_arclen is not None:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Dict, Sequence, Optional, Any
|
from typing import Sequence, Any
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
|
|
||||||
@ -92,8 +92,8 @@ class Ellipse(Shape):
|
|||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if raw:
|
if raw:
|
||||||
@ -114,7 +114,7 @@ class Ellipse(Shape):
|
|||||||
self.layer = layer
|
self.layer = layer
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Ellipse':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Ellipse':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -124,9 +124,9 @@ class Ellipse(Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> List[Polygon]:
|
) -> list[Polygon]:
|
||||||
if (num_vertices is None) and (max_arclen is None):
|
if (num_vertices is None) and (max_arclen is None):
|
||||||
raise PatternError('Number of points and arclength left unspecified'
|
raise PatternError('Number of points and arclength left unspecified'
|
||||||
' (default was also overridden)')
|
' (default was also overridden)')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict, Optional, Sequence, Any, cast
|
from typing import Sequence, Any, cast
|
||||||
import copy
|
import copy
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ class Path(Shape):
|
|||||||
_vertices: NDArray[numpy.float64]
|
_vertices: NDArray[numpy.float64]
|
||||||
_width: float
|
_width: float
|
||||||
_cap: PathCap
|
_cap: PathCap
|
||||||
_cap_extensions: Optional[NDArray[numpy.float64]]
|
_cap_extensions: NDArray[numpy.float64] | None
|
||||||
|
|
||||||
Cap = PathCap
|
Cap = PathCap
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ class Path(Shape):
|
|||||||
|
|
||||||
# cap_extensions property
|
# cap_extensions property
|
||||||
@property
|
@property
|
||||||
def cap_extensions(self) -> Optional[Any]: # TODO mypy#3004 NDArray[numpy.float64]]:
|
def cap_extensions(self) -> Any | None: # TODO mypy#3004 NDArray[numpy.float64]]:
|
||||||
"""
|
"""
|
||||||
Path end-cap extension
|
Path end-cap extension
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ class Path(Shape):
|
|||||||
return self._cap_extensions
|
return self._cap_extensions
|
||||||
|
|
||||||
@cap_extensions.setter
|
@cap_extensions.setter
|
||||||
def cap_extensions(self, vals: Optional[ArrayLike]) -> None:
|
def cap_extensions(self, vals: ArrayLike | None) -> 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:
|
||||||
@ -150,13 +150,13 @@ class Path(Shape):
|
|||||||
width: float = 0.0,
|
width: float = 0.0,
|
||||||
*,
|
*,
|
||||||
cap: PathCap = PathCap.Flush,
|
cap: PathCap = PathCap.Flush,
|
||||||
cap_extensions: Optional[ArrayLike] = None,
|
cap_extensions: ArrayLike | None = None,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._cap_extensions = None # Since .cap setter might access it
|
self._cap_extensions = None # Since .cap setter might access it
|
||||||
@ -185,7 +185,7 @@ class Path(Shape):
|
|||||||
self.rotate(rotation)
|
self.rotate(rotation)
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Path':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Path':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -197,10 +197,10 @@ class Path(Shape):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def travel(
|
def travel(
|
||||||
travel_pairs: Sequence[Tuple[float, float]],
|
travel_pairs: Sequence[tuple[float, float]],
|
||||||
width: float = 0.0,
|
width: float = 0.0,
|
||||||
cap: PathCap = PathCap.Flush,
|
cap: PathCap = PathCap.Flush,
|
||||||
cap_extensions: Optional[Tuple[float, float]] = None,
|
cap_extensions: tuple[float, float] | None = None,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
@ -243,9 +243,9 @@ class Path(Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = None,
|
num_vertices: int | None = None,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> List['Polygon']:
|
) -> 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)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Dict, Optional, Sequence, Any, cast
|
from typing import Sequence, Any, cast
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -83,8 +83,8 @@ class Polygon(Shape):
|
|||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: Sequence[bool] = (False, False),
|
mirrored: Sequence[bool] = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if raw:
|
if raw:
|
||||||
@ -104,7 +104,7 @@ class Polygon(Shape):
|
|||||||
self.rotate(rotation)
|
self.rotate(rotation)
|
||||||
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
[self.mirror(a) for a, do in enumerate(mirrored) if do]
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Polygon':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Polygon':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -119,7 +119,7 @@ class Polygon(Shape):
|
|||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
) -> 'Polygon':
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw a square given side_length, centered on the origin.
|
Draw a square given side_length, centered on the origin.
|
||||||
@ -151,7 +151,7 @@ class Polygon(Shape):
|
|||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
offset: ArrayLike = (0.0, 0.0),
|
offset: ArrayLike = (0.0, 0.0),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
) -> 'Polygon':
|
) -> '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.
|
||||||
@ -178,16 +178,16 @@ class Polygon(Shape):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def rect(
|
def rect(
|
||||||
*,
|
*,
|
||||||
xmin: Optional[float] = None,
|
xmin: float | None = None,
|
||||||
xctr: Optional[float] = None,
|
xctr: float | None = None,
|
||||||
xmax: Optional[float] = None,
|
xmax: float | None = None,
|
||||||
lx: Optional[float] = None,
|
lx: float | None = None,
|
||||||
ymin: Optional[float] = None,
|
ymin: float | None = None,
|
||||||
yctr: Optional[float] = None,
|
yctr: float | None = None,
|
||||||
ymax: Optional[float] = None,
|
ymax: float | None = None,
|
||||||
ly: Optional[float] = None,
|
ly: float | None = None,
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
) -> 'Polygon':
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw a rectangle by specifying side/center positions.
|
Draw a rectangle by specifying side/center positions.
|
||||||
@ -276,13 +276,13 @@ class Polygon(Shape):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def octagon(
|
def octagon(
|
||||||
*,
|
*,
|
||||||
side_length: Optional[float] = None,
|
side_length: float | None = None,
|
||||||
inner_radius: Optional[float] = None,
|
inner_radius: float | None = None,
|
||||||
regular: bool = True,
|
regular: bool = True,
|
||||||
center: ArrayLike = (0.0, 0.0),
|
center: ArrayLike = (0.0, 0.0),
|
||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
) -> 'Polygon':
|
) -> 'Polygon':
|
||||||
"""
|
"""
|
||||||
Draw an octagon given one of (side length, inradius, circumradius).
|
Draw an octagon given one of (side length, inradius, circumradius).
|
||||||
@ -333,9 +333,9 @@ class Polygon(Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = None, # unused
|
num_vertices: int | None = None, # unused
|
||||||
max_arclen: Optional[float] = None, # unused
|
max_arclen: float | None = None, # unused
|
||||||
) -> List['Polygon']:
|
) -> list['Polygon']:
|
||||||
return [copy.deepcopy(self)]
|
return [copy.deepcopy(self)]
|
||||||
|
|
||||||
def get_bounds(self) -> NDArray[numpy.float64]:
|
def get_bounds(self) -> NDArray[numpy.float64]:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Callable, TypeVar, Optional, TYPE_CHECKING
|
from typing import Callable, TypeVar, TYPE_CHECKING
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -15,9 +15,9 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
# Type definitions
|
# Type definitions
|
||||||
normalized_shape_tuple = Tuple[
|
normalized_shape_tuple = tuple[
|
||||||
Tuple,
|
tuple,
|
||||||
Tuple[NDArray[numpy.float64], float, float, bool],
|
tuple[NDArray[numpy.float64], float, float, bool],
|
||||||
Callable[[], 'Shape'],
|
Callable[[], 'Shape'],
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -49,9 +49,9 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = None,
|
num_vertices: int | None = None,
|
||||||
max_arclen: Optional[float] = None,
|
max_arclen: float | None = None,
|
||||||
) -> List['Polygon']:
|
) -> list['Polygon']:
|
||||||
"""
|
"""
|
||||||
Returns a list of polygons which approximate the shape.
|
Returns a list of polygons which approximate the shape.
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
|
|||||||
self,
|
self,
|
||||||
grid_x: ArrayLike,
|
grid_x: ArrayLike,
|
||||||
grid_y: ArrayLike,
|
grid_y: ArrayLike,
|
||||||
) -> List['Polygon']:
|
) -> 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.
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
|
|||||||
self,
|
self,
|
||||||
grid_x: ArrayLike,
|
grid_x: ArrayLike,
|
||||||
grid_y: ArrayLike,
|
grid_y: ArrayLike,
|
||||||
) -> List['Polygon']:
|
) -> 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.
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import List, Tuple, Dict, Sequence, Optional, Any
|
from typing import Sequence, Any
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -74,8 +74,8 @@ class Text(RotatableImpl, Shape):
|
|||||||
rotation: float = 0.0,
|
rotation: float = 0.0,
|
||||||
mirrored: ArrayLike = (False, False),
|
mirrored: ArrayLike = (False, False),
|
||||||
layer: layer_t = 0,
|
layer: layer_t = 0,
|
||||||
repetition: Optional[Repetition] = None,
|
repetition: Repetition | None = None,
|
||||||
annotations: Optional[annotations_t] = None,
|
annotations: annotations_t | None = None,
|
||||||
raw: bool = False,
|
raw: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
if raw:
|
if raw:
|
||||||
@ -100,7 +100,7 @@ class Text(RotatableImpl, Shape):
|
|||||||
self.annotations = annotations if annotations is not None else {}
|
self.annotations = annotations if annotations is not None else {}
|
||||||
self.font_path = font_path
|
self.font_path = font_path
|
||||||
|
|
||||||
def __deepcopy__(self, memo: Optional[Dict] = None) -> 'Text':
|
def __deepcopy__(self, memo: dict | None = None) -> 'Text':
|
||||||
memo = {} if memo is None else memo
|
memo = {} if memo is None else memo
|
||||||
new = copy.copy(self)
|
new = copy.copy(self)
|
||||||
new._offset = self._offset.copy()
|
new._offset = self._offset.copy()
|
||||||
@ -110,9 +110,9 @@ class Text(RotatableImpl, Shape):
|
|||||||
|
|
||||||
def to_polygons(
|
def to_polygons(
|
||||||
self,
|
self,
|
||||||
num_vertices: Optional[int] = None, # unused
|
num_vertices: int | None = None, # unused
|
||||||
max_arclen: Optional[float] = None, # unused
|
max_arclen: float | None = None, # unused
|
||||||
) -> List[Polygon]:
|
) -> list[Polygon]:
|
||||||
all_polygons = []
|
all_polygons = []
|
||||||
total_advance = 0.0
|
total_advance = 0.0
|
||||||
for char in self.string:
|
for char in self.string:
|
||||||
@ -172,7 +172,7 @@ def get_char_as_polygons(
|
|||||||
font_path: str,
|
font_path: str,
|
||||||
char: str,
|
char: str,
|
||||||
resolution: float = 48 * 64,
|
resolution: float = 48 * 64,
|
||||||
) -> Tuple[List[List[List[float]]], float]:
|
) -> 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
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ def get_char_as_polygons(
|
|||||||
tags = outline.tags[start:end + 1]
|
tags = outline.tags[start:end + 1]
|
||||||
tags.append(tags[0])
|
tags.append(tags[0])
|
||||||
|
|
||||||
segments: List[List[List[float]]] = []
|
segments: list[list[list[float]]] = []
|
||||||
for j, point in enumerate(points):
|
for j, point in enumerate(points):
|
||||||
# If we already have a segment, add this point to it
|
# If we already have a segment, add this point to it
|
||||||
if j > 0:
|
if j > 0:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import TypeVar, Tuple
|
from typing import TypeVar
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ class Mirrorable(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def mirror2d(self: T, axes: Tuple[bool, bool]) -> T:
|
def mirror2d(self: T, axes: tuple[bool, bool]) -> T:
|
||||||
"""
|
"""
|
||||||
Optionally mirror the entity across both axes
|
Optionally mirror the entity across both axes
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# TODO top-level comment about how traits should set __slots__ = (), and how to use AutoSlots
|
# TODO top-level comment about how traits should set __slots__ = (), and how to use AutoSlots
|
||||||
|
|
||||||
from typing import TypeVar, Any, Optional
|
from typing import TypeVar, Any
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -65,7 +65,7 @@ class Positionable(metaclass=ABCMeta):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_bounds(self) -> Optional[NDArray[numpy.float64]]:
|
def get_bounds(self) -> NDArray[numpy.float64] | None:
|
||||||
"""
|
"""
|
||||||
Returns `[[x_min, y_min], [x_max, y_max]]` which specify a minimal bounding box for the entity.
|
Returns `[[x_min, y_min], [x_max, y_max]]` which specify a minimal bounding box for the entity.
|
||||||
Returns `None` for an empty entity.
|
Returns `None` for an empty entity.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from typing import TypeVar, Optional, TYPE_CHECKING
|
from typing import TypeVar, TYPE_CHECKING
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
from ..error import MasqueError
|
from ..error import MasqueError
|
||||||
@ -26,7 +26,7 @@ class Repeatable(metaclass=ABCMeta):
|
|||||||
'''
|
'''
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def repetition(self) -> Optional['Repetition']:
|
def repetition(self) -> 'Repetition' | None:
|
||||||
"""
|
"""
|
||||||
Repetition object, or None (single instance only)
|
Repetition object, or None (single instance only)
|
||||||
"""
|
"""
|
||||||
@ -34,14 +34,14 @@ class Repeatable(metaclass=ABCMeta):
|
|||||||
|
|
||||||
# @repetition.setter
|
# @repetition.setter
|
||||||
# @abstractmethod
|
# @abstractmethod
|
||||||
# def repetition(self, repetition: Optional['Repetition']):
|
# def repetition(self, repetition: 'Repetition' | None):
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
'''
|
'''
|
||||||
---- Methods
|
---- Methods
|
||||||
'''
|
'''
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def set_repetition(self: T, repetition: Optional['Repetition']) -> T:
|
def set_repetition(self: T, repetition: 'Repetition' | None) -> T:
|
||||||
"""
|
"""
|
||||||
Set the repetition
|
Set the repetition
|
||||||
|
|
||||||
@ -60,18 +60,18 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
__slots__ = _empty_slots
|
__slots__ = _empty_slots
|
||||||
|
|
||||||
_repetition: Optional['Repetition']
|
_repetition: 'Repetition' | None
|
||||||
""" Repetition object, or None (single instance only) """
|
""" Repetition object, or None (single instance only) """
|
||||||
|
|
||||||
'''
|
'''
|
||||||
---- Non-abstract properties
|
---- Non-abstract properties
|
||||||
'''
|
'''
|
||||||
@property
|
@property
|
||||||
def repetition(self) -> Optional['Repetition']:
|
def repetition(self) -> 'Repetition' | None:
|
||||||
return self._repetition
|
return self._repetition
|
||||||
|
|
||||||
@repetition.setter
|
@repetition.setter
|
||||||
def repetition(self, repetition: Optional['Repetition']):
|
def repetition(self, repetition: 'Repetition' | None):
|
||||||
from ..repetition import Repetition
|
from ..repetition import Repetition
|
||||||
if repetition is not None and not isinstance(repetition, Repetition):
|
if repetition is not None and not isinstance(repetition, Repetition):
|
||||||
raise MasqueError(f'{repetition} is not a valid Repetition object!')
|
raise MasqueError(f'{repetition} is not a valid Repetition object!')
|
||||||
@ -80,6 +80,6 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
|
|||||||
'''
|
'''
|
||||||
---- Non-abstract methods
|
---- Non-abstract methods
|
||||||
'''
|
'''
|
||||||
def set_repetition(self: I, repetition: Optional['Repetition']) -> I:
|
def set_repetition(self: I, repetition: 'Repetition' | None) -> I:
|
||||||
self.repetition = repetition
|
self.repetition = repetition
|
||||||
return self
|
return self
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
2D bin-packing
|
2D bin-packing
|
||||||
"""
|
"""
|
||||||
from typing import Tuple, List, Set, Sequence, Callable, Mapping
|
from typing import Sequence, Callable, Mapping
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray, ArrayLike
|
from numpy.typing import NDArray, ArrayLike
|
||||||
@ -16,7 +16,7 @@ def maxrects_bssf(
|
|||||||
containers: ArrayLike,
|
containers: ArrayLike,
|
||||||
presort: bool = True,
|
presort: bool = True,
|
||||||
allow_rejects: bool = True,
|
allow_rejects: bool = True,
|
||||||
) -> Tuple[NDArray[numpy.float64], Set[int]]:
|
) -> tuple[NDArray[numpy.float64], set[int]]:
|
||||||
"""
|
"""
|
||||||
sizes should be Nx2
|
sizes should be Nx2
|
||||||
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
||||||
@ -88,7 +88,7 @@ def guillotine_bssf_sas(rect_sizes: numpy.ndarray,
|
|||||||
regions: numpy.ndarray,
|
regions: numpy.ndarray,
|
||||||
presort: bool = True,
|
presort: bool = True,
|
||||||
allow_rejects: bool = True,
|
allow_rejects: bool = True,
|
||||||
) -> Tuple[numpy.ndarray, Set[int]]:
|
) -> tuple[numpy.ndarray, set[int]]:
|
||||||
"""
|
"""
|
||||||
sizes should be Nx2
|
sizes should be Nx2
|
||||||
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
||||||
@ -146,11 +146,11 @@ def pack_patterns(
|
|||||||
library: Mapping[str, Pattern],
|
library: Mapping[str, Pattern],
|
||||||
patterns: Sequence[str],
|
patterns: Sequence[str],
|
||||||
regions: numpy.ndarray,
|
regions: numpy.ndarray,
|
||||||
spacing: Tuple[float, float],
|
spacing: tuple[float, float],
|
||||||
presort: bool = True,
|
presort: bool = True,
|
||||||
allow_rejects: bool = True,
|
allow_rejects: bool = True,
|
||||||
packer: Callable = maxrects_bssf,
|
packer: Callable = maxrects_bssf,
|
||||||
) -> Tuple[Pattern, List[str]]:
|
) -> tuple[Pattern, list[str]]:
|
||||||
half_spacing = numpy.array(spacing) / 2
|
half_spacing = numpy.array(spacing) / 2
|
||||||
|
|
||||||
bounds = [library[pp].get_bounds() for pp in patterns]
|
bounds = [library[pp].get_bounds() for pp in patterns]
|
||||||
|
@ -6,7 +6,7 @@ and retrieving it (`data_to_ports`).
|
|||||||
the port locations. This particular approach is just a sensible default; feel free to
|
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.
|
to write equivalent functions for your own format or alternate storage methods.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Optional, Mapping
|
from typing import Sequence, Mapping
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -56,7 +56,7 @@ def data_to_ports(
|
|||||||
pattern: Pattern, # Pattern is good since we don't want to do library[name] to avoid infinite recursion.
|
pattern: Pattern, # Pattern is good since we don't want to do library[name] to avoid infinite recursion.
|
||||||
# LazyLibrary protects against library[ref.target] causing a circular lookup.
|
# LazyLibrary protects against library[ref.target] causing a circular lookup.
|
||||||
# For others, maybe check for cycles up front? TODO
|
# For others, maybe check for cycles up front? TODO
|
||||||
name: Optional[str] = None, # Note: name optional, but arg order different from read(postprocess=)
|
name: str | None = None, # Note: name optional, but arg order different from read(postprocess=)
|
||||||
max_depth: int = 0,
|
max_depth: int = 0,
|
||||||
skip_subcells: bool = True,
|
skip_subcells: bool = True,
|
||||||
# TODO missing ok?
|
# TODO missing ok?
|
||||||
@ -130,7 +130,7 @@ def data_to_ports(
|
|||||||
def data_to_ports_flat(
|
def data_to_ports_flat(
|
||||||
layers: Sequence[layer_t],
|
layers: Sequence[layer_t],
|
||||||
pattern: Pattern,
|
pattern: Pattern,
|
||||||
cell_name: Optional[str] = None,
|
cell_name: str | None = None,
|
||||||
) -> Pattern:
|
) -> Pattern:
|
||||||
"""
|
"""
|
||||||
Examine `pattern` for labels specifying port info, and use that info
|
Examine `pattern` for labels specifying port info, and use that info
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Geometric transforms
|
Geometric transforms
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Tuple
|
from typing import Sequence
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
@ -21,7 +21,7 @@ def rotation_matrix_2d(theta: float) -> NDArray[numpy.float64]:
|
|||||||
[numpy.sin(theta), +numpy.cos(theta)]])
|
[numpy.sin(theta), +numpy.cos(theta)]])
|
||||||
|
|
||||||
|
|
||||||
def normalize_mirror(mirrored: Sequence[bool]) -> Tuple[bool, float]:
|
def normalize_mirror(mirrored: Sequence[bool]) -> tuple[bool, float]:
|
||||||
"""
|
"""
|
||||||
Converts 0-2 mirror operations `(mirror_across_x_axis, mirror_across_y_axis)`
|
Converts 0-2 mirror operations `(mirror_across_x_axis, mirror_across_y_axis)`
|
||||||
into 0-1 mirror operations and a rotation
|
into 0-1 mirror operations and a rotation
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
Type definitions
|
Type definitions
|
||||||
"""
|
"""
|
||||||
from typing import Union, Tuple, Dict, List, Protocol
|
from typing import Protocol
|
||||||
|
|
||||||
|
|
||||||
layer_t = Union[int, Tuple[int, int], str]
|
layer_t = int | tuple[int, int] | str
|
||||||
annotations_t = Dict[str, List[Union[int, float, str]]]
|
annotations_t = dict[str, list[int | float | str]]
|
||||||
|
|
||||||
|
|
||||||
class SupportsBool(Protocol):
|
class SupportsBool(Protocol):
|
||||||
|
@ -113,5 +113,3 @@ def poly_contains_points(
|
|||||||
inside = nontrivial.copy()
|
inside = nontrivial.copy()
|
||||||
inside[nontrivial] = nontrivial_inside
|
inside[nontrivial] = nontrivial_inside
|
||||||
return inside
|
return inside
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user