pather fixes / type updates
This commit is contained in:
parent
f22e737e60
commit
e5de33e1f1
@ -1,4 +1,4 @@
|
|||||||
from typing import Self, Sequence, Mapping
|
from typing import Self, Sequence, Mapping, Literal, overload, Final, cast
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -102,12 +102,6 @@ class Builder(PortList):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
# TODO documentation for Builder() constructor
|
||||||
|
|
||||||
# TODO MOVE THE BELOW DOCS to PortList
|
|
||||||
# If `ports` is `None`, two default ports ('A' and 'B') are created.
|
|
||||||
# Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0
|
|
||||||
# (attached devices will be placed to the left) and 'B' has rotation
|
|
||||||
# pi (attached devices will be placed to the right).
|
|
||||||
"""
|
"""
|
||||||
self._dead = False
|
self._dead = False
|
||||||
self.library = library
|
self.library = library
|
||||||
@ -165,8 +159,7 @@ class Builder(PortList):
|
|||||||
Args:
|
Args:
|
||||||
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
||||||
from which to create the interface.
|
from which to create the interface.
|
||||||
library: Used for buildin functions; if not passed and the source
|
library: Library from which existing patterns should be referenced, TODO
|
||||||
library: Library from which existing patterns should be referenced,
|
|
||||||
and to which new ones should be added. If not provided,
|
and to which new ones should be added. If not provided,
|
||||||
the source's library will be used (if available).
|
the source's library will be used (if available).
|
||||||
in_prefix: Prepended to port names for newly-created ports with
|
in_prefix: Prepended to port names for newly-created ports with
|
||||||
@ -230,18 +223,47 @@ class Builder(PortList):
|
|||||||
new = Builder(library=library, ports={**ports_in, **ports_out}, name=name)
|
new = Builder(library=library, ports={**ports_in, **ports_out}, name=name)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
# @overload
|
||||||
|
# def plug(
|
||||||
|
# self,
|
||||||
|
# other: Abstract | str,
|
||||||
|
# map_in: dict[str, str],
|
||||||
|
# map_out: dict[str, str | None] | None,
|
||||||
|
# *,
|
||||||
|
# mirrored: tuple[bool, bool],
|
||||||
|
# inherit_name: bool,
|
||||||
|
# set_rotation: bool | None,
|
||||||
|
# append: bool,
|
||||||
|
# ) -> Self:
|
||||||
|
# pass
|
||||||
|
#
|
||||||
|
# @overload
|
||||||
|
# def plug(
|
||||||
|
# self,
|
||||||
|
# other: Pattern,
|
||||||
|
# map_in: dict[str, str],
|
||||||
|
# map_out: dict[str, str | None] | None = None,
|
||||||
|
# *,
|
||||||
|
# mirrored: tuple[bool, bool] = (False, False),
|
||||||
|
# inherit_name: bool = True,
|
||||||
|
# set_rotation: bool | None = None,
|
||||||
|
# append: bool = False,
|
||||||
|
# ) -> Self:
|
||||||
|
# pass
|
||||||
|
|
||||||
def plug(
|
def plug(
|
||||||
self,
|
self,
|
||||||
other: Abstract | str,
|
other: Abstract | str | Pattern,
|
||||||
map_in: dict[str, str],
|
map_in: dict[str, str],
|
||||||
map_out: dict[str, str | None] | None = None,
|
map_out: dict[str, str | None] | None = None,
|
||||||
*,
|
*,
|
||||||
mirrored: tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
inherit_name: bool = True,
|
inherit_name: bool = True,
|
||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Instantiate a device `library[name]` into the current device, connecting
|
Instantiate or append a pattern into the current device, connecting
|
||||||
the ports specified by `map_in` and renaming the unconnected
|
the ports specified by `map_in` and renaming the unconnected
|
||||||
ports specified by `map_out`.
|
ports specified by `map_out`.
|
||||||
|
|
||||||
@ -327,23 +349,59 @@ class Builder(PortList):
|
|||||||
del self.ports[ki]
|
del self.ports[ki]
|
||||||
map_out[vi] = None
|
map_out[vi] = None
|
||||||
|
|
||||||
|
if isinstance(other, Pattern):
|
||||||
|
assert append
|
||||||
self.place(other, offset=translation, rotation=rotation, pivot=pivot,
|
self.place(other, offset=translation, rotation=rotation, pivot=pivot,
|
||||||
mirrored=mirrored, port_map=map_out, skip_port_check=True)
|
mirrored=mirrored, port_map=map_out, skip_port_check=True, append=append)
|
||||||
|
else:
|
||||||
|
self.place(other, offset=translation, rotation=rotation, pivot=pivot,
|
||||||
|
mirrored=mirrored, port_map=map_out, skip_port_check=True, append=append)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@overload
|
||||||
def place(
|
def place(
|
||||||
self,
|
self,
|
||||||
other: Abstract | str,
|
other: Abstract | str,
|
||||||
*,
|
*,
|
||||||
|
offset: ArrayLike,
|
||||||
|
rotation: float,
|
||||||
|
pivot: ArrayLike,
|
||||||
|
mirrored: tuple[bool, bool],
|
||||||
|
port_map: dict[str, str | None] | None,
|
||||||
|
skip_port_check: bool,
|
||||||
|
append: bool,
|
||||||
|
) -> Self:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def place(
|
||||||
|
self,
|
||||||
|
other: Pattern,
|
||||||
|
*,
|
||||||
|
offset: ArrayLike,
|
||||||
|
rotation: float,
|
||||||
|
pivot: ArrayLike,
|
||||||
|
mirrored: tuple[bool, bool],
|
||||||
|
port_map: dict[str, str | None] | None,
|
||||||
|
skip_port_check: bool,
|
||||||
|
append: Literal[True],
|
||||||
|
) -> Self:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def place(
|
||||||
|
self,
|
||||||
|
other: Abstract | str | Pattern,
|
||||||
|
*,
|
||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
pivot: ArrayLike = (0, 0),
|
pivot: ArrayLike = (0, 0),
|
||||||
mirrored: tuple[bool, bool] = (False, False),
|
mirrored: tuple[bool, bool] = (False, False),
|
||||||
port_map: dict[str, str | None] | None = None,
|
port_map: dict[str, str | None] | None = None,
|
||||||
skip_port_check: bool = False,
|
skip_port_check: bool = False,
|
||||||
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Instantiate the device `other` into the current device, adding its
|
Instantiate or append the device `other` into the current device, adding its
|
||||||
ports to those of the current device (but not connecting any ports).
|
ports to those of the current device (but not connecting any ports).
|
||||||
|
|
||||||
Mirroring is applied before rotation; translation (`offset`) is applied last.
|
Mirroring is applied before rotation; translation (`offset`) is applied last.
|
||||||
@ -375,7 +433,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
`PortError` if any ports specified in `map_in` or `map_out` do not
|
`PortError` if any ports specified in `map_in` or `map_out` do not
|
||||||
exist in `self.ports` or `library[name].ports`.
|
exist in `self.ports` or `other.ports`.
|
||||||
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
||||||
are applied.
|
are applied.
|
||||||
"""
|
"""
|
||||||
@ -408,10 +466,26 @@ class Builder(PortList):
|
|||||||
p.translate(offset)
|
p.translate(offset)
|
||||||
self.ports[name] = p
|
self.ports[name] = p
|
||||||
|
|
||||||
sp = Ref(other.name, mirrored=mirrored)
|
if append:
|
||||||
sp.rotate_around(pivot, rotation)
|
if isinstance(other, Pattern):
|
||||||
sp.translate(offset)
|
other_pat = other
|
||||||
self.pattern.refs.append(sp)
|
elif isinstance(other, Abstract):
|
||||||
|
assert self.library is not None
|
||||||
|
other_pat = self.library[other.name]
|
||||||
|
else:
|
||||||
|
other_pat = self.library[name]
|
||||||
|
other_copy = other_pat.deepcopy()
|
||||||
|
other_copy.ports.clear()
|
||||||
|
other_copy.mirror2d(mirrored)
|
||||||
|
other_copy.rotate_around(pivot, rotation)
|
||||||
|
other_copy.translate_elements(offset)
|
||||||
|
self.pattern.append(other_copy)
|
||||||
|
else:
|
||||||
|
assert not isinstance(other, Pattern)
|
||||||
|
ref = Ref(other.name, mirrored=mirrored)
|
||||||
|
ref.rotate_around(pivot, rotation)
|
||||||
|
ref.translate(offset)
|
||||||
|
self.pattern.refs.append(ref)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def translate(self, offset: ArrayLike) -> Self:
|
def translate(self, offset: ArrayLike) -> Self:
|
||||||
|
@ -53,6 +53,7 @@ class FlatBuilder(PortList):
|
|||||||
"""
|
"""
|
||||||
# TODO documentation for FlatBuilder() constructor
|
# TODO documentation for FlatBuilder() constructor
|
||||||
"""
|
"""
|
||||||
|
self._dead = False
|
||||||
if pattern is not None:
|
if pattern is not None:
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
else:
|
else:
|
||||||
@ -70,8 +71,6 @@ class FlatBuilder(PortList):
|
|||||||
else:
|
else:
|
||||||
self.tools = dict(tools)
|
self.tools = dict(tools)
|
||||||
|
|
||||||
self._dead = False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def interface(
|
def interface(
|
||||||
cls,
|
cls,
|
||||||
@ -105,7 +104,6 @@ class FlatBuilder(PortList):
|
|||||||
Args:
|
Args:
|
||||||
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
||||||
from which to create the interface.
|
from which to create the interface.
|
||||||
library: Used for buildin functions; if not passed and the source TODO
|
|
||||||
library: Library from which existing patterns should be referenced, TODO
|
library: Library from which existing patterns should be referenced, TODO
|
||||||
and to which new ones should be added. If not provided,
|
and to which new ones should be added. If not provided,
|
||||||
the source's library will be used (if available).
|
the source's library will be used (if available).
|
||||||
@ -429,7 +427,7 @@ class FlatBuilder(PortList):
|
|||||||
ccw: SupportsBool | None,
|
ccw: SupportsBool | None,
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
base_name: str = '_path',
|
base_name: str = '_path',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
@ -448,7 +446,7 @@ class FlatBuilder(PortList):
|
|||||||
ccw: SupportsBool | None,
|
ccw: SupportsBool | None,
|
||||||
position: float,
|
position: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
base_name: str = '_pathto',
|
base_name: str = '_pathto',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
@ -483,7 +481,7 @@ class FlatBuilder(PortList):
|
|||||||
*,
|
*,
|
||||||
spacing: float | ArrayLike | None = None,
|
spacing: float | ArrayLike | None = None,
|
||||||
set_rotation: float | None = None,
|
set_rotation: float | None = None,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
@ -218,7 +218,7 @@ class Pather(Builder):
|
|||||||
ccw: SupportsBool | None,
|
ccw: SupportsBool | None,
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
base_name: str = '_path',
|
base_name: str = '_path',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
@ -239,7 +239,7 @@ class Pather(Builder):
|
|||||||
ccw: SupportsBool | None,
|
ccw: SupportsBool | None,
|
||||||
position: float,
|
position: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
base_name: str = '_pathto',
|
base_name: str = '_pathto',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
@ -274,7 +274,7 @@ class Pather(Builder):
|
|||||||
*,
|
*,
|
||||||
spacing: float | ArrayLike | None = None,
|
spacing: float | ArrayLike | None = None,
|
||||||
set_rotation: float | None = None,
|
set_rotation: float | None = None,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: tuple[str, str] = ('A', 'B'),
|
||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
"""
|
"""
|
||||||
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
||||||
"""
|
"""
|
||||||
from typing import TYPE_CHECKING, Sequence, Literal, Tuple
|
from typing import TYPE_CHECKING, Sequence, Literal, Callable
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
from ..utils import SupportsBool
|
import numpy
|
||||||
|
|
||||||
|
from ..utils import SupportsBool, rotation_matrix_2d
|
||||||
from ..ports import Port
|
from ..ports import Port
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
from ..library import ILibrary
|
from ..abstract import Abstract
|
||||||
|
from ..library import ILibrary, Library
|
||||||
|
from ..error import BuildError
|
||||||
|
from .builder import Builder
|
||||||
|
|
||||||
|
|
||||||
render_step_t = (
|
render_step_t = (
|
||||||
@ -22,7 +28,7 @@ class Tool:
|
|||||||
*,
|
*,
|
||||||
in_ptype: str | None = None,
|
in_ptype: str | None = None,
|
||||||
out_ptype: str | None = None,
|
out_ptype: str | None = None,
|
||||||
port_names: Sequence[str] = ('A', 'B'),
|
port_names: tuple[str, str] = ('A', 'B'),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Pattern:
|
) -> Pattern:
|
||||||
raise NotImplementedError(f'path() not implemented for {type(self)}')
|
raise NotImplementedError(f'path() not implemented for {type(self)}')
|
||||||
@ -35,7 +41,7 @@ class Tool:
|
|||||||
in_ptype: str | None = None,
|
in_ptype: str | None = None,
|
||||||
out_ptype: str | None = None,
|
out_ptype: str | None = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Tuple[float, str]:
|
) -> tuple[float, str]:
|
||||||
raise NotImplementedError(f'planL() not implemented for {type(self)}')
|
raise NotImplementedError(f'planL() not implemented for {type(self)}')
|
||||||
|
|
||||||
def planS(
|
def planS(
|
||||||
@ -62,3 +68,91 @@ class Tool:
|
|||||||
assert batch[0][-1] == self
|
assert batch[0][-1] == self
|
||||||
raise NotImplementedError(f'render() not implemented for {type(self)}')
|
raise NotImplementedError(f'render() not implemented for {type(self)}')
|
||||||
|
|
||||||
|
|
||||||
|
class BasicTool(Tool, metaclass=ABCMeta):
|
||||||
|
straight: tuple[Callable[[float], Pattern], str, str]
|
||||||
|
bend: tuple[Abstract, str, str] # Assumed to be clockwise
|
||||||
|
transitions: dict[str, tuple[Abstract, str, str]]
|
||||||
|
|
||||||
|
def path(
|
||||||
|
self,
|
||||||
|
ccw: SupportsBool | None,
|
||||||
|
length: float,
|
||||||
|
*,
|
||||||
|
in_ptype: str | None = None,
|
||||||
|
out_ptype: str | None = None,
|
||||||
|
port_names: tuple[str, str] = ('A', 'B'),
|
||||||
|
**kwargs,
|
||||||
|
) -> Pattern:
|
||||||
|
|
||||||
|
# TODO check all the math for L-shaped bends
|
||||||
|
straight_length = length
|
||||||
|
bend_run = 0
|
||||||
|
if ccw is not None:
|
||||||
|
bend, bport_in, bport_out = self.bend
|
||||||
|
brot = bend.ports[bport_in].rotation
|
||||||
|
assert brot is not None
|
||||||
|
bend_dxy = numpy.abs(
|
||||||
|
rotation_matrix_2d(-brot) @ (
|
||||||
|
bend.ports[bport_out].offset
|
||||||
|
- bend.ports[bport_in].offset
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
straight_length -= bend_dxy[0]
|
||||||
|
bend_run += bend_dxy[1]
|
||||||
|
else:
|
||||||
|
bend_dxy = numpy.zeros(2)
|
||||||
|
|
||||||
|
in_transition = self.transitions.get('unk' if in_ptype is None else in_ptype, None)
|
||||||
|
if in_transition is not None:
|
||||||
|
ipat, iport_theirs, iport_ours = in_transition
|
||||||
|
irot = ipat.ports[iport_theirs].rotation
|
||||||
|
assert irot is not None
|
||||||
|
itrans_dxy = rotation_matrix_2d(-irot) @ (
|
||||||
|
ipat.ports[iport_ours].offset
|
||||||
|
- ipat.ports[iport_theirs].offset
|
||||||
|
)
|
||||||
|
|
||||||
|
straight_length -= itrans_dxy[0]
|
||||||
|
bend_run += itrans_dxy[1]
|
||||||
|
else:
|
||||||
|
itrans_dxy = numpy.zeros(2)
|
||||||
|
|
||||||
|
out_transition = self.transitions.get('unk' if out_ptype is None else out_ptype, None)
|
||||||
|
if out_transition is not None:
|
||||||
|
opat, oport_theirs, oport_ours = out_transition
|
||||||
|
orot = opat.ports[oport_ours].rotation
|
||||||
|
assert orot is not None
|
||||||
|
otrans_dxy = rotation_matrix_2d(-orot) @ (
|
||||||
|
opat.ports[oport_theirs].offset
|
||||||
|
- opat.ports[oport_ours].offset
|
||||||
|
)
|
||||||
|
if ccw:
|
||||||
|
otrans_dxy[0] *= -1
|
||||||
|
|
||||||
|
straight_length -= otrans_dxy[1]
|
||||||
|
bend_run += otrans_dxy[0]
|
||||||
|
else:
|
||||||
|
otrans_dxy = numpy.zeros(2)
|
||||||
|
|
||||||
|
if straight_length < 0:
|
||||||
|
raise BuildError(f'Asked to draw path with total length {length:g}, shorter than required bends and tapers:\n'
|
||||||
|
f'bend: {bend_dxy[0]:g} in_taper: {abs(itrans_dxy[0])} out_taper: {otrans_dxy[1]}')
|
||||||
|
|
||||||
|
gen_straight, sport_in, sport_out = self.straight
|
||||||
|
tree = Library()
|
||||||
|
bb = Builder(library=tree, name='_path').add_port_pair(names=port_names)
|
||||||
|
if in_transition:
|
||||||
|
bb.plug(ipat, {port_names[1]: iport_theirs})
|
||||||
|
if not numpy.isclose(straight_length, 0):
|
||||||
|
straight = tree << {'_straight': gen_straight(straight_length)}
|
||||||
|
bb.plug(straight, {port_names[1]: sport_in})
|
||||||
|
if ccw is not None:
|
||||||
|
bb.plug(bend, {port_names[1]: bport_in}, mirrored=(False, bool(ccw)))
|
||||||
|
if out_transition:
|
||||||
|
bb.plug(opat, {port_names[1]: oport_ours})
|
||||||
|
|
||||||
|
|
||||||
|
return bb.pattern
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user