[Builder / RenderPather] BREAKING remove aliases to old names

This commit is contained in:
Jan Petykiewicz 2026-04-08 23:08:26 -07:00
commit 778b3d9be7
18 changed files with 131 additions and 153 deletions

View file

@ -18,11 +18,17 @@ The biggest migration point is that the old routing verbs were renamed:
| `Pather.path(...)` | `Pather.trace(...)` |
| `Pather.path_to(...)` | `Pather.trace_to(...)` |
| `Pather.mpath(...)` | `Pather.trace(...)` / `Pather.trace_to(...)` with multiple ports |
| `Pather.pathS(...)` | `Pather.jog(...)` |
| `Pather.pathU(...)` | `Pather.uturn(...)` |
| `Pather.path_into(...)` | `Pather.trace_into(...)` |
| `RenderPather.path(...)` | `RenderPather.trace(...)` |
| `RenderPather.path_to(...)` | `RenderPather.trace_to(...)` |
| `RenderPather.mpath(...)` | `RenderPather.trace(...)` / `RenderPather.trace_to(...)` |
| `RenderPather.path_into(...)` | `RenderPather.trace_into(...)` |
| `Pather.path_from(src, dst)` | `Pather.at(src).trace_into(dst)` |
| `RenderPather.path(...)` | `Pather(..., auto_render=False).trace(...)` |
| `RenderPather.path_to(...)` | `Pather(..., auto_render=False).trace_to(...)` |
| `RenderPather.mpath(...)` | `Pather(..., auto_render=False).trace(...)` / `Pather(..., auto_render=False).trace_to(...)` |
| `RenderPather.pathS(...)` | `Pather(..., auto_render=False).jog(...)` |
| `RenderPather.pathU(...)` | `Pather(..., auto_render=False).uturn(...)` |
| `RenderPather.path_into(...)` | `Pather(..., auto_render=False).trace_into(...)` |
| `RenderPather.path_from(src, dst)` | `Pather(..., auto_render=False).at(src).trace_into(dst)` |
There are also new convenience wrappers:
@ -43,13 +49,19 @@ that still calls `pather.path(...)` must be renamed.
pather.path('VCC', False, 6_000)
pather.path_to('VCC', None, x=0)
pather.mpath(['GND', 'VCC'], True, xmax=-10_000, spacing=5_000)
pather.pathS('VCC', offset=-2_000, length=8_000)
pather.pathU('VCC', offset=4_000, length=5_000)
pather.path_into('src', 'dst')
pather.path_from('src', 'dst')
# new
pather.cw('VCC', 6_000)
pather.straight('VCC', x=0)
pather.ccw(['GND', 'VCC'], xmax=-10_000, spacing=5_000)
pather.jog('VCC', offset=-2_000, length=8_000)
pather.uturn('VCC', offset=4_000, length=5_000)
pather.trace_into('src', 'dst')
pather.at('src').trace_into('dst')
```
If you prefer the more explicit spelling, `trace(...)` and `trace_to(...)`
@ -73,11 +85,30 @@ Routing can now be written in a fluent style via `.at(...)`, which returns a
```
This is additive, not required for migration. Existing code can stay with the
non-fluent `Pather`/`RenderPather` methods after renaming the verbs above.
non-fluent `Pather` methods after renaming the verbs above.
Old `PortPather` helper names were also cleaned up:
| Old API | New API |
| --- | --- |
| `save_copy(...)` | `mark(...)` |
| `rename_to(...)` | `rename(...)` |
Example:
```python
# old
pp.save_copy('branch')
pp.rename_to('feed')
# new
pp.mark('branch')
pp.rename('feed')
```
## Imports and module layout
`Builder`, `Pather`, and `RenderPather` now live together in
`Pather` now provides the remaining builder/routing surface in
`masque/builder/pather.py`. The old module files
`masque/builder/builder.py` and `masque/builder/renderpather.py` were removed.
@ -89,14 +120,17 @@ from masque.builder.builder import Builder
from masque.builder.renderpather import RenderPather
# new
from masque.builder import Builder, RenderPather
from masque.builder import Pather
builder = Pather(...)
deferred = Pather(..., auto_render=False)
```
Top-level imports from `masque` also continue to work.
`Builder` is now a thin compatibility wrapper over the unified `Pather`
implementation with `auto_render=True`. `RenderPather` is the same wrapper with
`auto_render=False`.
`Pather` now defaults to `auto_render=True`, so plain construction replaces the
old `Builder` behavior. Use `Pather(..., auto_render=False)` where you
previously used `RenderPather`.
## `BasicTool` was replaced

View file

@ -145,7 +145,7 @@ References are accomplished by listing the target's name, not its `Pattern` obje
in order to create a reference, but they also need to access the pattern's ports.
* One way to provide this data is through an `Abstract`, generated via
`Library.abstract()` or through a `Library.abstract_view()`.
* Another way is use `Builder.place()` or `Builder.plug()`, which automatically creates
* Another way is use `Pather.place()` or `Pather.plug()`, which automatically creates
an `Abstract` from its internally-referenced `Library`.
@ -193,8 +193,8 @@ my_pattern.ref(new_name, ...) # instantiate the cell
# In practice, you may do lots of
my_pattern.ref(lib << make_tree(...), ...)
# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Builder(library=lib, ...)
# With a `Pather` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Pather(library=lib, ...)
...
my_builder.place(make_tree(...))
```

