Compare commits
7 Commits
572c32ad1b
...
7b9f9f4012
Author | SHA1 | Date | |
---|---|---|---|
7b9f9f4012 | |||
04f5eb23cc | |||
f587fe5341 | |||
8d4a6a8867 | |||
bbdf6b877d | |||
ed649dbd1a | |||
33a1e2eb85 |
@ -233,5 +233,3 @@ my_pattern.ref(_make_my_subpattern(), offset=..., ...)
|
|||||||
* Tests tests tests
|
* Tests tests tests
|
||||||
* check renderpather
|
* check renderpather
|
||||||
* pather and renderpather examples
|
* pather and renderpather examples
|
||||||
* context manager for retool
|
|
||||||
* allow a specific mismatch when connecting ports
|
|
||||||
|
@ -265,6 +265,12 @@ def main() -> None:
|
|||||||
# when using pather.retool().
|
# when using pather.retool().
|
||||||
pather.path_to('VCC', None, -50_000, out_ptype='m1wire')
|
pather.path_to('VCC', None, -50_000, out_ptype='m1wire')
|
||||||
|
|
||||||
|
# Now extend GND out to x=-50_000, using M2 for a portion of the path.
|
||||||
|
# We can use `pather.toolctx()` to temporarily retool, instead of calling `retool()` twice.
|
||||||
|
with pather.toolctx(M2_tool, keys=['GND']):
|
||||||
|
pather.path_to('GND', None, -40_000)
|
||||||
|
pather.path_to('GND', None, -50_000)
|
||||||
|
|
||||||
# Save the pather's pattern into our library
|
# Save the pather's pattern into our library
|
||||||
library['Pather_and_BasicTool'] = pather.pattern
|
library['Pather_and_BasicTool'] = pather.pattern
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Simplified Pattern assembly (`Builder`)
|
Simplified Pattern assembly (`Builder`)
|
||||||
"""
|
"""
|
||||||
from typing import Self
|
from typing import Self
|
||||||
from collections.abc import Sequence, Mapping
|
from collections.abc import Iterable, Sequence, Mapping
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
@ -226,6 +226,7 @@ class Builder(PortList):
|
|||||||
inherit_name: bool = True,
|
inherit_name: bool = True,
|
||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
append: bool = False,
|
append: bool = False,
|
||||||
|
ok_connections: Iterable[tuple[str, str]] = (),
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Wrapper around `Pattern.plug` which allows a string for `other`.
|
Wrapper around `Pattern.plug` which allows a string for `other`.
|
||||||
@ -260,6 +261,11 @@ class Builder(PortList):
|
|||||||
append: If `True`, `other` is appended instead of being referenced.
|
append: If `True`, `other` is appended instead of being referenced.
|
||||||
Note that this does not flatten `other`, so its refs will still
|
Note that this does not flatten `other`, so its refs will still
|
||||||
be refs (now inside `self`).
|
be refs (now inside `self`).
|
||||||
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
|
`(b, a)`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
@ -293,6 +299,7 @@ class Builder(PortList):
|
|||||||
inherit_name=inherit_name,
|
inherit_name=inherit_name,
|
||||||
set_rotation=set_rotation,
|
set_rotation=set_rotation,
|
||||||
append=append,
|
append=append,
|
||||||
|
ok_connections=ok_connections,
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
Manual wire/waveguide routing (`Pather`)
|
Manual wire/waveguide routing (`Pather`)
|
||||||
"""
|
"""
|
||||||
from typing import Self
|
from typing import Self
|
||||||
from collections.abc import Sequence, MutableMapping, Mapping
|
from collections.abc import Sequence, MutableMapping, Mapping, Iterator
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
from contextlib import contextmanager
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -281,6 +282,37 @@ class Pather(Builder):
|
|||||||
self.tools[key] = tool
|
self.tools[key] = tool
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def toolctx(
|
||||||
|
self,
|
||||||
|
tool: Tool,
|
||||||
|
keys: str | Sequence[str | None] | None = None,
|
||||||
|
) -> Iterator[Self]:
|
||||||
|
"""
|
||||||
|
Context manager for temporarily `retool`-ing and reverting the `retool`
|
||||||
|
upon exiting the context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: The new `Tool` to use for the given ports.
|
||||||
|
keys: Which ports the tool should apply to. `None` indicates the default tool,
|
||||||
|
used when there is no matching entry in `self.tools` for the port in question.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
"""
|
||||||
|
if keys is None or isinstance(keys, str):
|
||||||
|
keys = [keys]
|
||||||
|
saved_tools = {kk: self.tools.get(kk, None) for kk in keys} # If not in self.tools, save `None`
|
||||||
|
try:
|
||||||
|
yield self.retool(tool=tool, keys=keys)
|
||||||
|
finally:
|
||||||
|
for kk, tt in saved_tools.items():
|
||||||
|
if tt is None:
|
||||||
|
# delete if present
|
||||||
|
self.tools.pop(kk, None)
|
||||||
|
else:
|
||||||
|
self.tools[kk] = tt
|
||||||
|
|
||||||
def path(
|
def path(
|
||||||
self,
|
self,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
|
@ -542,7 +542,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
Return:
|
Return:
|
||||||
Topologically sorted list of pattern names.
|
Topologically sorted list of pattern names.
|
||||||
"""
|
"""
|
||||||
return list(TopologicalSorter(self.child_graph()).static_order())
|
return cast(list[str], list(TopologicalSorter(self.child_graph()).static_order()))
|
||||||
|
|
||||||
def find_refs_local(
|
def find_refs_local(
|
||||||
self,
|
self,
|
||||||
|
@ -1225,6 +1225,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
inherit_name: bool = True,
|
inherit_name: bool = True,
|
||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
append: bool = False,
|
append: bool = False,
|
||||||
|
ok_connections: Iterable[tuple[str, str]] = (),
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Instantiate or append a pattern into the current pattern, connecting
|
Instantiate or append a pattern into the current pattern, connecting
|
||||||
@ -1270,6 +1271,11 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
append: If `True`, `other` is appended instead of being referenced.
|
append: If `True`, `other` is appended instead of being referenced.
|
||||||
Note that this does not flatten `other`, so its refs will still
|
Note that this does not flatten `other`, so its refs will still
|
||||||
be refs (now inside `self`).
|
be refs (now inside `self`).
|
||||||
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
|
`(b, a)`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
@ -1300,6 +1306,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
map_in,
|
map_in,
|
||||||
mirrored=mirrored,
|
mirrored=mirrored,
|
||||||
set_rotation=set_rotation,
|
set_rotation=set_rotation,
|
||||||
|
ok_connections=ok_connections,
|
||||||
)
|
)
|
||||||
|
|
||||||
# get rid of plugged ports
|
# get rid of plugged ports
|
||||||
|
@ -419,6 +419,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
*,
|
*,
|
||||||
mirrored: bool = False,
|
mirrored: bool = False,
|
||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
|
ok_connections: Iterable[tuple[str, str]] = (),
|
||||||
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||||
"""
|
"""
|
||||||
Given a device `other` and a mapping `map_in` specifying port connections,
|
Given a device `other` and a mapping `map_in` specifying port connections,
|
||||||
@ -435,6 +436,11 @@ class PortList(metaclass=ABCMeta):
|
|||||||
port with `rotation=None`), `set_rotation` must be provided
|
port with `rotation=None`), `set_rotation` must be provided
|
||||||
to indicate how much `other` should be rotated. Otherwise,
|
to indicate how much `other` should be rotated. Otherwise,
|
||||||
`set_rotation` must remain `None`.
|
`set_rotation` must remain `None`.
|
||||||
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
|
`(b, a)`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
- The (x, y) translation (performed last)
|
- The (x, y) translation (performed last)
|
||||||
@ -451,6 +457,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
map_in=map_in,
|
map_in=map_in,
|
||||||
mirrored=mirrored,
|
mirrored=mirrored,
|
||||||
set_rotation=set_rotation,
|
set_rotation=set_rotation,
|
||||||
|
ok_connections=ok_connections,
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -461,13 +468,14 @@ class PortList(metaclass=ABCMeta):
|
|||||||
*,
|
*,
|
||||||
mirrored: bool = False,
|
mirrored: bool = False,
|
||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
|
ok_connections: Iterable[tuple[str, str]] = (),
|
||||||
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||||
"""
|
"""
|
||||||
Given two sets of ports (s_ports and o_ports) and a mapping `map_in`
|
Given two sets of ports (s_ports and o_ports) and a mapping `map_in`
|
||||||
specifying port connections, find the transform which will correctly
|
specifying port connections, find the transform which will correctly
|
||||||
align the specified o_ports onto their respective s_ports.
|
align the specified o_ports onto their respective s_ports.
|
||||||
|
|
||||||
Args:t
|
Args:
|
||||||
s_ports: A list of stationary ports
|
s_ports: A list of stationary ports
|
||||||
o_ports: A list of ports which are to be moved/mirrored.
|
o_ports: A list of ports which are to be moved/mirrored.
|
||||||
map_in: dict of `{'s_port': 'o_port'}` mappings, specifying
|
map_in: dict of `{'s_port': 'o_port'}` mappings, specifying
|
||||||
@ -479,6 +487,11 @@ class PortList(metaclass=ABCMeta):
|
|||||||
port with `rotation=None`), `set_rotation` must be provided
|
port with `rotation=None`), `set_rotation` must be provided
|
||||||
to indicate how much `o_ports` should be rotated. Otherwise,
|
to indicate how much `o_ports` should be rotated. Otherwise,
|
||||||
`set_rotation` must remain `None`.
|
`set_rotation` must remain `None`.
|
||||||
|
ok_connections: Set of "allowed" ptype combinations. Identical
|
||||||
|
ptypes are always allowed to connect, as is `'unk'` with
|
||||||
|
any other ptypte. Non-allowed ptype connections will emit a
|
||||||
|
warning. Order is ignored, i.e. `(a, b)` is equivalent to
|
||||||
|
`(b, a)`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
- The (x, y) translation (performed last)
|
- The (x, y) translation (performed last)
|
||||||
@ -502,7 +515,8 @@ class PortList(metaclass=ABCMeta):
|
|||||||
o_offsets[:, 1] *= -1
|
o_offsets[:, 1] *= -1
|
||||||
o_rotations *= -1
|
o_rotations *= -1
|
||||||
|
|
||||||
type_conflicts = numpy.array([st != ot and 'unk' not in (st, ot)
|
ok_pairs = {tuple(sorted(pair)) for pair in ok_connections if pair[0] != pair[1]}
|
||||||
|
type_conflicts = numpy.array([(st != ot) and ('unk' not in (st, ot)) and (tuple(sorted((st, ot))) not in ok_pairs)
|
||||||
for st, ot in zip(s_types, o_types, strict=True)])
|
for st, ot in zip(s_types, o_types, strict=True)])
|
||||||
if type_conflicts.any():
|
if type_conflicts.any():
|
||||||
msg = 'Ports have conflicting types:\n'
|
msg = 'Ports have conflicting types:\n'
|
||||||
@ -523,8 +537,8 @@ class PortList(metaclass=ABCMeta):
|
|||||||
if not numpy.allclose(rotations[:1], rotations):
|
if not numpy.allclose(rotations[:1], rotations):
|
||||||
rot_deg = numpy.rad2deg(rotations)
|
rot_deg = numpy.rad2deg(rotations)
|
||||||
msg = 'Port orientations do not match:\n'
|
msg = 'Port orientations do not match:\n'
|
||||||
for nn, (k, v) in enumerate(map_in.items()):
|
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||||
msg += f'{k} | {rot_deg[nn]:g} | {v}\n'
|
msg += f'{kk} | {rot_deg[nn]:g} | {vv}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
pivot = o_offsets[0].copy()
|
pivot = o_offsets[0].copy()
|
||||||
@ -532,8 +546,8 @@ class PortList(metaclass=ABCMeta):
|
|||||||
translations = s_offsets - o_offsets
|
translations = s_offsets - o_offsets
|
||||||
if not numpy.allclose(translations[:1], translations):
|
if not numpy.allclose(translations[:1], translations):
|
||||||
msg = 'Port translations do not match:\n'
|
msg = 'Port translations do not match:\n'
|
||||||
for nn, (k, v) in enumerate(map_in.items()):
|
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||||
msg += f'{k} | {translations[nn]} | {v}\n'
|
msg += f'{kk} | {translations[nn]} | {vv}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
return translations[0], rotations[0], o_offsets[0]
|
return translations[0], rotations[0], o_offsets[0]
|
||||||
|
@ -233,7 +233,7 @@ class Arc(Shape):
|
|||||||
r0, r1 = self.radii
|
r0, r1 = self.radii
|
||||||
|
|
||||||
# Convert from polar angle to ellipse parameter (for [rx*cos(t), ry*sin(t)] representation)
|
# Convert from polar angle to ellipse parameter (for [rx*cos(t), ry*sin(t)] representation)
|
||||||
a_ranges = self._angles_to_parameters()
|
a_ranges = cast(tuple[tuple[float, float], tuple[float, float]], self._angles_to_parameters())
|
||||||
|
|
||||||
# Approximate perimeter via numerical integration
|
# Approximate perimeter via numerical integration
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user