Compare commits
2 Commits
path_impro
...
master
Author | SHA1 | Date | |
---|---|---|---|
94a1b3d793 | |||
7c7a7e916c |
@ -233,3 +233,5 @@ 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,12 +265,6 @@ 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 Iterable, Sequence, Mapping
|
from collections.abc import Sequence, Mapping
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
@ -226,7 +226,6 @@ 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`.
|
||||||
@ -261,11 +260,6 @@ 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
|
||||||
@ -299,7 +293,6 @@ 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,10 +2,9 @@
|
|||||||
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, Iterator
|
from collections.abc import Sequence, MutableMapping, Mapping
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
from contextlib import contextmanager
|
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
@ -282,37 +281,6 @@ 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 cast(list[str], list(TopologicalSorter(self.child_graph()).static_order()))
|
return list(TopologicalSorter(self.child_graph()).static_order())
|
||||||
|
|
||||||
def find_refs_local(
|
def find_refs_local(
|
||||||
self,
|
self,
|
||||||
|
@ -1225,7 +1225,6 @@ 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
|
||||||
@ -1271,11 +1270,6 @@ 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
|
||||||
@ -1306,7 +1300,6 @@ 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,7 +419,6 @@ 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,
|
||||||
@ -436,11 +435,6 @@ 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)
|
||||||
@ -457,7 +451,6 @@ 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
|
||||||
@ -468,14 +461,13 @@ 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:
|
Args:t
|
||||||
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
|
||||||
@ -487,11 +479,6 @@ 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)
|
||||||
@ -515,8 +502,7 @@ class PortList(metaclass=ABCMeta):
|
|||||||
o_offsets[:, 1] *= -1
|
o_offsets[:, 1] *= -1
|
||||||
o_rotations *= -1
|
o_rotations *= -1
|
||||||
|
|
||||||
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)
|
||||||
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'
|
||||||
@ -537,8 +523,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, (kk, vv) in enumerate(map_in.items()):
|
for nn, (k, v) in enumerate(map_in.items()):
|
||||||
msg += f'{kk} | {rot_deg[nn]:g} | {vv}\n'
|
msg += f'{k} | {rot_deg[nn]:g} | {v}\n'
|
||||||
raise PortError(msg)
|
raise PortError(msg)
|
||||||
|
|
||||||
pivot = o_offsets[0].copy()
|
pivot = o_offsets[0].copy()
|
||||||
@ -546,8 +532,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, (kk, vv) in enumerate(map_in.items()):
|
for nn, (k, v) in enumerate(map_in.items()):
|
||||||
msg += f'{kk} | {translations[nn]} | {vv}\n'
|
msg += f'{k} | {translations[nn]} | {v}\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 = cast(tuple[tuple[float, float], tuple[float, float]], self._angles_to_parameters())
|
a_ranges = self._angles_to_parameters()
|
||||||
|
|
||||||
# Approximate perimeter via numerical integration
|
# Approximate perimeter via numerical integration
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class Polygon(Shape):
|
|||||||
A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an
|
A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an
|
||||||
implicitly-closed boundary, and an offset.
|
implicitly-closed boundary, and an offset.
|
||||||
|
|
||||||
Note that the setter for `Polygon.vertices` may creates a copy of the
|
Note that the setter for `Polygon.vertices` creates a copy of the
|
||||||
passed vertex coordinates.
|
passed vertex coordinates.
|
||||||
|
|
||||||
A `normalized_form(...)` is available, but can be quite slow with lots of vertices.
|
A `normalized_form(...)` is available, but can be quite slow with lots of vertices.
|
||||||
@ -379,8 +379,9 @@ class Polygon(Shape):
|
|||||||
def normalized_form(self, norm_value: float) -> normalized_shape_tuple:
|
def normalized_form(self, norm_value: float) -> normalized_shape_tuple:
|
||||||
# Note: this function is going to be pretty slow for many-vertexed polygons, relative to
|
# Note: this function is going to be pretty slow for many-vertexed polygons, relative to
|
||||||
# other shapes
|
# other shapes
|
||||||
offset = self.vertices.mean(axis=0) + self.offset
|
meanv = self.vertices.mean(axis=0)
|
||||||
zeroed_vertices = self.vertices - offset
|
zeroed_vertices = self.vertices - meanv
|
||||||
|
offset = meanv + self.offset
|
||||||
|
|
||||||
scale = zeroed_vertices.std()
|
scale = zeroed_vertices.std()
|
||||||
normed_vertices = zeroed_vertices / scale
|
normed_vertices = zeroed_vertices / scale
|
||||||
|
Loading…
Reference in New Issue
Block a user