View file

@ -17,7 +17,7 @@ Contents
* Build hierarchical photonic-crystal example devices
* Reference other patterns
* Add ports to a pattern
* Use `Builder` to snap ports together into a circuit
* Use `Pather` to snap ports together into a circuit
* Check for dangling references
- [library](library.py)
* Continue from `devices.py` using a lazy library
@ -29,7 +29,7 @@ Contents
* Use `AutoTool` to generate paths
* Use `AutoTool` to automatically transition between path types
- [renderpather](renderpather.py)
* Use `RenderPather` and `PathTool` to build a layout similar to the one in [pather](pather.py),
* Use `Pather(auto_render=False)` and `PathTool` to build a layout similar to the one in [pather](pather.py),
but using `Path` shapes instead of `Polygon`s.
- [port_pather](port_pather.py)
* Use `PortPather` and the `.at()` syntax for more concise routing

View file

@ -1,5 +1,5 @@
"""
Tutorial: building hierarchical devices with `Pattern`, `Port`, and `Builder`.
Tutorial: building hierarchical devices with `Pattern`, `Port`, and `Pather`.
This file uses photonic-crystal components as the concrete example, so some of
the geometry-generation code is domain-specific. The tutorial value is in the
@ -12,7 +12,7 @@ import numpy
from numpy import pi
from masque import (
layer_t, Pattern, Ref, Builder, Port, Polygon,
layer_t, Pattern, Ref, Pather, Port, Polygon,
Library,
)
from masque.utils import ports2data
@ -261,8 +261,8 @@ def main(interactive: bool = True) -> None:
#
# Build a circuit
#
# Create a `Builder`, and register the resulting top cell as "my_circuit".
circ = Builder(library=lib, name='my_circuit')
# Create a `Pather`, and register the resulting top cell as "my_circuit".
circ = Pather(library=lib, name='my_circuit')
# Start by placing a waveguide and renaming its ports to match the circuit-level
# names we want to use while assembling the design.
@ -278,7 +278,7 @@ def main(interactive: bool = True) -> None:
# lib['my_circuit'] = circ_pat
# circ_pat.place(lib.abstract('wg10'), ...)
# circ_pat.plug(lib.abstract('wg10'), ...)
# but `Builder` removes some repeated `lib.abstract(...)` boilerplate and keeps
# but `Pather` removes some repeated `lib.abstract(...)` boilerplate and keeps
# the assembly code focused on port-level intent.
# Attach a y-splitter to the signal path.

View file

@ -1,5 +1,5 @@
"""
Tutorial: using `LazyLibrary` and `Builder.interface()`.
Tutorial: using `LazyLibrary` and `Pather.interface()`.
This example assumes you have already read `devices.py` and generated the
`circuit.gds` file it writes. The goal here is not the photonic-crystal geometry
@ -10,7 +10,7 @@ from typing import Any
from pprint import pformat
from masque import Builder, LazyLibrary
from masque import Pather, LazyLibrary
from masque.file.gdsii import writefile, load_libraryfile
import basic_shapes
@ -64,10 +64,10 @@ def main() -> None:
# Start a new design by copying the ports from an existing library cell.
# This gives `circ2` the same external interface as `tri_l3cav`.
circ2 = Builder(library=lib, ports='tri_l3cav')
circ2 = Pather(library=lib, ports='tri_l3cav')
# First way to specify what we are plugging in: request an explicit abstract.
# This works with `Pattern` methods directly as well as with `Builder`.
# This works with `Pattern` methods directly as well as with `Pather`.
circ2.plug(lib.abstract('wg10'), {'input': 'right'})
# Second way: use an `AbstractView`, which behaves like a mapping of names
@ -75,7 +75,7 @@ def main() -> None:
abstracts = lib.abstract_view()
circ2.plug(abstracts['wg10'], {'output': 'left'})
# Third way: let `Builder` resolve a pattern name through its own library.
# Third way: let `Pather` resolve a pattern name through its own library.
# This shorthand is convenient, but it is specific to helpers that already
# carry a library reference.
circ2.plug('tri_wg10', {'input': 'right'})
@ -89,10 +89,10 @@ def main() -> None:
# Build a second device that is explicitly designed to mate with `circ2`.
#
# `Builder.interface()` makes a new pattern whose ports mirror an existing
# `Pather.interface()` makes a new pattern whose ports mirror an existing
# design's external interface. That is useful when you want to design an
# adapter, continuation, or mating structure.
circ3 = Builder.interface(source=circ2)
circ3 = Pather.interface(source=circ2)
# Continue routing outward from those inherited ports.
circ3.plug('tri_bend0', {'input': 'right'})

