modernize type annotations
This commit is contained in:
parent
ada8c591a0
commit
1463535676
@ -1,4 +1,4 @@
|
||||
from typing import Tuple, Sequence
|
||||
from typing import Sequence
|
||||
|
||||
import numpy
|
||||
from numpy import pi
|
||||
|
@ -1,5 +1,5 @@
|
||||
# TODO update tutorials
|
||||
from typing import Tuple, Sequence, Dict, Mapping
|
||||
from typing import Sequence, Mapping
|
||||
|
||||
import numpy
|
||||
from numpy import pi
|
||||
@ -43,7 +43,7 @@ def perturbed_l3(
|
||||
trench_layer: layer_t = (1, 0),
|
||||
shifts_a: Sequence[float] = (0.15, 0, 0.075),
|
||||
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,
|
||||
trench_width: float = 1200,
|
||||
) -> Pattern:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Tuple, Sequence, Callable
|
||||
from typing import Sequence, Callable
|
||||
from pprint import pformat
|
||||
|
||||
import numpy
|
||||
@ -116,12 +116,12 @@ if __name__ == '__main__':
|
||||
# other: Pattern,
|
||||
# label_layer: layer_t = 'WATLAYER',
|
||||
# *,
|
||||
# port_map: Optional[Dict[str, Optional[str]]] = None,
|
||||
# port_map: Dict[str, str | None] | None = None,
|
||||
# **kwargs,
|
||||
# ) -> 'prout':
|
||||
#
|
||||
# Pattern.place(self, other, port_map=port_map, **kwargs)
|
||||
# name: Optional[str]
|
||||
# name: str | None
|
||||
# for name in other.ports:
|
||||
# if port_map:
|
||||
# assert(name is not None)
|
||||
|
@ -2,7 +2,7 @@
|
||||
Routines for creating normalized 2D lattices and common photonic crystal
|
||||
cavity designs.
|
||||
"""
|
||||
from typing import Sequence, Tuple
|
||||
from typing import Sequence
|
||||
|
||||
import numpy
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
@ -1,12 +1,10 @@
|
||||
from typing import Dict, TypeVar
|
||||
#from typing import Union, Optional, MutableMapping, TYPE_CHECKING
|
||||
from typing import TypeVar
|
||||
import copy
|
||||
import logging
|
||||
|
||||
import numpy
|
||||
from numpy.typing import ArrayLike
|
||||
|
||||
#from .pattern import Pattern
|
||||
from .ref import Ref
|
||||
from .ports import PortList, Port
|
||||
from .utils import rotation_matrix_2d, normalize_mirror
|
||||
@ -28,21 +26,21 @@ class Abstract(PortList):
|
||||
name: str
|
||||
""" 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"""
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, Port]:
|
||||
def ports(self) -> dict[str, Port]:
|
||||
return self._ports
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value: Dict[str, Port]) -> None:
|
||||
def ports(self, value: dict[str, Port]) -> None:
|
||||
self._ports = value
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
ports: Dict[str, Port],
|
||||
ports: dict[str, Port],
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.ports = copy.deepcopy(ports)
|
||||
@ -50,7 +48,7 @@ class Abstract(PortList):
|
||||
# def build(
|
||||
# self,
|
||||
# library: 'MutableLibrary',
|
||||
# tools: Union[None, 'Tool', MutableMapping[Optional[str], 'Tool']] = None,
|
||||
# tools: None | 'Tool' | MutableMapping[str | None, 'Tool'] = None,
|
||||
# ) -> 'Builder':
|
||||
# """
|
||||
# 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 MutableMapping, Mapping
|
||||
from typing import TypeVar, Sequence, MutableMapping, Mapping
|
||||
import copy
|
||||
import logging
|
||||
|
||||
@ -84,7 +83,7 @@ class Builder(PortList):
|
||||
pattern: Pattern
|
||||
""" Layout of this device """
|
||||
|
||||
library: Optional[MutableLibrary]
|
||||
library: MutableLibrary | None
|
||||
"""
|
||||
Library from which existing patterns should be referenced, and to which
|
||||
new ones should be added
|
||||
@ -94,20 +93,20 @@ class Builder(PortList):
|
||||
""" If True, plug()/place() are skipped (for debugging)"""
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, Port]:
|
||||
def ports(self) -> dict[str, Port]:
|
||||
return self.pattern.ports
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value: Dict[str, Port]) -> None:
|
||||
def ports(self, value: dict[str, Port]) -> None:
|
||||
self.pattern.ports = value
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
library: Optional[MutableLibrary] = None,
|
||||
library: MutableLibrary | None = None,
|
||||
*,
|
||||
pattern: Optional[Pattern] = None,
|
||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
||||
name: Optional[str] = None,
|
||||
pattern: Pattern | None = None,
|
||||
ports: str | Mapping[str, Port] | None = None,
|
||||
name: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
# TODO documentation for Builder() constructor
|
||||
@ -143,13 +142,13 @@ class Builder(PortList):
|
||||
@classmethod
|
||||
def interface(
|
||||
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_',
|
||||
out_prefix: str = '',
|
||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||
name: Optional[str] = None,
|
||||
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||
name: str | None = None,
|
||||
) -> 'Builder':
|
||||
"""
|
||||
Begin building a new device based on all or some of the ports in the
|
||||
@ -241,13 +240,13 @@ class Builder(PortList):
|
||||
|
||||
def plug(
|
||||
self: BB,
|
||||
other: Union[Abstract, str, NamedPattern],
|
||||
map_in: Dict[str, str],
|
||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||
other: Abstract | str | NamedPattern,
|
||||
map_in: dict[str, str],
|
||||
map_out: dict[str, str | None] | None = None,
|
||||
*,
|
||||
mirrored: Tuple[bool, bool] = (False, False),
|
||||
mirrored: tuple[bool, bool] = (False, False),
|
||||
inherit_name: bool = True,
|
||||
set_rotation: Optional[bool] = None,
|
||||
set_rotation: bool | None = None,
|
||||
) -> BB:
|
||||
"""
|
||||
Instantiate a device `library[name]` into the current device, connecting
|
||||
@ -272,9 +271,9 @@ class Builder(PortList):
|
||||
|
||||
Args:
|
||||
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.
|
||||
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`.
|
||||
mirrored: Enables mirroring `other` across the x or y axes prior
|
||||
to connecting any ports.
|
||||
@ -342,13 +341,13 @@ class Builder(PortList):
|
||||
|
||||
def place(
|
||||
self: BB,
|
||||
other: Union[Abstract, str, NamedPattern],
|
||||
other: Abstract | str | NamedPattern,
|
||||
*,
|
||||
offset: ArrayLike = (0, 0),
|
||||
rotation: float = 0,
|
||||
pivot: ArrayLike = (0, 0),
|
||||
mirrored: Tuple[bool, bool] = (False, False),
|
||||
port_map: Optional[Dict[str, Optional[str]]] = None,
|
||||
mirrored: tuple[bool, bool] = (False, False),
|
||||
port_map: dict[str, str | None] | None = None,
|
||||
skip_port_check: bool = False,
|
||||
) -> BB:
|
||||
"""
|
||||
@ -373,7 +372,7 @@ class Builder(PortList):
|
||||
Rotation is applied prior to translation (`offset`).
|
||||
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
||||
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
|
||||
`None`, which will delete those 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
|
||||
"""
|
||||
|
||||
tools: Dict[Optional[str], Tool]
|
||||
tools: dict[str | None, Tool]
|
||||
"""
|
||||
Tool objects are used to dynamically generate new single-use Devices
|
||||
(e.g wires or waveguides) to be plugged into this device.
|
||||
@ -564,10 +563,10 @@ class Pather(Builder):
|
||||
self,
|
||||
library: MutableLibrary,
|
||||
*,
|
||||
pattern: Optional[Pattern] = None,
|
||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
name: Optional[str] = None,
|
||||
pattern: Pattern | None = None,
|
||||
ports: str | Mapping[str, Port] | None = None,
|
||||
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||
name: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
# TODO documentation for Builder() constructor
|
||||
@ -609,9 +608,9 @@ class Pather(Builder):
|
||||
library: MutableLibrary,
|
||||
base_name: str,
|
||||
*,
|
||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
) -> Tuple['Pather', str]:
|
||||
ports: str | Mapping[str, Port] | None= None,
|
||||
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||
) -> tuple['Pather', str]:
|
||||
""" Name-and-make combination """
|
||||
pat = library.create(base_name)
|
||||
pather = Pather(library, pattern=pat, ports=ports, tools=tools)
|
||||
@ -622,8 +621,8 @@ class Pather(Builder):
|
||||
cls,
|
||||
builder: Builder,
|
||||
*,
|
||||
library: Optional[MutableLibrary] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
library: MutableLibrary | None = None,
|
||||
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||
) -> 'Pather':
|
||||
"""TODO from_builder docs"""
|
||||
library = library if library is not None else builder.library
|
||||
@ -635,14 +634,14 @@ class Pather(Builder):
|
||||
@classmethod
|
||||
def interface(
|
||||
cls,
|
||||
source: Union[PortList, Mapping[str, Port], str],
|
||||
source: PortList | Mapping[str, Port] | str,
|
||||
*,
|
||||
library: Optional[MutableLibrary] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
library: MutableLibrary | None = None,
|
||||
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||
in_prefix: str = 'in_',
|
||||
out_prefix: str = '',
|
||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||
name: Optional[str] = None,
|
||||
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||
name: str | None = None,
|
||||
) -> 'Pather':
|
||||
"""
|
||||
TODO doc pather.interface
|
||||
@ -676,7 +675,7 @@ class Pather(Builder):
|
||||
def retool(
|
||||
self: PP,
|
||||
tool: Tool,
|
||||
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
||||
keys: str | Sequence[str | None] | None = None,
|
||||
) -> PP:
|
||||
if keys is None or isinstance(keys, str):
|
||||
self.tools[keys] = tool
|
||||
@ -688,7 +687,7 @@ class Pather(Builder):
|
||||
def path(
|
||||
self: PP,
|
||||
portspec: str,
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
length: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -709,7 +708,7 @@ class Pather(Builder):
|
||||
def path_to(
|
||||
self: PP,
|
||||
portspec: str,
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
position: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -742,11 +741,11 @@ class Pather(Builder):
|
||||
|
||||
def mpath(
|
||||
self: PP,
|
||||
portspec: Union[str, Sequence[str]],
|
||||
ccw: Optional[SupportsBool],
|
||||
portspec: str | Sequence[str],
|
||||
ccw: SupportsBool | None,
|
||||
*,
|
||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
||||
set_rotation: Optional[float] = None,
|
||||
spacing: float | ArrayLike | None = None,
|
||||
set_rotation: float | None = None,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
force_container: bool = False,
|
||||
base_name: str = '_mpath',
|
||||
|
@ -1,5 +1,4 @@
|
||||
from typing import Dict, Tuple, Union, TypeVar, Optional, Sequence
|
||||
from typing import MutableMapping, Mapping
|
||||
from typing import TypeVar, Sequence, MutableMapping, Mapping
|
||||
import copy
|
||||
import logging
|
||||
|
||||
@ -30,7 +29,7 @@ class FlatBuilder(PortList):
|
||||
pattern: Pattern
|
||||
""" 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
|
||||
(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)"""
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, Port]:
|
||||
def ports(self) -> dict[str, Port]:
|
||||
return self.pattern.ports
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value: Dict[str, Port]) -> None:
|
||||
def ports(self, value: dict[str, Port]) -> None:
|
||||
self.pattern.ports = value
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
pattern: Optional[Pattern] = None,
|
||||
ports: Union[None, Mapping[str, Port]] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
pattern: Pattern | None = None,
|
||||
ports: Mapping[str, Port] | None = None,
|
||||
tools: Tool | MutableMapping[str | None, Tool] | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
# TODO documentation for FlatBuilder() constructor
|
||||
@ -79,12 +78,12 @@ class FlatBuilder(PortList):
|
||||
@classmethod
|
||||
def interface(
|
||||
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_',
|
||||
out_prefix: str = '',
|
||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||
port_map: dict[str, str] | Sequence[str] | None = None,
|
||||
) -> 'FlatBuilder':
|
||||
"""
|
||||
Begin building a new device based on all or some of the ports in the
|
||||
@ -176,12 +175,12 @@ class FlatBuilder(PortList):
|
||||
def plug(
|
||||
self: BB,
|
||||
other: Pattern,
|
||||
map_in: Dict[str, str],
|
||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||
map_in: dict[str, str],
|
||||
map_out: dict[str, str | None] | None = None,
|
||||
*,
|
||||
mirrored: Tuple[bool, bool] = (False, False),
|
||||
mirrored: tuple[bool, bool] = (False, False),
|
||||
inherit_name: bool = True,
|
||||
set_rotation: Optional[bool] = None,
|
||||
set_rotation: bool | None = None,
|
||||
) -> BB:
|
||||
"""
|
||||
Instantiate another pattern into the current device, connecting
|
||||
@ -206,9 +205,9 @@ class FlatBuilder(PortList):
|
||||
|
||||
Args:
|
||||
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.
|
||||
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`.
|
||||
mirrored: Enables mirroring `other` across the x or y axes prior
|
||||
to connecting any ports.
|
||||
@ -276,8 +275,8 @@ class FlatBuilder(PortList):
|
||||
offset: ArrayLike = (0, 0),
|
||||
rotation: float = 0,
|
||||
pivot: ArrayLike = (0, 0),
|
||||
mirrored: Tuple[bool, bool] = (False, False),
|
||||
port_map: Optional[Dict[str, Optional[str]]] = None,
|
||||
mirrored: tuple[bool, bool] = (False, False),
|
||||
port_map: dict[str, str | None] | None = None,
|
||||
skip_port_check: bool = False,
|
||||
) -> BB:
|
||||
"""
|
||||
@ -302,7 +301,7 @@ class FlatBuilder(PortList):
|
||||
Rotation is applied prior to translation (`offset`).
|
||||
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
||||
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
|
||||
`None`, which will delete those ports.
|
||||
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
||||
@ -420,7 +419,7 @@ class FlatBuilder(PortList):
|
||||
def retool(
|
||||
self: BB,
|
||||
tool: Tool,
|
||||
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
||||
keys: str | Sequence[str | None] | None = None,
|
||||
) -> BB:
|
||||
if keys is None or isinstance(keys, str):
|
||||
self.tools[keys] = tool
|
||||
@ -432,7 +431,7 @@ class FlatBuilder(PortList):
|
||||
def path(
|
||||
self: BB,
|
||||
portspec: str,
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
length: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -451,7 +450,7 @@ class FlatBuilder(PortList):
|
||||
def path_to(
|
||||
self: BB,
|
||||
portspec: str,
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
position: float,
|
||||
*,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
@ -484,11 +483,11 @@ class FlatBuilder(PortList):
|
||||
|
||||
def mpath(
|
||||
self: BB,
|
||||
portspec: Union[str, Sequence[str]],
|
||||
ccw: Optional[SupportsBool],
|
||||
portspec: str | Sequence[str],
|
||||
ccw: SupportsBool | None,
|
||||
*,
|
||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
||||
set_rotation: Optional[float] = None,
|
||||
spacing: float | ArrayLike | None = None,
|
||||
set_rotation: float | None = None,
|
||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||
force_container: bool = False,
|
||||
base_name: str = '_mpath',
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
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
|
||||
|
||||
@ -12,11 +12,11 @@ if TYPE_CHECKING:
|
||||
class Tool:
|
||||
def path(
|
||||
self,
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
length: float,
|
||||
*,
|
||||
in_ptype: Optional[str] = None,
|
||||
out_ptype: Optional[str] = None,
|
||||
in_ptype: str | None = None,
|
||||
out_ptype: str | None = None,
|
||||
port_names: Sequence[str] = ('A', 'B'),
|
||||
**kwargs,
|
||||
) -> 'Pattern':
|
||||
|
@ -1,5 +1,4 @@
|
||||
from typing import Dict, Mapping, Sequence, SupportsFloat
|
||||
from typing import Optional, Union, cast, TYPE_CHECKING
|
||||
from typing import Mapping, Sequence, SupportsFloat, cast, TYPE_CHECKING
|
||||
from pprint import pformat
|
||||
|
||||
import numpy
|
||||
@ -15,13 +14,13 @@ if TYPE_CHECKING:
|
||||
|
||||
def ell(
|
||||
ports: Mapping[str, 'Port'],
|
||||
ccw: Optional[SupportsBool],
|
||||
ccw: SupportsBool | None,
|
||||
bound_type: str,
|
||||
bound: Union[float, ArrayLike],
|
||||
bound: float | ArrayLike,
|
||||
*,
|
||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
||||
set_rotation: Optional[float] = None,
|
||||
) -> Dict[str, float]:
|
||||
spacing: float | ArrayLike | None = None,
|
||||
set_rotation: float | None = None,
|
||||
) -> dict[str, float]:
|
||||
"""
|
||||
Calculate extension for each port in order to build a 90-degree bend with the provided
|
||||
channel spacing:
|
||||
|
@ -6,8 +6,7 @@ Notes:
|
||||
* ezdxf sets creation time, write time, $VERSIONGUID, and $FINGERPRINTGUID
|
||||
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 cast, TextIO, IO
|
||||
from typing import Any, Callable, Mapping, cast, TextIO, IO
|
||||
import io
|
||||
import logging
|
||||
import pathlib
|
||||
@ -107,7 +106,7 @@ def write(
|
||||
def writefile(
|
||||
library: Mapping[str, Pattern],
|
||||
top_name: str,
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
@ -128,7 +127,7 @@ def writefile(
|
||||
|
||||
gz_stream: IO[bytes]
|
||||
with tmpfile(path) as base_stream:
|
||||
streams: Tuple[Any, ...] = (base_stream,)
|
||||
streams: tuple[Any, ...] = (base_stream,)
|
||||
if path.suffix == '.gz':
|
||||
gz_stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||
streams = (gz_stream,) + streams
|
||||
@ -145,10 +144,10 @@ def writefile(
|
||||
|
||||
|
||||
def readfile(
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**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.
|
||||
|
||||
@ -172,7 +171,7 @@ def readfile(
|
||||
|
||||
def read(
|
||||
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
|
||||
translated into `Pattern` objects; `LWPolyline`s are translated into polygons, and `Insert`s
|
||||
@ -204,7 +203,7 @@ def read(
|
||||
return mlib, library_info
|
||||
|
||||
|
||||
def _read_block(block) -> Tuple[str, Pattern]:
|
||||
def _read_block(block) -> tuple[str, Pattern]:
|
||||
name = block.name
|
||||
pat = Pattern()
|
||||
for element in block:
|
||||
@ -230,7 +229,7 @@ def _read_block(block) -> Tuple[str, Pattern]:
|
||||
if 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]):
|
||||
shape = Polygon(layer=layer, vertices=points[:-1, :2])
|
||||
else:
|
||||
@ -285,8 +284,8 @@ def _read_block(block) -> Tuple[str, Pattern]:
|
||||
|
||||
|
||||
def _mrefs_to_drefs(
|
||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||
refs: List[Ref],
|
||||
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||
refs: list[Ref],
|
||||
) -> None:
|
||||
for ref in refs:
|
||||
if ref.target is None:
|
||||
@ -332,8 +331,8 @@ def _mrefs_to_drefs(
|
||||
|
||||
|
||||
def _shapes_to_elements(
|
||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||
shapes: List[Shape],
|
||||
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||
shapes: list[Shape],
|
||||
polygonize_paths: bool = False,
|
||||
) -> None:
|
||||
# Add `LWPolyline`s for each shape.
|
||||
@ -353,8 +352,8 @@ def _shapes_to_elements(
|
||||
|
||||
|
||||
def _labels_to_texts(
|
||||
block: Union[ezdxf.layouts.BlockLayout, ezdxf.layouts.Modelspace],
|
||||
labels: List[Label],
|
||||
block: ezdxf.layouts.BlockLayout | ezdxf.layouts.Modelspace,
|
||||
labels: list[Label],
|
||||
) -> None:
|
||||
for label in labels:
|
||||
attribs = dict(layer=_mlayer2dxf(label.layer))
|
||||
|
@ -19,8 +19,7 @@ Notes:
|
||||
* 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)
|
||||
"""
|
||||
from typing import List, Dict, Tuple, Callable, Union, Iterable, Mapping
|
||||
from typing import IO, cast, Optional, Any
|
||||
from typing import Callable, Iterable, Mapping, IO, cast, Any
|
||||
import io
|
||||
import mmap
|
||||
import logging
|
||||
@ -114,7 +113,7 @@ def write(
|
||||
|
||||
# Now create a structure for each pattern, and add in any Boundary and SREF elements
|
||||
for name, pat in library.items():
|
||||
elements: List[klamath.elements.Element] = []
|
||||
elements: list[klamath.elements.Element] = []
|
||||
elements += _shapes_to_elements(pat.shapes)
|
||||
elements += _labels_to_texts(pat.labels)
|
||||
elements += _mrefs_to_grefs(pat.refs)
|
||||
@ -125,7 +124,7 @@ def write(
|
||||
|
||||
def writefile(
|
||||
library: Mapping[str, Pattern],
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
@ -143,7 +142,7 @@ def writefile(
|
||||
path = pathlib.Path(filename)
|
||||
|
||||
with tmpfile(path) as base_stream:
|
||||
streams: Tuple[Any, ...] = (base_stream,)
|
||||
streams: tuple[Any, ...] = (base_stream,)
|
||||
if path.suffix == '.gz':
|
||||
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||
streams = (stream,) + streams
|
||||
@ -158,10 +157,10 @@ def writefile(
|
||||
|
||||
|
||||
def readfile(
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
||||
) -> tuple[WrapLibrary, dict[str, Any]]:
|
||||
"""
|
||||
Wrapper for `read()` that takes a filename or path instead of a stream.
|
||||
|
||||
@ -186,7 +185,7 @@ def readfile(
|
||||
def read(
|
||||
stream: IO[bytes],
|
||||
raw_mode: bool = True,
|
||||
) -> Tuple[WrapLibrary, Dict[str, Any]]:
|
||||
) -> tuple[WrapLibrary, dict[str, Any]]:
|
||||
"""
|
||||
# TODO check GDSII file for cycles!
|
||||
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.
|
||||
|
||||
Returns:
|
||||
- Dict of pattern_name:Patterns generated from GDSII structures
|
||||
- Dict of GDSII library info
|
||||
- dict of pattern_name:Patterns generated from GDSII structures
|
||||
- dict of GDSII library info
|
||||
"""
|
||||
library_info = _read_header(stream)
|
||||
|
||||
@ -220,7 +219,7 @@ def read(
|
||||
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.
|
||||
"""
|
||||
@ -272,7 +271,7 @@ def read_elements(
|
||||
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"""
|
||||
if isinstance(mlayer, int):
|
||||
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 = []
|
||||
for ref in refs:
|
||||
if ref.target is None:
|
||||
@ -402,11 +401,11 @@ def _mrefs_to_grefs(refs: List[Ref]) -> List[klamath.library.Reference]:
|
||||
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()}
|
||||
|
||||
|
||||
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
|
||||
props = {}
|
||||
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(
|
||||
shapes: List[Shape],
|
||||
shapes: list[Shape],
|
||||
polygonize_paths: bool = False,
|
||||
) -> List[klamath.elements.Element]:
|
||||
elements: List[klamath.elements.Element] = []
|
||||
) -> list[klamath.elements.Element]:
|
||||
elements: list[klamath.elements.Element] = []
|
||||
# Add a Boundary element for each shape, and Path elements if necessary
|
||||
for shape in shapes:
|
||||
if shape.repetition is not None:
|
||||
@ -446,7 +445,7 @@ def _shapes_to_elements(
|
||||
width = rint_cast(shape.width)
|
||||
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:
|
||||
extension = tuple(shape.cap_extensions) # type: ignore
|
||||
else:
|
||||
@ -486,7 +485,7 @@ def _shapes_to_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 = []
|
||||
for label in labels:
|
||||
properties = _annotations_to_properties(label.annotations, 128)
|
||||
@ -512,8 +511,8 @@ def load_library(
|
||||
stream: IO[bytes],
|
||||
*,
|
||||
full_load: bool = False,
|
||||
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
||||
postprocess: Callable[[Library, str, Pattern], Pattern] | None = None
|
||||
) -> tuple[LazyLibrary, dict[str, Any]]:
|
||||
"""
|
||||
Scan a GDSII stream to determine what structures are present, and create
|
||||
a library from them. This enables deferred reading of structures
|
||||
@ -568,12 +567,12 @@ def load_library(
|
||||
|
||||
|
||||
def load_libraryfile(
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*,
|
||||
use_mmap: bool = True,
|
||||
full_load: bool = False,
|
||||
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
||||
postprocess: Callable[[Library, str, Pattern], Pattern] | None = None
|
||||
) -> tuple[LazyLibrary, dict[str, Any]]:
|
||||
"""
|
||||
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:
|
||||
* 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 IO, Mapping, Optional, cast, Sequence
|
||||
from typing import Any, Callable, Iterable, IO, Mapping, cast, Sequence
|
||||
import logging
|
||||
import pathlib
|
||||
import gzip
|
||||
@ -57,9 +56,9 @@ def rint_cast(val: ArrayLike) -> NDArray[numpy.int64]:
|
||||
def build(
|
||||
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
||||
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:
|
||||
"""
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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).
|
||||
@ -127,7 +126,7 @@ def build(
|
||||
)
|
||||
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
|
||||
layer_num = layer_map[mlayer] if isinstance(mlayer, str) else mlayer
|
||||
return _mlayer2oas(layer_num)
|
||||
@ -170,7 +169,7 @@ def write(
|
||||
|
||||
def writefile(
|
||||
library: Mapping[str, Pattern], # NOTE: Pattern here should be treated as immutable!
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
@ -188,7 +187,7 @@ def writefile(
|
||||
path = pathlib.Path(filename)
|
||||
|
||||
with tmpfile(path) as base_stream:
|
||||
streams: Tuple[Any, ...] = (base_stream,)
|
||||
streams: tuple[Any, ...] = (base_stream,)
|
||||
if path.suffix == '.gz':
|
||||
stream = cast(IO[bytes], gzip.GzipFile(filename='', mtime=0, fileobj=base_stream, mode='wb'))
|
||||
streams += (stream,)
|
||||
@ -203,10 +202,10 @@ def writefile(
|
||||
|
||||
|
||||
def readfile(
|
||||
filename: Union[str, pathlib.Path],
|
||||
filename: str | pathlib.Path,
|
||||
*args,
|
||||
**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.
|
||||
|
||||
@ -230,7 +229,7 @@ def readfile(
|
||||
|
||||
def read(
|
||||
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
|
||||
translated into Pattern objects; Polygons are translated into polygons, and Placements
|
||||
@ -245,13 +244,13 @@ def read(
|
||||
stream: Stream to read from.
|
||||
|
||||
Returns:
|
||||
- Dict of `pattern_name`:`Pattern`s generated from OASIS cells
|
||||
- Dict of OASIS library info
|
||||
- dict of `pattern_name`:`Pattern`s generated from OASIS cells
|
||||
- dict of OASIS library info
|
||||
"""
|
||||
|
||||
lib = fatamorgana.OasisLayout.read(stream)
|
||||
|
||||
library_info: Dict[str, Any] = {
|
||||
library_info: dict[str, Any] = {
|
||||
'units_per_micrometer': lib.unit,
|
||||
'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
|
||||
cap = cap_start
|
||||
|
||||
path_args: Dict[str, Any] = {}
|
||||
path_args: dict[str, Any] = {}
|
||||
if cap == Path.Cap.SquareCustom:
|
||||
path_args['cap_extensions'] = numpy.array((
|
||||
element.get_extension_start()[1],
|
||||
@ -468,7 +467,7 @@ def read(
|
||||
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"""
|
||||
if isinstance(mlayer, int):
|
||||
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
|
||||
|
||||
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)
|
||||
if placement.angle is None:
|
||||
@ -514,8 +513,8 @@ def _placement_to_ref(placement: fatrec.Placement, lib: fatamorgana.OasisLayout)
|
||||
|
||||
|
||||
def _refs_to_placements(
|
||||
refs: List[Ref],
|
||||
) -> List[fatrec.Placement]:
|
||||
refs: list[Ref],
|
||||
) -> list[fatrec.Placement]:
|
||||
placements = []
|
||||
for ref in refs:
|
||||
if ref.target is None:
|
||||
@ -543,11 +542,11 @@ def _refs_to_placements(
|
||||
|
||||
|
||||
def _shapes_to_elements(
|
||||
shapes: List[Shape],
|
||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
||||
) -> List[Union[fatrec.Polygon, fatrec.Path, fatrec.Circle]]:
|
||||
shapes: list[Shape],
|
||||
layer2oas: Callable[[layer_t], tuple[int, int]],
|
||||
) -> list[fatrec.Polygon | fatrec.Path | fatrec.Circle]:
|
||||
# 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:
|
||||
layer, datatype = layer2oas(shape.layer)
|
||||
repetition, rep_offset = repetition_masq2fata(shape.repetition)
|
||||
@ -594,7 +593,7 @@ def _shapes_to_elements(
|
||||
datatype=datatype,
|
||||
x=xy[0],
|
||||
y=xy[1],
|
||||
point_list=cast(List[List[int]], points),
|
||||
point_list=cast(list[list[int]], points),
|
||||
properties=properties,
|
||||
repetition=repetition,
|
||||
))
|
||||
@ -602,9 +601,9 @@ def _shapes_to_elements(
|
||||
|
||||
|
||||
def _labels_to_texts(
|
||||
labels: List[Label],
|
||||
layer2oas: Callable[[layer_t], Tuple[int, int]],
|
||||
) -> List[fatrec.Text]:
|
||||
labels: list[Label],
|
||||
layer2oas: Callable[[layer_t], tuple[int, int]],
|
||||
) -> list[fatrec.Text]:
|
||||
texts = []
|
||||
for label in labels:
|
||||
layer, datatype = layer2oas(label.layer)
|
||||
@ -624,9 +623,9 @@ def _labels_to_texts(
|
||||
|
||||
|
||||
def repetition_fata2masq(
|
||||
rep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None],
|
||||
) -> Optional[Repetition]:
|
||||
mrep: Optional[Repetition]
|
||||
rep: fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None,
|
||||
) -> Repetition | None:
|
||||
mrep: Repetition | None
|
||||
if isinstance(rep, fatamorgana.GridRepetition):
|
||||
mrep = Grid(a_vector=rep.a_vector,
|
||||
b_vector=rep.b_vector,
|
||||
@ -645,22 +644,22 @@ def repetition_fata2masq(
|
||||
|
||||
|
||||
def repetition_masq2fata(
|
||||
rep: Optional[Repetition],
|
||||
) -> Tuple[Union[fatamorgana.GridRepetition,
|
||||
fatamorgana.ArbitraryRepetition,
|
||||
None],
|
||||
Tuple[int, int]]:
|
||||
frep: Union[fatamorgana.GridRepetition, fatamorgana.ArbitraryRepetition, None]
|
||||
rep: Repetition | None,
|
||||
) -> tuple[
|
||||
fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None,
|
||||
tuple[int, int]
|
||||
]:
|
||||
frep: fatamorgana.GridRepetition | fatamorgana.ArbitraryRepetition | None
|
||||
if isinstance(rep, Grid):
|
||||
a_vector = rint_cast(rep.a_vector)
|
||||
b_vector = rint_cast(rep.b_vector) if rep.b_vector is not None else None
|
||||
a_count = rint_cast(rep.a_count)
|
||||
b_count = rint_cast(rep.b_count) if rep.b_count is not None else None
|
||||
frep = fatamorgana.GridRepetition(
|
||||
a_vector=cast(List[int], a_vector),
|
||||
b_vector=cast(Optional[List[int]], b_vector),
|
||||
a_vector=cast(list[int], a_vector),
|
||||
b_vector=cast(list[int] | None, b_vector),
|
||||
a_count=cast(int, a_count),
|
||||
b_count=cast(Optional[int], b_count),
|
||||
b_count=cast(int | None, b_count),
|
||||
)
|
||||
offset = (0, 0)
|
||||
elif isinstance(rep, Arbitrary):
|
||||
@ -675,7 +674,7 @@ def repetition_masq2fata(
|
||||
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?
|
||||
properties = []
|
||||
for key, values in annotations.items():
|
||||
@ -686,9 +685,9 @@ def annotations_to_properties(annotations: annotations_t) -> List[fatrec.Propert
|
||||
|
||||
|
||||
def properties_to_annotations(
|
||||
properties: List[fatrec.Property],
|
||||
propnames: Dict[int, NString],
|
||||
propstrings: Dict[int, AString],
|
||||
properties: list[fatrec.Property],
|
||||
propnames: dict[int, NString],
|
||||
propstrings: dict[int, AString],
|
||||
) -> annotations_t:
|
||||
annotations = {}
|
||||
for proprec in properties:
|
||||
@ -697,7 +696,7 @@ def properties_to_annotations(
|
||||
key = propnames[proprec.name].string
|
||||
else:
|
||||
key = proprec.name.string
|
||||
values: List[Union[str, float, int]] = []
|
||||
values: list[str | float | int] = []
|
||||
|
||||
assert proprec.values is not None
|
||||
for value in proprec.values:
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Helper functions for file reading and writing
|
||||
"""
|
||||
from typing import Union, IO, Iterator
|
||||
from typing import IO, Iterator
|
||||
import re
|
||||
import pathlib
|
||||
import logging
|
||||
@ -62,7 +62,7 @@ def is_gzipped(path: pathlib.Path) -> bool:
|
||||
|
||||
|
||||
@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,
|
||||
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)
|
||||
finally:
|
||||
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 numpy
|
||||
@ -44,8 +44,8 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
|
||||
*,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
) -> None:
|
||||
self.string = string
|
||||
self.offset = numpy.array(offset, dtype=float, copy=True)
|
||||
@ -61,7 +61,7 @@ class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl,
|
||||
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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
|
@ -5,8 +5,8 @@ Library classes for managing unique name->pattern mappings and
|
||||
# TODO documentn all library classes
|
||||
# TODO toplevel documentation of library, classes, and abstracts
|
||||
"""
|
||||
from typing import List, Dict, Callable, TypeVar, Type, TYPE_CHECKING, cast
|
||||
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
|
||||
from typing import Callable, TypeVar, Type, TYPE_CHECKING, cast
|
||||
from typing import Iterator, Mapping, MutableMapping, Sequence
|
||||
import logging
|
||||
import base64
|
||||
import struct
|
||||
@ -75,8 +75,8 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
def dangling_refs(
|
||||
self,
|
||||
tops: Union[None, str, Sequence[str]] = None,
|
||||
) -> Set[Optional[str]]:
|
||||
tops: str | Sequence[str] | None = None,
|
||||
) -> set[str | None]:
|
||||
"""
|
||||
Get the set of all pattern names not present in the library but referenced
|
||||
by `tops`, recursively traversing any refs.
|
||||
@ -99,9 +99,9 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
def referenced_patterns(
|
||||
self,
|
||||
tops: Union[None, str, Sequence[str]] = None,
|
||||
skip: Optional[Set[Optional[str]]] = None,
|
||||
) -> Set[Optional[str]]:
|
||||
tops: str | Sequence[str] | None = None,
|
||||
skip: set[str | None] | None = None,
|
||||
) -> set[str | None]:
|
||||
"""
|
||||
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(
|
||||
self,
|
||||
tops: Union[str, Sequence[str]],
|
||||
tops: str | Sequence[str],
|
||||
) -> 'Library':
|
||||
"""
|
||||
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):
|
||||
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)
|
||||
|
||||
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(
|
||||
self: L,
|
||||
poly_num_points: Optional[int] = None,
|
||||
poly_max_arclen: Optional[float] = None,
|
||||
num_vertices: int | None = None,
|
||||
max_arclen: float | None = None,
|
||||
) -> L:
|
||||
"""
|
||||
Calls `.polygonize(...)` on each pattern in this library.
|
||||
Arguments are passed on to `shape.to_polygons(...)`.
|
||||
|
||||
Args:
|
||||
poly_num_points: 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'
|
||||
num_vertices: Number of points to use for each polygon. Can be overridden by
|
||||
`max_arclen` if that results in more points. Optional, defaults to shapes'
|
||||
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.
|
||||
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
for pat in self.values():
|
||||
pat.polygonize(poly_num_points, poly_max_arclen)
|
||||
pat.polygonize(num_vertices, max_arclen)
|
||||
return self
|
||||
|
||||
def manhattanize(
|
||||
@ -206,9 +206,9 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
def flatten(
|
||||
self,
|
||||
tops: Union[str, Sequence[str]],
|
||||
tops: str | Sequence[str],
|
||||
flatten_ports: bool = False, # TODO document
|
||||
) -> Dict[str, 'Pattern']:
|
||||
) -> dict[str, 'Pattern']:
|
||||
"""
|
||||
Removes all refs and adds equivalent shapes.
|
||||
Also flattens all referenced patterns.
|
||||
@ -222,7 +222,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
if isinstance(tops, str):
|
||||
tops = (tops,)
|
||||
|
||||
flattened: Dict[str, Optional['Pattern']] = {}
|
||||
flattened: dict[str, 'Pattern' | None] = {}
|
||||
|
||||
def flatten_single(name) -> None:
|
||||
flattened[name] = None
|
||||
@ -261,7 +261,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
name: str = '__',
|
||||
sanitize: bool = True,
|
||||
max_length: int = 32,
|
||||
quiet: Optional[bool] = None,
|
||||
quiet: bool | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
Find a unique name for the pattern.
|
||||
@ -308,7 +308,7 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
|
||||
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.
|
||||
|
||||
@ -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.
|
||||
"""
|
||||
names = set(self.keys())
|
||||
not_toplevel: Set[Optional[str]] = set()
|
||||
not_toplevel: set[str | None] = set()
|
||||
for name in names:
|
||||
not_toplevel |= set(sp.target for sp in self[name].refs)
|
||||
|
||||
@ -326,12 +326,12 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
def dfs(
|
||||
self: L,
|
||||
pattern: 'Pattern',
|
||||
visit_before: Optional[visitor_function_t] = None,
|
||||
visit_after: Optional[visitor_function_t] = None,
|
||||
visit_before: visitor_function_t | None = None,
|
||||
visit_after: visitor_function_t | None = None,
|
||||
*,
|
||||
hierarchy: Tuple[Optional[str], ...] = (None,),
|
||||
transform: Union[ArrayLike, bool, None] = False,
|
||||
memo: Optional[Dict] = None,
|
||||
hierarchy: tuple[str | None, ...] = (None,),
|
||||
transform: ArrayLike | bool | None = False,
|
||||
memo: dict | None = None,
|
||||
) -> L:
|
||||
"""
|
||||
Convenience function.
|
||||
@ -434,14 +434,14 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
#def __getitem__(self, key: str) -> 'Pattern':
|
||||
#def __iter__(self) -> Iterator[str]:
|
||||
#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:
|
||||
|
||||
@abstractmethod
|
||||
def __setitem__(
|
||||
self,
|
||||
key: str,
|
||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
||||
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@ -510,31 +510,11 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
self[name] = 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(
|
||||
self,
|
||||
other: Mapping[str, 'Pattern'],
|
||||
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
||||
) -> Dict[str, str]:
|
||||
) -> dict[str, str]:
|
||||
"""
|
||||
Add keys from another library into this one.
|
||||
|
||||
@ -581,7 +561,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
def add_tree(
|
||||
self,
|
||||
tree: 'Tree',
|
||||
name: Optional[str] = None,
|
||||
name: str | None = None,
|
||||
rename_theirs: Callable[['Library', str], str] = _rename_patterns,
|
||||
) -> str:
|
||||
"""
|
||||
@ -623,8 +603,8 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
def dedup(
|
||||
self: ML,
|
||||
norm_value: int = int(1e6),
|
||||
exclude_types: Tuple[Type] = (Polygon,),
|
||||
label2name: Optional[Callable[[Tuple], str]] = None,
|
||||
exclude_types: tuple[Type] = (Polygon,),
|
||||
label2name: Callable[[tuple], str] | None = None,
|
||||
threshold: int = 2,
|
||||
) -> ML:
|
||||
"""
|
||||
@ -664,7 +644,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
return 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 = {}
|
||||
|
||||
# ## First pass ##
|
||||
@ -692,7 +672,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
# are to be replaced.
|
||||
# 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):
|
||||
if any(isinstance(shape, t) for t in exclude_types):
|
||||
continue
|
||||
@ -727,7 +707,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
|
||||
def wrap_repeated_shapes(
|
||||
self: ML,
|
||||
name_func: Optional[Callable[['Pattern', Union[Shape, Label]], str]] = None,
|
||||
name_func: Callable[['Pattern', Shape | Label], str] | None = None,
|
||||
) -> ML:
|
||||
"""
|
||||
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(
|
||||
self: ML,
|
||||
tops: Union[str, Sequence[str]],
|
||||
tops: str | Sequence[str],
|
||||
) -> ML:
|
||||
"""
|
||||
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):
|
||||
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)
|
||||
|
||||
new = type(self)()
|
||||
@ -802,7 +782,7 @@ class MutableLibrary(Library, MutableMapping[str, 'Pattern'], metaclass=ABCMeta)
|
||||
def prune_empty(
|
||||
self,
|
||||
repeat: bool = True,
|
||||
) -> Set[str]:
|
||||
) -> set[str]:
|
||||
# TODO doc prune_empty
|
||||
trimmed = set()
|
||||
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
||||
@ -859,7 +839,7 @@ class WrapLibrary(MutableLibrary):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mapping: Optional[MutableMapping[str, 'Pattern']] = None,
|
||||
mapping: MutableMapping[str, 'Pattern'] | None = None,
|
||||
) -> None:
|
||||
if mapping is None:
|
||||
self.mapping = {}
|
||||
@ -881,7 +861,7 @@ class WrapLibrary(MutableLibrary):
|
||||
def __setitem__(
|
||||
self,
|
||||
key: str,
|
||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
||||
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||
) -> None:
|
||||
if key in self.mapping:
|
||||
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
|
||||
functions which generate or load the relevant `Pattern` object as-needed.
|
||||
"""
|
||||
dict: Dict[str, Callable[[], 'Pattern']]
|
||||
cache: Dict[str, 'Pattern']
|
||||
_lookups_in_progress: Set[str]
|
||||
mapping: dict[str, Callable[[], 'Pattern']]
|
||||
cache: dict[str, 'Pattern']
|
||||
_lookups_in_progress: set[str]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.dict = {}
|
||||
self.mapping = {}
|
||||
self.cache = {}
|
||||
self._lookups_in_progress = set()
|
||||
|
||||
def __setitem__(
|
||||
self,
|
||||
key: str,
|
||||
value: Union['Pattern', Callable[[], 'Pattern']],
|
||||
value: 'Pattern' | Callable[[], 'Pattern'],
|
||||
) -> None:
|
||||
if key in self.dict:
|
||||
if key in self.mapping:
|
||||
raise LibraryError(f'"{key}" already exists in the library. Overwriting is not allowed!')
|
||||
|
||||
if callable(value):
|
||||
@ -931,12 +911,12 @@ class LazyLibrary(MutableLibrary):
|
||||
else:
|
||||
value_func = lambda: cast('Pattern', value) # noqa: E731
|
||||
|
||||
self.dict[key] = value_func
|
||||
self.mapping[key] = value_func
|
||||
if key in self.cache:
|
||||
del self.cache[key]
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self.dict[key]
|
||||
del self.mapping[key]
|
||||
if key in self.cache:
|
||||
del self.cache[key]
|
||||
|
||||
@ -954,24 +934,24 @@ class LazyLibrary(MutableLibrary):
|
||||
)
|
||||
|
||||
self._lookups_in_progress.add(key)
|
||||
func = self.dict[key]
|
||||
func = self.mapping[key]
|
||||
pat = func()
|
||||
self._lookups_in_progress.remove(key)
|
||||
self.cache[key] = pat
|
||||
return pat
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter(self.dict)
|
||||
return iter(self.mapping)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.dict)
|
||||
return len(self.mapping)
|
||||
|
||||
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:
|
||||
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:
|
||||
self.cache[key_self] = other.cache[key_other]
|
||||
else:
|
||||
@ -999,7 +979,7 @@ class LazyLibrary(MutableLibrary):
|
||||
Returns:
|
||||
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:
|
||||
self.cache[new_name] = self.cache[old_name]
|
||||
del self[old_name]
|
||||
@ -1034,11 +1014,11 @@ class LazyLibrary(MutableLibrary):
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
for key in self.dict:
|
||||
for key in self.mapping:
|
||||
_ = self[key] # want to trigger our own __getitem__
|
||||
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)')
|
||||
|
||||
|
||||
@ -1068,14 +1048,14 @@ class Tree(MutableLibrary):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
top: Union[str, 'NamedPattern'],
|
||||
library: Optional[MutableLibrary] = None
|
||||
top: str | 'NamedPattern',
|
||||
library: MutableLibrary | None = None
|
||||
) -> None:
|
||||
self.top = top if isinstance(top, str) else top.name
|
||||
self.library = library if library is not None else WrapLibrary()
|
||||
|
||||
@classmethod
|
||||
def mk(cls, top: str) -> Tuple['Tree', 'Pattern']:
|
||||
def mk(cls, top: str) -> tuple['Tree', 'Pattern']:
|
||||
from .pattern import Pattern
|
||||
tree = cls(top=top)
|
||||
pat = Pattern()
|
||||
@ -1091,7 +1071,7 @@ class Tree(MutableLibrary):
|
||||
def __len__(self) -> int:
|
||||
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
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
|
@ -2,8 +2,7 @@
|
||||
Base object representing a lithography mask.
|
||||
"""
|
||||
|
||||
from typing import List, Callable, Dict, Union, Set, Sequence, Optional, cast
|
||||
from typing import Mapping, TypeVar, Any
|
||||
from typing import Callable, Sequence, cast, Mapping, TypeVar, Any
|
||||
import copy
|
||||
from itertools import chain
|
||||
|
||||
@ -35,29 +34,29 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
'_offset', '_annotations',
|
||||
)
|
||||
|
||||
shapes: List[Shape]
|
||||
shapes: list[Shape]
|
||||
""" List of all shapes in this Pattern.
|
||||
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. """
|
||||
|
||||
refs: List[Ref]
|
||||
refs: list[Ref]
|
||||
""" List of all references to other patterns (`Ref`s) in this `Pattern`.
|
||||
Multiple objects in this list may reference the same Pattern 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"""
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, Port]:
|
||||
def ports(self) -> dict[str, Port]:
|
||||
return self._ports
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value: Dict[str, Port]) -> None:
|
||||
def ports(self, value: dict[str, Port]) -> None:
|
||||
self._ports = value
|
||||
|
||||
def __init__(
|
||||
@ -66,8 +65,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
shapes: Sequence[Shape] = (),
|
||||
labels: Sequence[Label] = (),
|
||||
refs: Sequence[Ref] = (),
|
||||
annotations: Optional[annotations_t] = None,
|
||||
ports: Optional[Mapping[str, 'Port']] = None
|
||||
annotations: annotations_t | None = None,
|
||||
ports: Mapping[str, 'Port'] | None = None
|
||||
) -> None:
|
||||
"""
|
||||
Basic init; arguments get assigned to member variables.
|
||||
@ -118,7 +117,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
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
|
||||
new = Pattern(
|
||||
shapes=copy.deepcopy(self.shapes, memo),
|
||||
@ -158,11 +157,11 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
|
||||
def subset(
|
||||
self,
|
||||
shapes: Optional[Callable[[Shape], bool]] = None,
|
||||
labels: Optional[Callable[[Label], bool]] = None,
|
||||
refs: Optional[Callable[[Ref], bool]] = None,
|
||||
annotations: Optional[Callable[[str, List[Union[int, float, str]]], bool]] = None,
|
||||
ports: Optional[Callable[[str, Port], bool]] = None,
|
||||
shapes: Callable[[Shape], bool] | None = None,
|
||||
labels: Callable[[Label], bool] | None = None,
|
||||
refs: Callable[[Ref], bool] | None = None,
|
||||
annotations: Callable[[str, list[int | float | str]], bool] | None = None,
|
||||
ports: Callable[[str, Port], bool] | None = None,
|
||||
default_keep: bool = False
|
||||
) -> 'Pattern':
|
||||
"""
|
||||
@ -214,8 +213,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
|
||||
def polygonize(
|
||||
self: P,
|
||||
num_points: Optional[int] = None,
|
||||
max_arclen: Optional[float] = None,
|
||||
num_points: int | None = None,
|
||||
max_arclen: float | None = None,
|
||||
) -> P:
|
||||
"""
|
||||
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)))
|
||||
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.
|
||||
|
||||
@ -274,7 +273,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
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
|
||||
|
||||
def referenced_patterns(self) -> Set[Optional[str]]:
|
||||
def referenced_patterns(self) -> set[str | None]:
|
||||
"""
|
||||
Get all pattern namers referenced by this pattern. Non-recursive.
|
||||
|
||||
@ -285,9 +284,9 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
|
||||
def get_bounds(
|
||||
self,
|
||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
||||
library: Mapping[str, 'Pattern'] | None = None,
|
||||
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
|
||||
extent of the Pattern's contents in each dimension.
|
||||
@ -330,7 +329,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
|
||||
def get_bounds_nonempty(
|
||||
self,
|
||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
||||
library: Mapping[str, 'Pattern'] | None = None,
|
||||
recurse: bool = True,
|
||||
) -> NDArray[numpy.float64]:
|
||||
"""
|
||||
@ -574,10 +573,10 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
Returns:
|
||||
self
|
||||
"""
|
||||
flattened: Dict[Optional[str], Optional[P]] = {}
|
||||
flattened: dict[str | None, P | None] = {}
|
||||
|
||||
# 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:
|
||||
pat = self
|
||||
else:
|
||||
@ -611,7 +610,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
|
||||
def visualize(
|
||||
self: P,
|
||||
library: Optional[Mapping[str, P]] = None,
|
||||
library: Mapping[str, P] | None = None,
|
||||
offset: ArrayLike = (0., 0.),
|
||||
line_color: str = 'k',
|
||||
fill_color: str = 'none',
|
||||
@ -710,7 +709,7 @@ class NamedPattern(Pattern):
|
||||
def __copy__(self) -> Pattern:
|
||||
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)
|
||||
|
||||
def as_pattern(self) -> Pattern:
|
||||
|
@ -1,5 +1,4 @@
|
||||
from typing import Dict, Iterable, List, Tuple, KeysView, ValuesView
|
||||
from typing import overload, Union, Optional, TypeVar
|
||||
from typing import Iterable, KeysView, ValuesView, overload, TypeVar
|
||||
import warnings
|
||||
import traceback
|
||||
import logging
|
||||
@ -43,7 +42,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
||||
'_offset',
|
||||
)
|
||||
|
||||
_rotation: Optional[float]
|
||||
_rotation: float | None
|
||||
""" radians counterclockwise from +x, pointing into device body.
|
||||
Can be `None` to signify undirected port """
|
||||
|
||||
@ -53,7 +52,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
||||
def __init__(
|
||||
self,
|
||||
offset: ArrayLike,
|
||||
rotation: Optional[float],
|
||||
rotation: float | None,
|
||||
ptype: str = 'unk',
|
||||
) -> None:
|
||||
self.offset = offset
|
||||
@ -61,7 +60,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
||||
self.ptype = ptype
|
||||
|
||||
@property
|
||||
def rotation(self) -> Optional[float]:
|
||||
def rotation(self) -> float | None:
|
||||
""" Rotation, radians counterclockwise, pointing into device body. Can be None. """
|
||||
return self._rotation
|
||||
|
||||
@ -94,7 +93,7 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
|
||||
self.rotation += rotation
|
||||
return self
|
||||
|
||||
def set_rotation(self: P, rotation: Optional[float]) -> P:
|
||||
def set_rotation(self: P, rotation: float | None) -> P:
|
||||
self.rotation = rotation
|
||||
return self
|
||||
|
||||
@ -111,13 +110,13 @@ class PortList(metaclass=ABCMeta):
|
||||
|
||||
@property
|
||||
@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"""
|
||||
pass
|
||||
|
||||
@ports.setter
|
||||
@abstractmethod
|
||||
def ports(self, value: Dict[str, Port]) -> None:
|
||||
def ports(self, value: dict[str, Port]) -> None:
|
||||
pass
|
||||
|
||||
@overload
|
||||
@ -125,10 +124,10 @@ class PortList(metaclass=ABCMeta):
|
||||
pass
|
||||
|
||||
@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
|
||||
|
||||
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:
|
||||
- `pattern['A'] == Port((0, 0), 0)`
|
||||
@ -150,7 +149,7 @@ class PortList(metaclass=ABCMeta):
|
||||
|
||||
def rename_ports(
|
||||
self: PL,
|
||||
mapping: Dict[str, Optional[str]],
|
||||
mapping: dict[str, str | None],
|
||||
overwrite: bool = False,
|
||||
) -> PL:
|
||||
"""
|
||||
@ -158,7 +157,7 @@ class PortList(metaclass=ABCMeta):
|
||||
Ports can be explicitly deleted by mapping them to `None`.
|
||||
|
||||
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
|
||||
overwrite an existing non-renamed port to implicitly delete it if
|
||||
`overwrite` is set to `True`.
|
||||
@ -183,7 +182,7 @@ class PortList(metaclass=ABCMeta):
|
||||
self: PL,
|
||||
offset: ArrayLike = (0, 0),
|
||||
rotation: float = 0.0,
|
||||
names: Tuple[str, str] = ('A', 'B'),
|
||||
names: tuple[str, str] = ('A', 'B'),
|
||||
ptype: str = 'unk',
|
||||
) -> PL:
|
||||
"""
|
||||
@ -210,8 +209,8 @@ class PortList(metaclass=ABCMeta):
|
||||
def check_ports(
|
||||
self: PL,
|
||||
other_names: Iterable[str],
|
||||
map_in: Optional[Dict[str, str]] = None,
|
||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
||||
map_in: dict[str, str] | None = None,
|
||||
map_out: dict[str, str | None] | None = None,
|
||||
) -> PL:
|
||||
"""
|
||||
Given the provided port mappings, check that:
|
||||
@ -221,9 +220,9 @@ class PortList(metaclass=ABCMeta):
|
||||
Args:
|
||||
other_names: List of port names being considered for inclusion into
|
||||
`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.
|
||||
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.
|
||||
|
||||
Returns:
|
||||
@ -279,18 +278,18 @@ class PortList(metaclass=ABCMeta):
|
||||
def find_transform(
|
||||
self: PL,
|
||||
other: PL2,
|
||||
map_in: Dict[str, str],
|
||||
map_in: dict[str, str],
|
||||
*,
|
||||
mirrored: Tuple[bool, bool] = (False, False),
|
||||
set_rotation: Optional[bool] = None,
|
||||
) -> Tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||
mirrored: tuple[bool, bool] = (False, False),
|
||||
set_rotation: bool | None = None,
|
||||
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||
"""
|
||||
Given a device `other` and a mapping `map_in` specifying port connections,
|
||||
find the transform which will correctly align the specified ports.
|
||||
|
||||
Args:
|
||||
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.
|
||||
mirrored: Mirrors `other` across the x or y axes prior to
|
||||
connecting any ports.
|
||||
|
@ -4,7 +4,7 @@
|
||||
"""
|
||||
#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 numpy
|
||||
@ -41,7 +41,7 @@ class Ref(
|
||||
'_offset', '_rotation', 'scale', '_repetition', '_annotations',
|
||||
)
|
||||
|
||||
_target: Optional[str]
|
||||
_target: str | None
|
||||
""" The name of the `Pattern` being instanced """
|
||||
|
||||
_mirrored: NDArray[numpy.bool_]
|
||||
@ -49,14 +49,14 @@ class Ref(
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target: Union[None, str, 'NamedPattern'],
|
||||
target: str | 'NamedPattern' | None,
|
||||
*,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
rotation: float = 0.0,
|
||||
mirrored: Optional[Sequence[bool]] = None,
|
||||
mirrored: Sequence[bool] | None = None,
|
||||
scale: float = 1.0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Args:
|
||||
@ -91,7 +91,7 @@ class Ref(
|
||||
)
|
||||
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
|
||||
new = copy.copy(self)
|
||||
new.repetition = copy.deepcopy(self.repetition, memo)
|
||||
@ -100,11 +100,11 @@ class Ref(
|
||||
|
||||
# target property
|
||||
@property
|
||||
def target(self) -> Optional[str]:
|
||||
def target(self) -> str | None:
|
||||
return self._target
|
||||
|
||||
@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):
|
||||
raise PatternError(f'Provided target {val} is not a str or None!')
|
||||
self._target = val
|
||||
@ -123,8 +123,8 @@ class Ref(
|
||||
def as_pattern(
|
||||
self,
|
||||
*,
|
||||
pattern: Optional['Pattern'] = None,
|
||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
||||
pattern: 'Pattern' | None = None,
|
||||
library: Mapping[str, 'Pattern'] | None = None,
|
||||
) -> 'Pattern':
|
||||
"""
|
||||
Args:
|
||||
@ -180,9 +180,9 @@ class Ref(
|
||||
def get_bounds(
|
||||
self,
|
||||
*,
|
||||
pattern: Optional['Pattern'] = None,
|
||||
library: Optional[Mapping[str, 'Pattern']] = None,
|
||||
) -> Optional[NDArray[numpy.float64]]:
|
||||
pattern: 'Pattern' | None = None,
|
||||
library: Mapping[str, 'Pattern'] | None = None,
|
||||
) -> NDArray[numpy.float64] | None:
|
||||
"""
|
||||
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the
|
||||
extent of the `Ref` in each dimension.
|
||||
|
@ -3,7 +3,7 @@
|
||||
instances of an object .
|
||||
"""
|
||||
|
||||
from typing import Union, Dict, Optional, Any, Type
|
||||
from typing import Any, Type
|
||||
import copy
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
@ -52,7 +52,7 @@ class Grid(Repetition):
|
||||
_a_count: int
|
||||
""" 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.
|
||||
Specifies center-to-center spacing between adjacent elements.
|
||||
Can be `None` for a 1D array.
|
||||
@ -65,8 +65,8 @@ class Grid(Repetition):
|
||||
self,
|
||||
a_vector: ArrayLike,
|
||||
a_count: int,
|
||||
b_vector: Optional[ArrayLike] = None,
|
||||
b_count: Optional[int] = 1,
|
||||
b_vector: ArrayLike | None = None,
|
||||
b_count: int | None = 1,
|
||||
) -> None:
|
||||
"""
|
||||
Args:
|
||||
@ -133,7 +133,7 @@ class Grid(Repetition):
|
||||
)
|
||||
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
|
||||
new = copy.copy(self)
|
||||
return new
|
||||
@ -154,7 +154,7 @@ class Grid(Repetition):
|
||||
|
||||
# b_vector property
|
||||
@property
|
||||
def b_vector(self) -> Optional[NDArray[numpy.float64]]:
|
||||
def b_vector(self) -> NDArray[numpy.float64] | None:
|
||||
return self._b_vector
|
||||
|
||||
@b_vector.setter
|
||||
@ -228,7 +228,7 @@ class Grid(Repetition):
|
||||
self.b_vector[1 - axis] *= -1
|
||||
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
|
||||
extent of the `Grid` in each dimension.
|
||||
@ -237,7 +237,7 @@ class Grid(Repetition):
|
||||
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
||||
"""
|
||||
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))
|
||||
xy_min = numpy.min(corners, axis=0)
|
||||
@ -350,7 +350,7 @@ class Arbitrary(Repetition):
|
||||
self.displacements[1 - axis] *= -1
|
||||
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
|
||||
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 math
|
||||
|
||||
@ -157,8 +157,8 @@ class Arc(Shape):
|
||||
rotation: float = 0,
|
||||
mirrored: Sequence[bool] = (False, False),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
if raw:
|
||||
@ -184,7 +184,7 @@ class Arc(Shape):
|
||||
self.layer = layer
|
||||
[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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -195,9 +195,9 @@ class Arc(Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: Optional[float] = None,
|
||||
) -> List[Polygon]:
|
||||
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: float | None = None,
|
||||
) -> list[Polygon]:
|
||||
if (num_vertices is None) and (max_arclen is None):
|
||||
raise PatternError('Max number of points and arclength left unspecified'
|
||||
+ ' (default was also overridden)')
|
||||
|
@ -1,4 +1,3 @@
|
||||
from typing import List, Dict, Optional
|
||||
import copy
|
||||
|
||||
import numpy
|
||||
@ -46,8 +45,8 @@ class Circle(Shape):
|
||||
*,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
if raw:
|
||||
@ -64,7 +63,7 @@ class Circle(Shape):
|
||||
self.annotations = annotations if annotations is not None else {}
|
||||
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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -73,14 +72,14 @@ class Circle(Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: Optional[float] = None,
|
||||
) -> List[Polygon]:
|
||||
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: float | None = None,
|
||||
) -> list[Polygon]:
|
||||
if (num_vertices is None) and (max_arclen is None):
|
||||
raise PatternError('Number of points and arclength left '
|
||||
'unspecified (default was also overridden)')
|
||||
|
||||
n: List[float] = []
|
||||
n: list[float] = []
|
||||
if num_vertices is not None:
|
||||
n += [num_vertices]
|
||||
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 math
|
||||
|
||||
@ -92,8 +92,8 @@ class Ellipse(Shape):
|
||||
rotation: float = 0,
|
||||
mirrored: Sequence[bool] = (False, False),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
if raw:
|
||||
@ -114,7 +114,7 @@ class Ellipse(Shape):
|
||||
self.layer = layer
|
||||
[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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -124,9 +124,9 @@ class Ellipse(Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: Optional[float] = None,
|
||||
) -> List[Polygon]:
|
||||
num_vertices: int | None = DEFAULT_POLY_NUM_VERTICES,
|
||||
max_arclen: float | None = None,
|
||||
) -> list[Polygon]:
|
||||
if (num_vertices is None) and (max_arclen is None):
|
||||
raise PatternError('Number of points and arclength left unspecified'
|
||||
' (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
|
||||
from enum import Enum
|
||||
|
||||
@ -36,7 +36,7 @@ class Path(Shape):
|
||||
_vertices: NDArray[numpy.float64]
|
||||
_width: float
|
||||
_cap: PathCap
|
||||
_cap_extensions: Optional[NDArray[numpy.float64]]
|
||||
_cap_extensions: NDArray[numpy.float64] | None
|
||||
|
||||
Cap = PathCap
|
||||
|
||||
@ -76,7 +76,7 @@ class Path(Shape):
|
||||
|
||||
# cap_extensions 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
|
||||
|
||||
@ -86,7 +86,7 @@ class Path(Shape):
|
||||
return self._cap_extensions
|
||||
|
||||
@cap_extensions.setter
|
||||
def cap_extensions(self, vals: Optional[ArrayLike]) -> None:
|
||||
def cap_extensions(self, vals: ArrayLike | None) -> None:
|
||||
custom_caps = (PathCap.SquareCustom,)
|
||||
if self.cap in custom_caps:
|
||||
if vals is None:
|
||||
@ -150,13 +150,13 @@ class Path(Shape):
|
||||
width: float = 0.0,
|
||||
*,
|
||||
cap: PathCap = PathCap.Flush,
|
||||
cap_extensions: Optional[ArrayLike] = None,
|
||||
cap_extensions: ArrayLike | None = None,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
rotation: float = 0,
|
||||
mirrored: Sequence[bool] = (False, False),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
self._cap_extensions = None # Since .cap setter might access it
|
||||
@ -185,7 +185,7 @@ class Path(Shape):
|
||||
self.rotate(rotation)
|
||||
[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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -197,10 +197,10 @@ class Path(Shape):
|
||||
|
||||
@staticmethod
|
||||
def travel(
|
||||
travel_pairs: Sequence[Tuple[float, float]],
|
||||
travel_pairs: Sequence[tuple[float, float]],
|
||||
width: float = 0.0,
|
||||
cap: PathCap = PathCap.Flush,
|
||||
cap_extensions: Optional[Tuple[float, float]] = None,
|
||||
cap_extensions: tuple[float, float] | None = None,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
rotation: float = 0,
|
||||
mirrored: Sequence[bool] = (False, False),
|
||||
@ -243,9 +243,9 @@ class Path(Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = None,
|
||||
max_arclen: Optional[float] = None,
|
||||
) -> List['Polygon']:
|
||||
num_vertices: int | None = None,
|
||||
max_arclen: float | None = None,
|
||||
) -> list['Polygon']:
|
||||
extensions = self._calculate_cap_extensions()
|
||||
|
||||
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 numpy
|
||||
@ -83,8 +83,8 @@ class Polygon(Shape):
|
||||
rotation: float = 0.0,
|
||||
mirrored: Sequence[bool] = (False, False),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
if raw:
|
||||
@ -104,7 +104,7 @@ class Polygon(Shape):
|
||||
self.rotate(rotation)
|
||||
[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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -119,7 +119,7 @@ class Polygon(Shape):
|
||||
rotation: float = 0.0,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
repetition: Repetition | None = None,
|
||||
) -> 'Polygon':
|
||||
"""
|
||||
Draw a square given side_length, centered on the origin.
|
||||
@ -151,7 +151,7 @@ class Polygon(Shape):
|
||||
rotation: float = 0,
|
||||
offset: ArrayLike = (0.0, 0.0),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
repetition: Repetition | None = None,
|
||||
) -> 'Polygon':
|
||||
"""
|
||||
Draw a rectangle with side lengths lx and ly, centered on the origin.
|
||||
@ -178,16 +178,16 @@ class Polygon(Shape):
|
||||
@staticmethod
|
||||
def rect(
|
||||
*,
|
||||
xmin: Optional[float] = None,
|
||||
xctr: Optional[float] = None,
|
||||
xmax: Optional[float] = None,
|
||||
lx: Optional[float] = None,
|
||||
ymin: Optional[float] = None,
|
||||
yctr: Optional[float] = None,
|
||||
ymax: Optional[float] = None,
|
||||
ly: Optional[float] = None,
|
||||
xmin: float | None = None,
|
||||
xctr: float | None = None,
|
||||
xmax: float | None = None,
|
||||
lx: float | None = None,
|
||||
ymin: float | None = None,
|
||||
yctr: float | None = None,
|
||||
ymax: float | None = None,
|
||||
ly: float | None = None,
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
repetition: Repetition | None = None,
|
||||
) -> 'Polygon':
|
||||
"""
|
||||
Draw a rectangle by specifying side/center positions.
|
||||
@ -276,13 +276,13 @@ class Polygon(Shape):
|
||||
@staticmethod
|
||||
def octagon(
|
||||
*,
|
||||
side_length: Optional[float] = None,
|
||||
inner_radius: Optional[float] = None,
|
||||
side_length: float | None = None,
|
||||
inner_radius: float | None = None,
|
||||
regular: bool = True,
|
||||
center: ArrayLike = (0.0, 0.0),
|
||||
rotation: float = 0.0,
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
repetition: Repetition | None = None,
|
||||
) -> 'Polygon':
|
||||
"""
|
||||
Draw an octagon given one of (side length, inradius, circumradius).
|
||||
@ -333,9 +333,9 @@ class Polygon(Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = None, # unused
|
||||
max_arclen: Optional[float] = None, # unused
|
||||
) -> List['Polygon']:
|
||||
num_vertices: int | None = None, # unused
|
||||
max_arclen: float | None = None, # unused
|
||||
) -> list['Polygon']:
|
||||
return [copy.deepcopy(self)]
|
||||
|
||||
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
|
||||
|
||||
import numpy
|
||||
@ -15,9 +15,9 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
# Type definitions
|
||||
normalized_shape_tuple = Tuple[
|
||||
Tuple,
|
||||
Tuple[NDArray[numpy.float64], float, float, bool],
|
||||
normalized_shape_tuple = tuple[
|
||||
tuple,
|
||||
tuple[NDArray[numpy.float64], float, float, bool],
|
||||
Callable[[], 'Shape'],
|
||||
]
|
||||
|
||||
@ -49,9 +49,9 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
|
||||
@abstractmethod
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = None,
|
||||
max_arclen: Optional[float] = None,
|
||||
) -> List['Polygon']:
|
||||
num_vertices: int | None = None,
|
||||
max_arclen: float | None = None,
|
||||
) -> list['Polygon']:
|
||||
"""
|
||||
Returns a list of polygons which approximate the shape.
|
||||
|
||||
@ -98,7 +98,7 @@ class Shape(PositionableImpl, LayerableImpl, Rotatable, Mirrorable, Copyable, Sc
|
||||
self,
|
||||
grid_x: ArrayLike,
|
||||
grid_y: ArrayLike,
|
||||
) -> List['Polygon']:
|
||||
) -> list['Polygon']:
|
||||
"""
|
||||
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,
|
||||
grid_x: ArrayLike,
|
||||
grid_y: ArrayLike,
|
||||
) -> List['Polygon']:
|
||||
) -> list['Polygon']:
|
||||
"""
|
||||
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 numpy
|
||||
@ -74,8 +74,8 @@ class Text(RotatableImpl, Shape):
|
||||
rotation: float = 0.0,
|
||||
mirrored: ArrayLike = (False, False),
|
||||
layer: layer_t = 0,
|
||||
repetition: Optional[Repetition] = None,
|
||||
annotations: Optional[annotations_t] = None,
|
||||
repetition: Repetition | None = None,
|
||||
annotations: annotations_t | None = None,
|
||||
raw: bool = False,
|
||||
) -> None:
|
||||
if raw:
|
||||
@ -100,7 +100,7 @@ class Text(RotatableImpl, Shape):
|
||||
self.annotations = annotations if annotations is not None else {}
|
||||
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
|
||||
new = copy.copy(self)
|
||||
new._offset = self._offset.copy()
|
||||
@ -110,9 +110,9 @@ class Text(RotatableImpl, Shape):
|
||||
|
||||
def to_polygons(
|
||||
self,
|
||||
num_vertices: Optional[int] = None, # unused
|
||||
max_arclen: Optional[float] = None, # unused
|
||||
) -> List[Polygon]:
|
||||
num_vertices: int | None = None, # unused
|
||||
max_arclen: float | None = None, # unused
|
||||
) -> list[Polygon]:
|
||||
all_polygons = []
|
||||
total_advance = 0.0
|
||||
for char in self.string:
|
||||
@ -172,7 +172,7 @@ def get_char_as_polygons(
|
||||
font_path: str,
|
||||
char: str,
|
||||
resolution: float = 48 * 64,
|
||||
) -> Tuple[List[List[List[float]]], float]:
|
||||
) -> tuple[list[list[list[float]]], float]:
|
||||
from freetype import Face # 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.append(tags[0])
|
||||
|
||||
segments: List[List[List[float]]] = []
|
||||
segments: list[list[list[float]]] = []
|
||||
for j, point in enumerate(points):
|
||||
# If we already have a segment, add this point to it
|
||||
if j > 0:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import TypeVar, Tuple
|
||||
from typing import TypeVar
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ class Mirrorable(metaclass=ABCMeta):
|
||||
"""
|
||||
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
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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
|
||||
|
||||
import numpy
|
||||
@ -65,7 +65,7 @@ class Positionable(metaclass=ABCMeta):
|
||||
pass
|
||||
|
||||
@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 `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 ..error import MasqueError
|
||||
@ -26,7 +26,7 @@ class Repeatable(metaclass=ABCMeta):
|
||||
'''
|
||||
@property
|
||||
@abstractmethod
|
||||
def repetition(self) -> Optional['Repetition']:
|
||||
def repetition(self) -> 'Repetition' | None:
|
||||
"""
|
||||
Repetition object, or None (single instance only)
|
||||
"""
|
||||
@ -34,14 +34,14 @@ class Repeatable(metaclass=ABCMeta):
|
||||
|
||||
# @repetition.setter
|
||||
# @abstractmethod
|
||||
# def repetition(self, repetition: Optional['Repetition']):
|
||||
# def repetition(self, repetition: 'Repetition' | None):
|
||||
# pass
|
||||
|
||||
'''
|
||||
---- Methods
|
||||
'''
|
||||
@abstractmethod
|
||||
def set_repetition(self: T, repetition: Optional['Repetition']) -> T:
|
||||
def set_repetition(self: T, repetition: 'Repetition' | None) -> T:
|
||||
"""
|
||||
Set the repetition
|
||||
|
||||
@ -60,18 +60,18 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
|
||||
"""
|
||||
__slots__ = _empty_slots
|
||||
|
||||
_repetition: Optional['Repetition']
|
||||
_repetition: 'Repetition' | None
|
||||
""" Repetition object, or None (single instance only) """
|
||||
|
||||
'''
|
||||
---- Non-abstract properties
|
||||
'''
|
||||
@property
|
||||
def repetition(self) -> Optional['Repetition']:
|
||||
def repetition(self) -> 'Repetition' | None:
|
||||
return self._repetition
|
||||
|
||||
@repetition.setter
|
||||
def repetition(self, repetition: Optional['Repetition']):
|
||||
def repetition(self, repetition: 'Repetition' | None):
|
||||
from ..repetition import Repetition
|
||||
if repetition is not None and not isinstance(repetition, Repetition):
|
||||
raise MasqueError(f'{repetition} is not a valid Repetition object!')
|
||||
@ -80,6 +80,6 @@ class RepeatableImpl(Repeatable, metaclass=ABCMeta):
|
||||
'''
|
||||
---- Non-abstract methods
|
||||
'''
|
||||
def set_repetition(self: I, repetition: Optional['Repetition']) -> I:
|
||||
def set_repetition(self: I, repetition: 'Repetition' | None) -> I:
|
||||
self.repetition = repetition
|
||||
return self
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
2D bin-packing
|
||||
"""
|
||||
from typing import Tuple, List, Set, Sequence, Callable, Mapping
|
||||
from typing import Sequence, Callable, Mapping
|
||||
|
||||
import numpy
|
||||
from numpy.typing import NDArray, ArrayLike
|
||||
@ -16,7 +16,7 @@ def maxrects_bssf(
|
||||
containers: ArrayLike,
|
||||
presort: bool = True,
|
||||
allow_rejects: bool = True,
|
||||
) -> Tuple[NDArray[numpy.float64], Set[int]]:
|
||||
) -> tuple[NDArray[numpy.float64], set[int]]:
|
||||
"""
|
||||
sizes should be Nx2
|
||||
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
||||
@ -88,7 +88,7 @@ def guillotine_bssf_sas(rect_sizes: numpy.ndarray,
|
||||
regions: numpy.ndarray,
|
||||
presort: bool = True,
|
||||
allow_rejects: bool = True,
|
||||
) -> Tuple[numpy.ndarray, Set[int]]:
|
||||
) -> tuple[numpy.ndarray, set[int]]:
|
||||
"""
|
||||
sizes should be Nx2
|
||||
regions should be Mx4 (xmin, ymin, xmax, ymax)
|
||||
@ -146,11 +146,11 @@ def pack_patterns(
|
||||
library: Mapping[str, Pattern],
|
||||
patterns: Sequence[str],
|
||||
regions: numpy.ndarray,
|
||||
spacing: Tuple[float, float],
|
||||
spacing: tuple[float, float],
|
||||
presort: bool = True,
|
||||
allow_rejects: bool = True,
|
||||
packer: Callable = maxrects_bssf,
|
||||
) -> Tuple[Pattern, List[str]]:
|
||||
) -> tuple[Pattern, list[str]]:
|
||||
half_spacing = numpy.array(spacing) / 2
|
||||
|
||||
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
|
||||
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 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.
|
||||
# LazyLibrary protects against library[ref.target] causing a circular lookup.
|
||||
# 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,
|
||||
skip_subcells: bool = True,
|
||||
# TODO missing ok?
|
||||
@ -130,7 +130,7 @@ def data_to_ports(
|
||||
def data_to_ports_flat(
|
||||
layers: Sequence[layer_t],
|
||||
pattern: Pattern,
|
||||
cell_name: Optional[str] = None,
|
||||
cell_name: str | None = None,
|
||||
) -> Pattern:
|
||||
"""
|
||||
Examine `pattern` for labels specifying port info, and use that info
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Geometric transforms
|
||||
"""
|
||||
from typing import Sequence, Tuple
|
||||
from typing import Sequence
|
||||
|
||||
import numpy
|
||||
from numpy.typing import NDArray
|
||||
@ -21,7 +21,7 @@ def rotation_matrix_2d(theta: float) -> NDArray[numpy.float64]:
|
||||
[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)`
|
||||
into 0-1 mirror operations and a rotation
|
||||
|
@ -1,11 +1,11 @@
|
||||
"""
|
||||
Type definitions
|
||||
"""
|
||||
from typing import Union, Tuple, Dict, List, Protocol
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
layer_t = Union[int, Tuple[int, int], str]
|
||||
annotations_t = Dict[str, List[Union[int, float, str]]]
|
||||
layer_t = int | tuple[int, int] | str
|
||||
annotations_t = dict[str, list[int | float | str]]
|
||||
|
||||
|
||||
class SupportsBool(Protocol):
|
||||
|
@ -113,5 +113,3 @@ def poly_contains_points(
|
||||
inside = nontrivial.copy()
|
||||
inside[nontrivial] = nontrivial_inside
|
||||
return inside
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user