107 lines
3.7 KiB
Python
107 lines
3.7 KiB
Python
import pytest
|
|
import numpy
|
|
from numpy import pi
|
|
from numpy.testing import assert_allclose
|
|
|
|
from masque import Pather, Library, Pattern, Port
|
|
from masque.builder.tools import AutoTool
|
|
|
|
|
|
def make_straight(length: float, width: float = 2, ptype: str = "wire") -> Pattern:
|
|
pat = Pattern()
|
|
pat.rect((1, 0), xmin=0, xmax=length, yctr=0, ly=width)
|
|
pat.ports["A"] = Port((0, 0), 0, ptype=ptype)
|
|
pat.ports["B"] = Port((length, 0), pi, ptype=ptype)
|
|
return pat
|
|
|
|
def make_bend(radius: float, width: float = 2, ptype: str = "wire", clockwise: bool = True) -> Pattern:
|
|
pat = Pattern()
|
|
# Rectangular approximation of a 90 degree bend.
|
|
if clockwise:
|
|
pat.rect((1, 0), xmin=0, xmax=radius, yctr=0, ly=width)
|
|
pat.rect((1, 0), xctr=radius, lx=width, ymin=-radius, ymax=0)
|
|
pat.ports["A"] = Port((0, 0), 0, ptype=ptype)
|
|
pat.ports["B"] = Port((radius, -radius), pi/2, ptype=ptype)
|
|
else:
|
|
pat.rect((1, 0), xmin=0, xmax=radius, yctr=0, ly=width)
|
|
pat.rect((1, 0), xctr=radius, lx=width, ymin=0, ymax=radius)
|
|
pat.ports["A"] = Port((0, 0), 0, ptype=ptype)
|
|
pat.ports["B"] = Port((radius, radius), -pi/2, ptype=ptype)
|
|
return pat
|
|
|
|
@pytest.fixture
|
|
def multi_bend_tool() -> tuple[AutoTool, Library]:
|
|
lib = Library()
|
|
|
|
lib["b1"] = make_bend(2, ptype="wire")
|
|
b1_abs = lib.abstract("b1")
|
|
lib["b2"] = make_bend(5, ptype="wire")
|
|
b2_abs = lib.abstract("b2")
|
|
|
|
tool = (
|
|
AutoTool()
|
|
.add_straight(make_straight, "wire", "A", length_range=(0, 10))
|
|
.add_straight(lambda length: make_straight(length, width=4), "wire", "A", length_range=(10, 1e8))
|
|
.add_bend(b1_abs, "A", "B", clockwise=True, mirror=True)
|
|
.add_bend(b2_abs, "A", "B", clockwise=True, mirror=True)
|
|
)
|
|
return tool, lib
|
|
|
|
def test_autotool_uturn() -> None:
|
|
from masque.builder.tools import AutoTool
|
|
lib = Library()
|
|
|
|
def make_straight(length: float) -> Pattern:
|
|
pat = Pattern()
|
|
pat.rect(layer='M1', xmin=0, xmax=length, yctr=0, ly=1000)
|
|
pat.ports['in'] = Port((0, 0), 0)
|
|
pat.ports['out'] = Port((length, 0), pi)
|
|
return pat
|
|
|
|
bend_pat = Pattern()
|
|
bend_pat.polygon(layer='M1', vertices=[(0, -500), (0, 500), (1000, -500)])
|
|
bend_pat.ports['in'] = Port((0, 0), 0)
|
|
bend_pat.ports['out'] = Port((500, -500), pi/2)
|
|
lib['bend'] = bend_pat
|
|
|
|
tool = (
|
|
AutoTool()
|
|
.add_straight(make_straight, 'wire', 'in')
|
|
.add_bend(lib.abstract('bend'), 'in', 'out', clockwise=True)
|
|
)
|
|
|
|
p = Pather(lib, tools=tool)
|
|
p.pattern.ports['A'] = Port((0, 0), 0)
|
|
|
|
p.at('A').uturn(offset=-2000, length=1000)
|
|
|
|
# U-turn plan output is transformed into the port extension frame.
|
|
assert numpy.allclose(p.pattern.ports['A'].offset, (-1000, 2000))
|
|
assert p.pattern.ports['A'].rotation is not None
|
|
assert numpy.isclose(p.pattern.ports['A'].rotation, pi)
|
|
|
|
def test_deferred_render_autotool_double_L(multi_bend_tool: tuple[AutoTool, Library]) -> None:
|
|
tool, lib = multi_bend_tool
|
|
rp = Pather(lib, tools=tool)
|
|
rp.ports["A"] = Port((0,0), 0, ptype="wire")
|
|
|
|
rp.jog("A", 10, length=20)
|
|
|
|
assert_allclose(rp.ports["A"].offset, [-20, -10])
|
|
assert_allclose(rp.ports["A"].rotation, 0)
|
|
|
|
rp.render()
|
|
assert len(rp.pattern.refs) > 0
|
|
|
|
def test_pather_uturn_fallback_no_heuristic(multi_bend_tool: tuple[AutoTool, Library]) -> None:
|
|
tool, lib = multi_bend_tool
|
|
|
|
p = Pather(lib, tools=tool)
|
|
p.ports["A"] = Port((0,0), 0, ptype="wire")
|
|
|
|
p.uturn("A", 10, length=5)
|
|
|
|
# Fallback U-turn uses two CCW bends: (7, 2) then (8, 2) in local tool frames,
|
|
# yielding a global endpoint at (-5, -10).
|
|
assert_allclose(p.ports["A"].offset, [-5, -10])
|
|
assert_allclose(p.ports["A"].rotation, pi)
|