View file

@ -204,9 +204,9 @@ def prepare_tools() -> tuple[Library, Tool, Tool]:
#
# Now we can start building up our library (collection of static cells) and pathing tools.
#
# If any of the operations below are confusing, you can cross-reference against the `RenderPather`
# tutorial, which handles some things more explicitly (e.g. via placement) and simplifies others
# (e.g. geometry definition).
# If any of the operations below are confusing, you can cross-reference against the deferred
# `Pather` tutorial, which handles some things more explicitly (e.g. via placement) and simplifies
# others (e.g. geometry definition).
#
def main() -> None:
library, M1_tool, M2_tool = prepare_tools()

View file

@ -1,7 +1,7 @@
"""
PortPather tutorial: Using .at() syntax
"""
from masque import RenderPather, Pattern, Port, R90
from masque import Pather, Pattern, Port, R90
from masque.file.gdsii import writefile
from basic_shapes import GDS_OPTS
@ -12,8 +12,8 @@ def main() -> None:
# Reuse the same patterns (pads, bends, vias) and tools as in pather.py
library, M1_tool, M2_tool = prepare_tools()
# Create a RenderPather and place some initial pads (same as Pather tutorial)
rpather = RenderPather(library, tools=M2_tool)
# Create a deferred Pather and place some initial pads (same as Pather tutorial)
rpather = Pather(library, tools=M2_tool, auto_render=False)
rpather.place('pad', offset=(18_000, 30_000), port_map={'wire_port': 'VCC'})
rpather.place('pad', offset=(18_000, 60_000), port_map={'wire_port': 'GND'})
@ -156,7 +156,7 @@ def main() -> None:
#
# Rendering and Saving
#
# Since we used RenderPather, we must call .render() to generate the geometry.
# Since we deferred auto-rendering, we must call .render() to generate the geometry.
rpather.render()
library['PortPather_Tutorial'] = rpather.pattern

View file

