Compare commits
7 Commits
master
...
path_impro
Author | SHA1 | Date | |
---|---|---|---|
7b9f9f4012 | |||
04f5eb23cc | |||
f587fe5341 | |||
8d4a6a8867 | |||
bbdf6b877d | |||
ed649dbd1a | |||
33a1e2eb85 |
@ -233,5 +233,3 @@ my_pattern.ref(_make_my_subpattern(), offset=..., ...)
|
||||
* Tests tests tests
|
||||
* check renderpather
|
||||
* 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().
|
||||
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
|
||||
library['Pather_and_BasicTool'] = pather.pattern
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Simplified Pattern assembly (`Builder`)
|
||||
"""
|
||||
from typing import Self
|
||||
from collections.abc import Sequence, Mapping
|
||||
from collections.abc import Iterable, Sequence, Mapping
|
||||
import copy
|
||||
import logging
|
||||
from functools import wraps
|
||||
@ -226,6 +226,7 @@ class Builder(PortList):
|
||||
inherit_name: bool = True,
|
||||
set_rotation: bool | None = None,
|
||||
append: bool = False,
|
||||
ok_connections: Iterable[tuple[str, str]] = (),
|
||||
) -> Self:
|
||||
"""
|
||||
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.
|
||||
Note that this does not flatten `other`, so its refs will still
|
||||
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:
|
||||
self
|
||||
@ -293,6 +299,7 @@ class Builder(PortList):
|
||||
inherit_name=inherit_name,
|
||||
set_rotation=set_rotation,
|
||||
append=append,
|
||||
ok_connections=ok_connections,
|
||||
)
|
||||
return self
|
||||
|
||||
|
@ -2,9 +2,10 @@
|
||||
Manual wire/waveguide routing (`Pather`)
|
||||
"""
|
||||
from typing import Self
|
||||
from collections.abc import Sequence, MutableMapping, Mapping
|
||||
from collections.abc import Sequence, MutableMapping, Mapping, Iterator
|
||||
import copy
|
||||
import logging
|
||||
from contextlib import contextmanager
|
||||
from pprint import pformat
|
||||
|
||||
import numpy
|
||||
@ -281,6 +282,37 @@ class Pather(Builder):
|
||||
self.tools[key] = tool
|
||||
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(
|
||||
self,
|
||||
portspec: str,
|
||||
|
@ -542,7 +542,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||
Return:
|
||||
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(
|
||||
self,
|
||||
|
@ -1225,6 +1225,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
inherit_name: bool = True,
|
||||
set_rotation: bool | None = None,
|
||||
append: bool = False,
|
||||
ok_connections: Iterable[tuple[str, str]] = (),
|
||||
) -> Self:
|
||||
"""
|
||||
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.
|
||||
Note that this does not flatten `other`, so its refs will still
|
||||
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:
|
||||
self
|
||||
@ -1300,6 +1306,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||
map_in,
|
||||
mirrored=mirrored,
|
||||
set_rotation=set_rotation,
|
||||
ok_connections=ok_connections,
|
||||
)
|
||||
|
||||
# get rid of plugged ports
|
||||
|
@ -419,6 +419,7 @@ class PortList(metaclass=ABCMeta):
|
||||
*,
|
||||
mirrored: bool = False,
|
||||
set_rotation: bool | None = None,
|
||||
ok_connections: Iterable[tuple[str, str]] = (),
|
||||
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||
"""
|
||||
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
|
||||
to indicate how much `other` should be rotated. Otherwise,
|
||||
`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:
|
||||
- The (x, y) translation (performed last)
|
||||
@ -451,6 +457,7 @@ class PortList(metaclass=ABCMeta):
|
||||
map_in=map_in,
|
||||
mirrored=mirrored,
|
||||
set_rotation=set_rotation,
|
||||
ok_connections=ok_connections,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@ -461,13 +468,14 @@ class PortList(metaclass=ABCMeta):
|
||||
*,
|
||||
mirrored: bool = False,
|
||||
set_rotation: bool | None = None,
|
||||
ok_connections: Iterable[tuple[str, str]] = (),
|
||||
) -> tuple[NDArray[numpy.float64], float, NDArray[numpy.float64]]:
|
||||
"""
|
||||
Given two sets of ports (s_ports and o_ports) and a mapping `map_in`
|
||||
specifying port connections, find the transform which will correctly
|
||||
align the specified o_ports onto their respective s_ports.
|
||||
|
||||
Args:t
|
||||
Args:
|
||||
s_ports: A list of stationary ports
|
||||
o_ports: A list of ports which are to be moved/mirrored.
|
||||
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
|
||||
to indicate how much `o_ports` should be rotated. Otherwise,
|
||||
`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:
|
||||
- The (x, y) translation (performed last)
|
||||
@ -502,7 +515,8 @@ class PortList(metaclass=ABCMeta):
|
||||
o_offsets[:, 1] *= -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)])
|
||||
if type_conflicts.any():
|
||||
msg = 'Ports have conflicting types:\n'
|
||||
@ -523,8 +537,8 @@ class PortList(metaclass=ABCMeta):
|
||||
if not numpy.allclose(rotations[:1], rotations):
|
||||
rot_deg = numpy.rad2deg(rotations)
|
||||
msg = 'Port orientations do not match:\n'
|
||||
for nn, (k, v) in enumerate(map_in.items()):
|
||||
msg += f'{k} | {rot_deg[nn]:g} | {v}\n'
|
||||
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||
msg += f'{kk} | {rot_deg[nn]:g} | {vv}\n'
|
||||
raise PortError(msg)
|
||||
|
||||
pivot = o_offsets[0].copy()
|
||||
@ -532,8 +546,8 @@ class PortList(metaclass=ABCMeta):
|
||||
translations = s_offsets - o_offsets
|
||||
if not numpy.allclose(translations[:1], translations):
|
||||
msg = 'Port translations do not match:\n'
|
||||
for nn, (k, v) in enumerate(map_in.items()):
|
||||
msg += f'{k} | {translations[nn]} | {v}\n'
|
||||
for nn, (kk, vv) in enumerate(map_in.items()):
|
||||
msg += f'{kk} | {translations[nn]} | {vv}\n'
|
||||
raise PortError(msg)
|
||||
|
||||
return translations[0], rotations[0], o_offsets[0]
|
||||
|
@ -233,7 +233,7 @@ class Arc(Shape):
|
||||
r0, r1 = self.radii
|
||||
|
||||
# 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user