2023-10-07 01:54:16 -07:00
|
|
|
"""
|
|
|
|
Simplified Pattern assembly (`Builder`)
|
|
|
|
"""
|
2023-04-12 13:56:50 -07:00
|
|
|
from typing import Self, Sequence, Mapping, Literal, overload
|
2020-11-09 22:09:47 -08:00
|
|
|
import copy
|
|
|
|
import logging
|
|
|
|
|
|
|
|
from numpy import pi
|
2023-01-23 22:27:26 -08:00
|
|
|
from numpy.typing import ArrayLike
|
2020-11-09 22:09:47 -08:00
|
|
|
|
2023-04-06 16:52:01 -07:00
|
|
|
from ..pattern import Pattern
|
2023-01-21 21:22:11 -08:00
|
|
|
from ..ref import Ref
|
2023-04-07 18:08:42 -07:00
|
|
|
from ..library import ILibrary
|
2023-01-22 16:59:32 -08:00
|
|
|
from ..error import PortError, BuildError
|
2023-01-19 22:20:16 -08:00
|
|
|
from ..ports import PortList, Port
|
2023-01-24 12:45:44 -08:00
|
|
|
from ..abstract import Abstract
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2023-01-21 21:22:11 -08:00
|
|
|
class Builder(PortList):
|
2023-01-11 20:19:31 -08:00
|
|
|
"""
|
2023-10-07 01:45:52 -07:00
|
|
|
A `Builder` is a helper object used for snapping together multiple
|
|
|
|
lower-level patterns at their `Port`s.
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
The `Builder` mostly just holds context, in the form of a `Library`,
|
|
|
|
in addition to its underlying pattern. This simplifies some calls
|
|
|
|
to `plug` and `place`, by making the library implicit.
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
`Builder` can also be `set_dead()`, at which point further calls to `plug()`
|
|
|
|
and `place()` are ignored (intended for debugging).
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
|
|
|
|
Examples: Creating a Builder
|
2023-01-11 20:19:31 -08:00
|
|
|
===========================
|
2023-10-07 01:45:52 -07:00
|
|
|
- `Builder(library, ports={'A': port_a, 'C': port_c}, name='mypat')` makes
|
|
|
|
an empty pattern, adds the given ports, and places it into `library`
|
|
|
|
under the name `'mypat'`.
|
|
|
|
|
|
|
|
- `Builder(library)` makes an empty pattern with no ports. The pattern
|
|
|
|
is not added into `library` and must later be added with e.g.
|
|
|
|
`library['mypat'] = builder.pattern`
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
- `Builder(library, pattern=pattern, name='mypat')` uses an existing
|
|
|
|
pattern (including its ports) and sets `library['mypat'] = pattern`.
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
- `Builder.interface(other_pat, port_map=['A', 'B'], library=library)`
|
|
|
|
makes a new (empty) pattern, copies over ports 'A' and 'B' from
|
|
|
|
`other_pat`, and creates additional ports 'in_A' and 'in_B' facing
|
|
|
|
in the opposite directions. This can be used to build a device which
|
|
|
|
can plug into `other_pat` (using the 'in_*' ports) but which does not
|
|
|
|
itself include `other_pat` as a subcomponent.
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
- `Builder.interface(other_builder, ...)` does the same thing as
|
|
|
|
`Builder.interface(other_builder.pattern, ...)` but also uses
|
|
|
|
`other_builder.library` as its library by default.
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
|
|
|
|
Examples: Adding to a pattern
|
|
|
|
=============================
|
2023-01-11 20:19:31 -08:00
|
|
|
- `my_device.plug(subdevice, {'A': 'C', 'B': 'B'}, map_out={'D': 'myport'})`
|
|
|
|
instantiates `subdevice` into `my_device`, plugging ports 'A' and 'B'
|
|
|
|
of `my_device` into ports 'C' and 'B' of `subdevice`. The connected ports
|
|
|
|
are removed and any unconnected ports from `subdevice` are added to
|
|
|
|
`my_device`. Port 'D' of `subdevice` (unconnected) is renamed to 'myport'.
|
|
|
|
|
|
|
|
- `my_device.plug(wire, {'myport': 'A'})` places port 'A' of `wire` at 'myport'
|
|
|
|
of `my_device`. If `wire` has only two ports (e.g. 'A' and 'B'), no `map_out`,
|
|
|
|
argument is provided, and the `inherit_name` argument is not explicitly
|
|
|
|
set to `False`, the unconnected port of `wire` is automatically renamed to
|
|
|
|
'myport'. This allows easy extension of existing ports without changing
|
|
|
|
their names or having to provide `map_out` each time `plug` is called.
|
|
|
|
|
|
|
|
- `my_device.place(pad, offset=(10, 10), rotation=pi / 2, port_map={'A': 'gnd'})`
|
|
|
|
instantiates `pad` at the specified (x, y) offset and with the specified
|
|
|
|
rotation, adding its ports to those of `my_device`. Port 'A' of `pad` is
|
|
|
|
renamed to 'gnd' so that further routing can use this signal or net name
|
|
|
|
rather than the port name on the original `pad` device.
|
|
|
|
"""
|
2023-01-31 22:28:02 -08:00
|
|
|
__slots__ = ('pattern', 'library', '_dead')
|
2023-01-11 20:19:31 -08:00
|
|
|
|
|
|
|
pattern: Pattern
|
|
|
|
""" Layout of this device """
|
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
library: ILibrary
|
2023-01-21 21:22:11 -08:00
|
|
|
"""
|
2023-10-07 01:45:52 -07:00
|
|
|
Library from which patterns should be referenced
|
2023-01-21 21:22:11 -08:00
|
|
|
"""
|
2023-01-11 20:19:31 -08:00
|
|
|
|
|
|
|
_dead: bool
|
|
|
|
""" If True, plug()/place() are skipped (for debugging)"""
|
|
|
|
|
2023-01-24 23:25:10 -08:00
|
|
|
@property
|
2023-02-23 13:15:32 -08:00
|
|
|
def ports(self) -> dict[str, Port]:
|
2023-01-24 23:25:10 -08:00
|
|
|
return self.pattern.ports
|
|
|
|
|
|
|
|
@ports.setter
|
2023-02-23 13:15:32 -08:00
|
|
|
def ports(self, value: dict[str, Port]) -> None:
|
2023-01-24 23:25:10 -08:00
|
|
|
self.pattern.ports = value
|
|
|
|
|
2023-01-11 20:19:31 -08:00
|
|
|
def __init__(
|
|
|
|
self,
|
2023-10-07 01:45:52 -07:00
|
|
|
library: ILibrary,
|
2023-01-11 20:19:31 -08:00
|
|
|
*,
|
2023-02-23 13:15:32 -08:00
|
|
|
pattern: Pattern | None = None,
|
|
|
|
ports: str | Mapping[str, Port] | None = None,
|
|
|
|
name: str | None = None,
|
2023-01-11 20:19:31 -08:00
|
|
|
) -> None:
|
|
|
|
"""
|
2023-10-07 01:54:16 -07:00
|
|
|
Args:
|
|
|
|
library: The library from which referenced patterns will be taken
|
|
|
|
pattern: The pattern which will be modified by subsequent operations.
|
|
|
|
If `None` (default), a new pattern is created.
|
|
|
|
ports: Allows specifying the initial set of ports, if `pattern` does
|
|
|
|
not already have any ports (or is not provided). May be a string,
|
|
|
|
in which case it is interpreted as a name in `library`.
|
|
|
|
Default `None` (no ports).
|
|
|
|
name: If specified, `library[name]` is set to `self.pattern`.
|
2023-01-11 20:19:31 -08:00
|
|
|
"""
|
2023-02-04 09:05:34 -08:00
|
|
|
self._dead = False
|
2023-01-21 21:22:11 -08:00
|
|
|
self.library = library
|
2023-01-22 16:59:32 -08:00
|
|
|
if pattern is not None:
|
|
|
|
self.pattern = pattern
|
|
|
|
else:
|
|
|
|
self.pattern = Pattern()
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-01-22 16:59:32 -08:00
|
|
|
if ports is not None:
|
|
|
|
if self.pattern.ports:
|
|
|
|
raise BuildError('Ports supplied for pattern with pre-existing ports!')
|
2023-01-24 23:25:10 -08:00
|
|
|
if isinstance(ports, str):
|
|
|
|
ports = library.abstract(ports).ports
|
|
|
|
|
|
|
|
self.pattern.ports.update(copy.deepcopy(dict(ports)))
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-02-04 09:06:22 -08:00
|
|
|
if name is not None:
|
|
|
|
library[name] = self.pattern
|
2023-01-11 20:19:31 -08:00
|
|
|
|
2023-01-22 16:59:32 -08:00
|
|
|
@classmethod
|
|
|
|
def interface(
|
|
|
|
cls,
|
2023-02-23 13:15:32 -08:00
|
|
|
source: PortList | Mapping[str, Port] | str,
|
2023-01-22 16:59:32 -08:00
|
|
|
*,
|
2023-04-07 18:08:42 -07:00
|
|
|
library: ILibrary | None = None,
|
2023-01-11 20:19:31 -08:00
|
|
|
in_prefix: str = 'in_',
|
|
|
|
out_prefix: str = '',
|
2023-02-23 13:15:32 -08:00
|
|
|
port_map: dict[str, str] | Sequence[str] | None = None,
|
|
|
|
name: str | None = None,
|
2023-01-21 21:22:11 -08:00
|
|
|
) -> 'Builder':
|
2023-01-22 16:59:32 -08:00
|
|
|
"""
|
2023-10-07 01:45:52 -07:00
|
|
|
Wrapper for `Pattern.interface()`, which returns a Builder instead.
|
2023-01-22 16:59:32 -08:00
|
|
|
|
|
|
|
Args:
|
|
|
|
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
2023-10-07 01:45:52 -07:00
|
|
|
from which to create the interface. May be a pattern name if
|
|
|
|
`library` is provided.
|
|
|
|
library: Library from which existing patterns should be referenced,
|
|
|
|
and to which the new one should be added (if named). If not provided,
|
|
|
|
`source.library` must exist and will be used.
|
2023-01-22 16:59:32 -08:00
|
|
|
in_prefix: Prepended to port names for newly-created ports with
|
|
|
|
reversed directions compared to the current device.
|
|
|
|
out_prefix: Prepended to port names for ports which are directly
|
|
|
|
copied from the current device.
|
|
|
|
port_map: Specification for ports to copy into the new device:
|
|
|
|
- If `None`, all ports are copied.
|
|
|
|
- If a sequence, only the listed ports are copied
|
|
|
|
- If a mapping, the listed ports (keys) are copied and
|
|
|
|
renamed (to the values).
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
The new builder, with an empty pattern and 2x as many ports as
|
|
|
|
listed in port_map.
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
`PortError` if `port_map` contains port names not present in the
|
|
|
|
current device.
|
|
|
|
`PortError` if applying the prefixes results in duplicate port
|
|
|
|
names.
|
|
|
|
"""
|
|
|
|
if library is None:
|
2023-04-07 18:08:42 -07:00
|
|
|
if hasattr(source, 'library') and isinstance(source.library, ILibrary):
|
2023-01-22 16:59:32 -08:00
|
|
|
library = source.library
|
|
|
|
else:
|
2023-10-07 01:45:52 -07:00
|
|
|
raise BuildError('No library was given, and `source.library` does not have one either.')
|
2023-01-22 16:59:32 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
if isinstance(source, str):
|
|
|
|
source = library.abstract(source).ports
|
2023-01-22 16:59:32 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
pat = Pattern.interface(source, in_prefix=in_prefix, out_prefix=out_prefix, port_map=port_map)
|
|
|
|
new = Builder(library=library, pattern=pat, name=name)
|
2020-11-09 22:09:47 -08:00
|
|
|
return new
|
|
|
|
|
2022-02-23 11:27:11 -08:00
|
|
|
def plug(
|
2023-02-23 13:37:34 -08:00
|
|
|
self,
|
2023-04-11 19:57:09 -07:00
|
|
|
other: Abstract | str | Pattern,
|
2023-02-23 13:15:32 -08:00
|
|
|
map_in: dict[str, str],
|
|
|
|
map_out: dict[str, str | None] | None = None,
|
2022-02-23 11:27:11 -08:00
|
|
|
*,
|
2023-04-14 22:19:56 -07:00
|
|
|
mirrored: bool = False,
|
2022-02-23 11:27:11 -08:00
|
|
|
inherit_name: bool = True,
|
2023-02-23 13:15:32 -08:00
|
|
|
set_rotation: bool | None = None,
|
2023-04-11 19:57:09 -07:00
|
|
|
append: bool = False,
|
2023-02-23 13:37:34 -08:00
|
|
|
) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
2023-10-07 01:45:52 -07:00
|
|
|
Wrapper around `Pattern.plug` which allows a string for `other`.
|
|
|
|
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
|
|
|
one is passed with `append=True`).
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Args:
|
2023-10-07 01:45:52 -07:00
|
|
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
2023-02-23 13:15:32 -08:00
|
|
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
2020-11-09 22:09:47 -08:00
|
|
|
port connections between the two devices.
|
2023-02-23 13:15:32 -08:00
|
|
|
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
|
2020-11-09 22:09:47 -08:00
|
|
|
new names for ports in `other`.
|
2023-10-07 01:45:52 -07:00
|
|
|
mirrored: Enables mirroring `other` across the x axis prior to
|
|
|
|
connecting any ports.
|
2020-11-09 22:09:47 -08:00
|
|
|
inherit_name: If `True`, and `map_in` specifies only a single port,
|
|
|
|
and `map_out` is `None`, and `other` has only two ports total,
|
|
|
|
then automatically renames the output port of `other` to the
|
|
|
|
name of the port from `self` that appears in `map_in`. This
|
|
|
|
makes it easy to extend a device with simple 2-port devices
|
|
|
|
(e.g. wires) without providing `map_out` each time `plug` is
|
|
|
|
called. See "Examples" above for more info. Default `True`.
|
|
|
|
set_rotation: If the necessary rotation cannot be determined from
|
|
|
|
the ports being connected (i.e. all pairs have at least one
|
|
|
|
port with `rotation=None`), `set_rotation` must be provided
|
|
|
|
to indicate how much `other` should be rotated. Otherwise,
|
|
|
|
`set_rotation` must remain `None`.
|
2023-10-07 01:45:52 -07:00
|
|
|
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`).
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
|
|
|
|
Raises:
|
2023-01-22 16:59:32 -08:00
|
|
|
`PortError` if any ports specified in `map_in` or `map_out` do not
|
2020-11-09 22:09:47 -08:00
|
|
|
exist in `self.ports` or `other_names`.
|
2023-01-22 16:59:32 -08:00
|
|
|
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
2020-11-09 22:09:47 -08:00
|
|
|
are applied.
|
2023-01-22 16:59:32 -08:00
|
|
|
`PortError` if the specified port mapping is not achieveable (the ports
|
2020-11-09 22:09:47 -08:00
|
|
|
do not line up)
|
|
|
|
"""
|
|
|
|
if self._dead:
|
|
|
|
logger.error('Skipping plug() since device is dead')
|
|
|
|
return self
|
|
|
|
|
2023-01-25 23:19:25 -08:00
|
|
|
if isinstance(other, str):
|
|
|
|
other = self.library.abstract(other)
|
2023-10-07 01:45:52 -07:00
|
|
|
if append and isinstance(other, Abstract):
|
|
|
|
other = self.library[other.name]
|
2023-01-25 23:19:25 -08:00
|
|
|
|
2023-10-07 01:45:52 -07:00
|
|
|
self.pattern.plug(
|
|
|
|
other=other,
|
|
|
|
map_in=map_in,
|
|
|
|
map_out=map_out,
|
2023-01-21 21:22:11 -08:00
|
|
|
mirrored=mirrored,
|
2023-10-07 01:45:52 -07:00
|
|
|
inherit_name=inherit_name,
|
2023-01-21 21:22:11 -08:00
|
|
|
set_rotation=set_rotation,
|
2023-10-07 01:45:52 -07:00
|
|
|
append=append,
|
2023-01-21 21:22:11 -08:00
|
|
|
)
|
2020-11-09 22:09:47 -08:00
|
|
|
return self
|
|
|
|
|
2023-04-11 19:57:09 -07:00
|
|
|
def place(
|
|
|
|
self,
|
|
|
|
other: Abstract | str | Pattern,
|
|
|
|
*,
|
2022-02-23 15:47:38 -08:00
|
|
|
offset: ArrayLike = (0, 0),
|
2022-02-23 11:27:11 -08:00
|
|
|
rotation: float = 0,
|
2022-02-23 15:47:38 -08:00
|
|
|
pivot: ArrayLike = (0, 0),
|
2023-04-14 22:19:56 -07:00
|
|
|
mirrored: bool = False,
|
2023-02-23 13:15:32 -08:00
|
|
|
port_map: dict[str, str | None] | None = None,
|
2022-02-23 11:27:11 -08:00
|
|
|
skip_port_check: bool = False,
|
2023-04-11 19:57:09 -07:00
|
|
|
append: bool = False,
|
2023-02-23 13:37:34 -08:00
|
|
|
) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
2023-10-07 01:54:16 -07:00
|
|
|
Wrapper around `Pattern.place` which allows a string for `other`.
|
|
|
|
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
|
|
|
one is passed with `append=True`).
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Args:
|
2023-10-07 01:54:16 -07:00
|
|
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
2022-07-07 11:27:29 -07:00
|
|
|
offset: Offset at which to place the instance. Default (0, 0).
|
|
|
|
rotation: Rotation applied to the instance before placement. Default 0.
|
2020-11-09 22:09:47 -08:00
|
|
|
pivot: Rotation is applied around this pivot point (default (0, 0)).
|
|
|
|
Rotation is applied prior to translation (`offset`).
|
2023-10-07 01:54:16 -07:00
|
|
|
mirrored: Whether theinstance should be mirrored across the x axis.
|
2020-11-09 22:09:47 -08:00
|
|
|
Mirroring is applied before translation and rotation.
|
2023-02-23 13:15:32 -08:00
|
|
|
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
2022-07-07 11:27:29 -07:00
|
|
|
new names for ports in the instantiated device. New names can be
|
|
|
|
`None`, which will delete those ports.
|
2020-11-09 22:09:47 -08:00
|
|
|
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
|
|
|
in case it has already been performed elsewhere.
|
2023-10-07 01:54:16 -07:00
|
|
|
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`).
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
|
|
|
|
Raises:
|
2023-01-22 16:59:32 -08:00
|
|
|
`PortError` if any ports specified in `map_in` or `map_out` do not
|
2023-04-11 19:57:09 -07:00
|
|
|
exist in `self.ports` or `other.ports`.
|
2023-01-22 16:59:32 -08:00
|
|
|
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
2020-11-09 22:09:47 -08:00
|
|
|
are applied.
|
|
|
|
"""
|
|
|
|
if self._dead:
|
|
|
|
logger.error('Skipping place() since device is dead')
|
|
|
|
return self
|
|
|
|
|
2023-01-25 23:19:25 -08:00
|
|
|
if isinstance(other, str):
|
|
|
|
other = self.library.abstract(other)
|
2023-10-07 01:45:52 -07:00
|
|
|
if append and isinstance(other, Abstract):
|
|
|
|
other = self.library[other.name]
|
|
|
|
|
|
|
|
self.pattern.place(
|
|
|
|
other=other,
|
|
|
|
offset=offset,
|
|
|
|
rotation=rotation,
|
|
|
|
pivot=pivot,
|
|
|
|
mirrored=mirrored,
|
|
|
|
port_map=port_map,
|
|
|
|
skip_port_check=skip_port_check,
|
|
|
|
append=append,
|
|
|
|
)
|
2020-11-09 22:09:47 -08:00
|
|
|
return self
|
|
|
|
|
2023-02-23 13:37:34 -08:00
|
|
|
def translate(self, offset: ArrayLike) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
|
|
|
Translate the pattern and all ports.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
offset: (x, y) distance to translate by
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
"""
|
|
|
|
self.pattern.translate_elements(offset)
|
|
|
|
return self
|
|
|
|
|
2023-02-23 13:37:34 -08:00
|
|
|
def rotate_around(self, pivot: ArrayLike, angle: float) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
2023-01-11 20:19:31 -08:00
|
|
|
Rotate the pattern and all ports.
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Args:
|
2023-01-11 20:19:31 -08:00
|
|
|
angle: angle (radians, counterclockwise) to rotate by
|
|
|
|
pivot: location to rotate around
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
"""
|
|
|
|
self.pattern.rotate_around(pivot, angle)
|
|
|
|
for port in self.ports.values():
|
|
|
|
port.rotate_around(pivot, angle)
|
|
|
|
return self
|
|
|
|
|
2023-04-14 22:19:56 -07:00
|
|
|
def mirror(self, axis: int = 0) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
2023-01-11 20:19:31 -08:00
|
|
|
Mirror the pattern and all ports across the specified axis.
|
2020-11-09 22:09:47 -08:00
|
|
|
|
|
|
|
Args:
|
|
|
|
axis: Axis to mirror across (x=0, y=1)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
"""
|
|
|
|
self.pattern.mirror(axis)
|
|
|
|
return self
|
|
|
|
|
2023-02-23 13:37:34 -08:00
|
|
|
def set_dead(self) -> Self:
|
2020-11-09 22:09:47 -08:00
|
|
|
"""
|
|
|
|
Disallows further changes through `plug()` or `place()`.
|
|
|
|
This is meant for debugging:
|
|
|
|
```
|
|
|
|
dev.plug(a, ...)
|
|
|
|
dev.set_dead() # added for debug purposes
|
|
|
|
dev.plug(b, ...) # usually raises an error, but now skipped
|
|
|
|
dev.plug(c, ...) # also skipped
|
|
|
|
dev.pattern.visualize() # shows the device as of the set_dead() call
|
|
|
|
```
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
self
|
|
|
|
"""
|
|
|
|
self._dead = True
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
2023-10-07 01:50:22 -07:00
|
|
|
s = f'<Builder {self.pattern} L({len(self.library)})>'
|
2020-11-09 22:09:47 -08:00
|
|
|
return s
|
|
|
|
|
2023-01-31 22:28:02 -08:00
|
|
|
|