@ -1,7 +1,7 @@
"""
Manual wire routing tutorial: RenderPather an PathTool
Manual wire routing tutorial: deferred Pather and PathTool
"""
from masque import RenderPather, Library
from masque import Pather, Library
from masque.builder.tools import PathTool
from masque.file.gdsii import writefile
@ -11,9 +11,9 @@ from pather import M1_WIDTH, V1_WIDTH, M2_WIDTH, map_layer, make_pad, make_via
def main() -> None:
#
# To illustrate the advantages of using `RenderPather`, we use `PathTool` instead
# To illustrate deferred routing with `Pather`, we use `PathTool` instead
# of `AutoTool`. `PathTool` lacks some sophistication (e.g. no automatic transitions)
# but when used with `RenderPather`, it can consolidate multiple routing steps into
# but when used with `Pather(auto_render=False)`, it can consolidate multiple routing steps into
# a single `Path` shape.
#
# We'll try to nearly replicate the layout from the `Pather` tutorial; see `pather.py`
@ -39,7 +39,7 @@ def main() -> None:
# and what port type to present.
M1_ptool = PathTool(layer='M1', width=M1_WIDTH, ptype='m1wire')
M2_ptool = PathTool(layer='M2', width=M2_WIDTH, ptype='m2wire')
rpather = RenderPather(tools=M2_ptool, library=library)
rpather = Pather(tools=M2_ptool, library=library, auto_render=False)
# As in the pather tutorial, we make some pads and labels...
rpather.place('pad', offset=(18_000, 30_000), port_map={'wire_port': 'VCC'})
@ -85,7 +85,7 @@ def main() -> None:
# Render the path we defined
rpather.render()
library['RenderPather_and_PathTool'] = rpather.pattern
library['Deferred_Pather_and_PathTool'] = rpather.pattern
# Convert from text-based layers to numeric layers for GDS, and output the file

View file

@ -73,10 +73,8 @@ from .ports import (
)
from .abstract import Abstract as Abstract
from .builder import (
Builder as Builder,
Tool as Tool,
Pather as Pather,
RenderPather as RenderPather,
RenderStep as RenderStep,
SimpleTool as SimpleTool,
AutoTool as AutoTool,

View file

@ -1,8 +1,6 @@
from .pather import (
Pather as Pather,
PortPather as PortPather,
Builder as Builder,
RenderPather as RenderPather,
)
from .utils import ell as ell
from .tools import (

View file

@ -1,5 +1,5 @@
"""
Logging and operation decorators for Builder/Pather
Logging and operation decorators for Pather
"""
from typing import TYPE_CHECKING, Any
from collections.abc import Iterator, Sequence, Callable
@ -31,7 +31,7 @@ def _format_log_args(**kwargs) -> str:
class PatherLogger:
"""
Encapsulates state for Pather/Builder diagnostic logging.
Encapsulates state for Pather diagnostic logging.
"""
debug: bool
indent: int
@ -90,7 +90,7 @@ def logged_op(
portspec_getter: Callable[[dict[str, Any]], str | Sequence[str] | None] | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""
Decorator to wrap Builder methods with logging.
Decorator to wrap Pather methods with logging.
"""
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
sig = inspect.signature(func)

View file

@ -38,11 +38,9 @@ class Pather(PortList):
The `Pather` holds context in the form of a `Library`, its underlying
pattern, and a set of `Tool`s for generating routing segments.
Routing operations (`trace`, `jog`, `uturn`, etc.) are by default
deferred: they record the intended path but do not immediately generate
geometry. `render()` must be called to generate the final layout.
Alternatively, setting `auto_render=True` in the constructor will
cause geometry to be generated incrementally after each routing step.
Routing operations (`trace`, `jog`, `uturn`, etc.) are rendered
incrementally by default. Set `auto_render=False` to defer geometry
generation until an explicit call to `render()`.
Examples: Creating a Pather
===========================
@ -58,8 +56,8 @@ class Pather(PortList):
connects port 'A' of the current pattern to port 'C' of `subdevice`.
- `pather.trace('my_port', ccw=True, length=100)` plans a 100-unit bend
starting at 'my_port'. If `auto_render=True`, geometry is added
immediately. Otherwise, call `pather.render()` later.
starting at 'my_port'. Geometry is added immediately by default.
Set `auto_render=False` to defer and call `pather.render()` later.
"""
__slots__ = (
'pattern', 'library', 'tools', 'paths',
@ -118,7 +116,7 @@ class Pather(PortList):
tools: Tool | MutableMapping[str | None, Tool] | None = None,
name: str | None = None,
debug: bool = False,
auto_render: bool = False,
auto_render: bool = True,
auto_render_append: bool = True,
) -> None:
"""
@ -1358,53 +1356,3 @@ class PortPather:
self.pather.rename_ports({name: None})
self.ports = [pp for pp in self.ports if pp != name]
return self
class Builder(Pather):
"""
Backward-compatible wrapper for Pather with auto_render=True.
"""
def __init__(
self,
library: ILibrary,
*,
pattern: Pattern | None = None,
ports: str | Mapping[str, Port] | None = None,
tools: Tool | MutableMapping[str | None, Tool] | None = None,
name: str | None = None,
debug: bool = False,
) -> None:
super().__init__(
library=library,
pattern=pattern,
ports=ports,
tools=tools,
name=name,
debug=debug,
auto_render=True,
)
class RenderPather(Pather):
"""
Backward-compatible wrapper for Pather with auto_render=False.
"""
def __init__(
self,
library: ILibrary,
*,
pattern: Pattern | None = None,
ports: str | Mapping[str, Port] | None = None,
tools: Tool | MutableMapping[str | None, Tool] | None = None,
name: str | None = None,
debug: bool = False,
) -> None:
super().__init__(
library=library,
pattern=pattern,
ports=ports,
tools=tools,
name=name,
debug=debug,
auto_render=False,
)

View file

@ -25,8 +25,8 @@ from ..error import BuildError
@dataclass(frozen=True, slots=True)
class RenderStep:
"""
Representation of a single saved operation, used by `RenderPather` and passed
to `Tool.render()` when `RenderPather.render()` is called.
Representation of a single saved operation, used by deferred `Pather`
instances and passed to `Tool.render()` when `Pather.render()` is called.
"""
opcode: Literal['L', 'S', 'U', 'P']
""" What operation is being performed.
@ -128,7 +128,7 @@ class Tool:
Create a wire or waveguide that travels exactly `length` distance along the axis
of its input port.
Used by `Pather` and `RenderPather`.
Used by `Pather`.
The output port must be exactly `length` away along the input port's axis, but
may be placed an additional (unspecified) distance away along the perpendicular
@ -174,7 +174,7 @@ class Tool:
of its input port, and `jog` distance on the perpendicular axis.
`jog` is positive when moving left of the direction of travel (from input to ouput port).
Used by `Pather` and `RenderPather`.
Used by `Pather`.
The output port should be rotated to face the input port (i.e. plugging the device
into a port will move that port but keep its orientation).
@ -214,7 +214,7 @@ class Tool:
Plan a wire or waveguide that travels exactly `length` distance along the axis
of its input port.
Used by `RenderPather`.
Used by `Pather` when `auto_render=False`.
The output port must be exactly `length` away along the input port's axis, but
may be placed an additional (unspecified) distance away along the perpendicular
@ -266,7 +266,7 @@ class Tool:
Plan a wire or waveguide that travels exactly `length` distance along the axis
of its input port and `jog` distance along the perpendicular axis (i.e. an S-bend).
Used by `RenderPather`.
Used by `Pather` when `auto_render=False`.
The output port must have an orientation rotated by pi from the input port.
@ -315,7 +315,7 @@ class Tool:
Create a wire or waveguide that travels exactly `jog` distance along the axis
perpendicular to its input port (i.e. a U-bend).
Used by `Pather` and `RenderPather`. Tools may leave this unimplemented if they
Used by `Pather`. Tools may leave this unimplemented if they
do not support a native U-bend primitive.
The output port must have an orientation identical to the input port.
@ -354,7 +354,7 @@ class Tool:
Plan a wire or waveguide that travels exactly `jog` distance along the axis
perpendicular to its input port (i.e. a U-bend).
Used by `RenderPather`. This is an optional native-planning hook: tools may
Used by `Pather` when `auto_render=False`. This is an optional native-planning hook: tools may
implement it when they can represent a U-turn directly, otherwise they may rely
on `traceU()` or let `Pather` synthesize the route from simpler primitives.
@ -1323,7 +1323,7 @@ class PathTool(Tool, metaclass=ABCMeta):
# Transform the batch so the first port is local (at 0,0) but retains its global rotation.
# This allows the path to be rendered with its original orientation, simplified by
# translation to the origin. RenderPather.render will handle the final placement
# translation to the origin. Pather.render will handle the final placement
# (including rotation alignment) via `pat.plug`.
first_port = batch[0].start_port
translation = -first_port.offset

View file

@ -38,8 +38,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
or provide equivalent functions.
`Pattern` also stores a dict of `Port`s, which can be used to "snap" together points.
See `Pattern.plug()` and `Pattern.place()`, as well as the helper classes
`builder.Builder`, `builder.Pather`, `builder.RenderPather`, and `ports.PortsList`.
See `Pattern.plug()` and `Pattern.place()`, as well as `builder.Pather`
and `ports.PortsList`.
For convenience, ports can be read out using square brackets:
- `pattern['A'] == Port((0, 0), 0)`
@ -1664,7 +1664,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
current device.
Args:
source: A collection of ports (e.g. Pattern, Builder, or dict)
source: A collection of ports (e.g. Pattern, Pather, or dict)
from which to create the interface.
in_prefix: Prepended to port names for newly-created ports with
reversed directions compared to the current device.

View file

@ -6,7 +6,7 @@ from masque.builder.tools import AutoTool
from masque.pattern import Pattern
from masque.ports import Port
from masque.library import Library
from masque.builder.pather import Pather, RenderPather
from masque.builder.pather import Pather
def make_straight(length, width=2, ptype="wire"):
pat = Pattern()
@ -166,7 +166,7 @@ def test_autotool_planS_pure_sbend_with_transition_dx() -> None:
def test_renderpather_autotool_double_L(multi_bend_tool) -> None:
tool, lib = multi_bend_tool
rp = RenderPather(lib, tools=tool)
rp = Pather(lib, tools=tool, auto_render=False)
rp.ports["A"] = Port((0,0), 0, ptype="wire")
# This should trigger double-L fallback in planS

View file

@ -3,7 +3,7 @@ import pytest
from numpy.testing import assert_equal, assert_allclose
from numpy import pi
from ..builder import Builder
from ..builder import Pather
from ..builder.utils import ell
from ..error import BuildError
from ..library import Library
@ -13,7 +13,7 @@ from ..ports import Port
def test_builder_init() -> None:
lib = Library()
b = Builder(lib, name="mypat")
b = Pather(lib, name="mypat")
assert b.pattern is lib["mypat"]
assert b.library is lib
@ -24,7 +24,7 @@ def test_builder_place() -> None:
child.ports["A"] = Port((0, 0), 0)
lib["child"] = child
b = Builder(lib)
b = Pather(lib)
b.place("child", offset=(10, 20), port_map={"A": "child_A"})
assert "child_A" in b.ports
@ -40,7 +40,7 @@ def test_builder_plug() -> None:
wire.ports["out"] = Port((10, 0), pi)
lib["wire"] = wire
b = Builder(lib)
b = Pather(lib)
b.ports["start"] = Port((100, 100), 0)
# Plug wire's "in" port into builder's "start" port
@ -64,7 +64,7 @@ def test_builder_interface() -> None:
source.ports["P1"] = Port((0, 0), 0)
lib["source"] = source
b = Builder.interface("source", library=lib, name="iface")
b = Pather.interface("source", library=lib, name="iface")
assert "in_P1" in b.ports
assert "P1" in b.ports
assert b.pattern is lib["iface"]
@ -73,7 +73,7 @@ def test_builder_interface() -> None:
def test_builder_set_dead() -> None:
lib = Library()
lib["sub"] = Pattern()
b = Builder(lib)
b = Pather(lib)
b.set_dead()
b.place("sub")
@ -84,7 +84,7 @@ def test_builder_dead_ports() -> None:
lib = Library()
pat = Pattern()
pat.ports['A'] = Port((0, 0), 0)
b = Builder(lib, pattern=pat)
b = Pather(lib, pattern=pat)
b.set_dead()
# Attempt to plug a device where ports don't line up
@ -107,7 +107,7 @@ def test_dead_plug_best_effort() -> None:
lib = Library()
pat = Pattern()
pat.ports['A'] = Port((0, 0), 0)
b = Builder(lib, pattern=pat)
b = Pather(lib, pattern=pat)
b.set_dead()
# Device with multiple ports, none of which line up correctly

View file

@ -3,14 +3,14 @@ from typing import Any
import pytest
import numpy
from numpy import pi
from masque import Pather, RenderPather, Library, Pattern, Port
from masque import Pather, Library, Pattern, Port
from masque.builder.tools import PathTool, Tool
from masque.error import BuildError, PortError, PatternError
def test_pather_trace_basic() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
# Port rotation 0 points in +x (INTO device).
# To extend it, we move in -x direction.
@ -35,7 +35,7 @@ def test_pather_trace_basic() -> None:
def test_pather_trace_to() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
@ -50,7 +50,7 @@ def test_pather_trace_to() -> None:
def test_pather_bundle_trace() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
p.pattern.ports['B'] = Port((0, 2000), rotation=0)
@ -74,7 +74,7 @@ def test_pather_bundle_trace() -> None:
def test_pather_each_bound() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
p.pattern.ports['B'] = Port((-1000, 2000), rotation=0)
@ -198,7 +198,7 @@ def test_rename() -> None:
def test_renderpather_uturn_fallback() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
rp = RenderPather(lib, tools=tool)
rp = Pather(lib, tools=tool, auto_render=False)
rp.pattern.ports['A'] = Port((0, 0), rotation=0)
# PathTool doesn't implement planU, so it should fall back to two planL calls
@ -239,7 +239,7 @@ def test_autotool_uturn() -> None:
default_out_ptype='wire'
)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), 0)
# CW U-turn (jog < 0)
@ -261,7 +261,7 @@ def test_autotool_uturn() -> None:
def test_pather_trace_into() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
# 1. Straight connector
p.pattern.ports['A'] = Port((0, 0), rotation=0)
@ -313,7 +313,7 @@ def test_pather_trace_into() -> None:
def test_pather_trace_into_dead_updates_ports_without_geometry() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000, ptype='wire')
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
p.pattern.ports['B'] = Port((-10000, 0), rotation=pi, ptype='wire')
p.set_dead()
@ -332,7 +332,7 @@ def test_pather_trace_into_dead_updates_ports_without_geometry() -> None:
def test_pather_dead_fallback_preserves_out_ptype() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000, ptype='wire')
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
p.set_dead()
@ -674,7 +674,7 @@ def test_pather_uturn_failed_fallback_is_atomic() -> None:
def test_renderpather_rename_to_none_keeps_pending_geometry_without_port() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
rp = RenderPather(lib, tools=tool)
rp = Pather(lib, tools=tool, auto_render=False)
rp.pattern.ports['A'] = Port((0, 0), rotation=0)
rp.at('A').straight(5000)
@ -722,7 +722,7 @@ def test_pather_plug_treeview_resolves_once() -> None:
def test_pather_failed_plug_does_not_add_break_marker() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.annotations = {'k': [1]}
p.pattern.ports['A'] = Port((0, 0), rotation=0)
@ -744,7 +744,7 @@ def test_pather_failed_plug_does_not_add_break_marker() -> None:
def test_pather_place_reused_deleted_name_keeps_break_marker() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
p.at('A').straight(5000)
@ -765,7 +765,7 @@ def test_pather_place_reused_deleted_name_keeps_break_marker() -> None:
def test_pather_plug_reused_deleted_name_keeps_break_marker() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
p.pattern.ports['B'] = Port((0, 0), rotation=0)
@ -793,7 +793,7 @@ def test_pather_plug_reused_deleted_name_keeps_break_marker() -> None:
def test_pather_failed_plugged_does_not_add_break_marker() -> None:
lib = Library()
tool = PathTool(layer='M1', width=1000)
p = Pather(lib, tools=tool)
p = Pather(lib, tools=tool, auto_render=False)
p.pattern.ports['A'] = Port((0, 0), rotation=0)
p.at('A').straight(5000)

View file

@ -3,7 +3,7 @@ from typing import cast, TYPE_CHECKING
from numpy.testing import assert_allclose
from numpy import pi
from ..builder import RenderPather
from ..builder import Pather
from ..builder.tools import PathTool
from ..library import Library
from ..ports import Port
@ -13,15 +13,15 @@ if TYPE_CHECKING:
@pytest.fixture
def rpather_setup() -> tuple[RenderPather, PathTool, Library]:
def rpather_setup() -> tuple[Pather, PathTool, Library]:
lib = Library()
tool = PathTool(layer=(1, 0), width=2, ptype="wire")
rp = RenderPather(lib, tools=tool)
rp = Pather(lib, tools=tool, auto_render=False)
rp.ports["start"] = Port((0, 0), pi / 2, ptype="wire")
return rp, tool, lib
def test_renderpather_basic(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_basic(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
# Plan two segments
rp.at("start").straight(10).straight(10)
@ -46,7 +46,7 @@ def test_renderpather_basic(rpather_setup: tuple[RenderPather, PathTool, Library
assert_allclose(path_shape.vertices, [[0, 0], [0, -10], [0, -20]], atol=1e-10)
def test_renderpather_bend(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_bend(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
# Plan straight then bend
rp.at("start").straight(10).cw(10)
@ -65,7 +65,7 @@ def test_renderpather_bend(rpather_setup: tuple[RenderPather, PathTool, Library]
assert_allclose(path_shape.vertices, [[0, 0], [0, -10], [0, -20], [-1, -20]], atol=1e-10)
def test_renderpather_mirror_preserves_planned_bend_geometry(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_mirror_preserves_planned_bend_geometry(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
rp.at("start").straight(10).cw(10)
@ -76,7 +76,7 @@ def test_renderpather_mirror_preserves_planned_bend_geometry(rpather_setup: tupl
assert_allclose(path_shape.vertices, [[0, 0], [0, 10], [0, 20], [-1, 20]], atol=1e-10)
def test_renderpather_retool(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_retool(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool1, lib = rpather_setup
tool2 = PathTool(layer=(2, 0), width=4, ptype="wire")
@ -90,7 +90,7 @@ def test_renderpather_retool(rpather_setup: tuple[RenderPather, PathTool, Librar
assert len(rp.pattern.shapes[(2, 0)]) == 1
def test_portpather_translate_only_affects_future_steps(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_portpather_translate_only_affects_future_steps(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
pp = rp.at("start")
pp.straight(10)
@ -109,7 +109,7 @@ def test_portpather_translate_only_affects_future_steps(rpather_setup: tuple[Ren
def test_renderpather_dead_ports() -> None:
lib = Library()
tool = PathTool(layer=(1, 0), width=1)
rp = RenderPather(lib, ports={"in": Port((0, 0), 0)}, tools=tool)
rp = Pather(lib, ports={"in": Port((0, 0), 0)}, tools=tool, auto_render=False)
rp.set_dead()
# Impossible path
@ -126,7 +126,7 @@ def test_renderpather_dead_ports() -> None:
assert not rp.pattern.has_shapes()
def test_renderpather_rename_port(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_rename_port(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
rp.at("start").straight(10)
# Rename port while path is planned
@ -148,7 +148,7 @@ def test_renderpather_rename_port(rpather_setup: tuple[RenderPather, PathTool, L
assert_allclose(rp.ports["new_start"].offset, [0, -20], atol=1e-10)
def test_renderpather_drop_keeps_pending_geometry_without_port(rpather_setup: tuple[RenderPather, PathTool, Library]) -> None:
def test_renderpather_drop_keeps_pending_geometry_without_port(rpather_setup: tuple[Pather, PathTool, Library]) -> None:
rp, tool, lib = rpather_setup
rp.at("start").straight(10).drop()