129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
from numpy.testing import assert_equal, assert_allclose
|
|
from numpy import pi
|
|
|
|
from ..builder import Builder
|
|
from ..library import Library
|
|
from ..pattern import Pattern
|
|
from ..ports import Port
|
|
|
|
|
|
def test_builder_init() -> None:
|
|
lib = Library()
|
|
b = Builder(lib, name="mypat")
|
|
assert b.pattern is lib["mypat"]
|
|
assert b.library is lib
|
|
|
|
|
|
def test_builder_place() -> None:
|
|
lib = Library()
|
|
child = Pattern()
|
|
child.ports["A"] = Port((0, 0), 0)
|
|
lib["child"] = child
|
|
|
|
b = Builder(lib)
|
|
b.place("child", offset=(10, 20), port_map={"A": "child_A"})
|
|
|
|
assert "child_A" in b.ports
|
|
assert_equal(b.ports["child_A"].offset, [10, 20])
|
|
assert "child" in b.pattern.refs
|
|
|
|
|
|
def test_builder_plug() -> None:
|
|
lib = Library()
|
|
|
|
wire = Pattern()
|
|
wire.ports["in"] = Port((0, 0), 0)
|
|
wire.ports["out"] = Port((10, 0), pi)
|
|
lib["wire"] = wire
|
|
|
|
b = Builder(lib)
|
|
b.ports["start"] = Port((100, 100), 0)
|
|
|
|
# Plug wire's "in" port into builder's "start" port
|
|
# Wire's "out" port should be renamed to "start" because thru=True (default) and wire has 2 ports
|
|
# builder start: (100, 100) rotation 0
|
|
# wire in: (0, 0) rotation 0
|
|
# wire out: (10, 0) rotation pi
|
|
# Plugging wire in (rot 0) to builder start (rot 0) means wire is rotated by pi (180 deg)
|
|
# so wire in is at (100, 100), wire out is at (100 - 10, 100) = (90, 100)
|
|
b.plug("wire", map_in={"start": "in"})
|
|
|
|
assert "start" in b.ports
|
|
assert_equal(b.ports["start"].offset, [90, 100])
|
|
assert_allclose(b.ports["start"].rotation, 0, atol=1e-10)
|
|
|
|
|
|
def test_builder_interface() -> None:
|
|
lib = Library()
|
|
source = Pattern()
|
|
source.ports["P1"] = Port((0, 0), 0)
|
|
lib["source"] = source
|
|
|
|
b = Builder.interface("source", library=lib, name="iface")
|
|
assert "in_P1" in b.ports
|
|
assert "P1" in b.ports
|
|
assert b.pattern is lib["iface"]
|
|
|
|
|
|
def test_builder_set_dead() -> None:
|
|
lib = Library()
|
|
lib["sub"] = Pattern()
|
|
b = Builder(lib)
|
|
b.set_dead()
|
|
|
|
b.place("sub")
|
|
assert not b.pattern.has_refs()
|
|
|
|
|
|
def test_builder_dead_ports() -> None:
|
|
lib = Library()
|
|
pat = Pattern()
|
|
pat.ports['A'] = Port((0, 0), 0)
|
|
b = Builder(lib, pattern=pat)
|
|
b.set_dead()
|
|
|
|
# Attempt to plug a device where ports don't line up
|
|
# A has rotation 0, C has rotation 0. plug() expects opposing rotations (pi difference).
|
|
other = Pattern(ports={'C': Port((10, 10), 0), 'D': Port((20, 20), 0)})
|
|
|
|
# This should NOT raise PortError because b is dead
|
|
b.plug(other, map_in={'A': 'C'}, map_out={'D': 'B'})
|
|
|
|
# Port A should be removed, and Port B (renamed from D) should be added
|
|
assert 'A' not in b.ports
|
|
assert 'B' in b.ports
|
|
|
|
# Verify geometry was not added
|
|
assert not b.pattern.has_refs()
|
|
assert not b.pattern.has_shapes()
|
|
|
|
|
|
def test_dead_plug_best_effort() -> None:
|
|
lib = Library()
|
|
pat = Pattern()
|
|
pat.ports['A'] = Port((0, 0), 0)
|
|
b = Builder(lib, pattern=pat)
|
|
b.set_dead()
|
|
|
|
# Device with multiple ports, none of which line up correctly
|
|
other = Pattern(ports={
|
|
'P1': Port((10, 10), 0), # Wrong rotation (0 instead of pi)
|
|
'P2': Port((20, 20), pi) # Correct rotation but wrong offset
|
|
})
|
|
|
|
# Try to plug. find_transform will fail.
|
|
# It should fall back to aligning the first pair ('A' and 'P1').
|
|
b.plug(other, map_in={'A': 'P1'}, map_out={'P2': 'B'})
|
|
|
|
assert 'A' not in b.ports
|
|
assert 'B' in b.ports
|
|
|
|
# Dummy transform aligns A (0,0) with P1 (10,10)
|
|
# A rotation 0, P1 rotation 0 -> rotation = (0 - 0 - pi) = -pi
|
|
# P2 (20,20) rotation pi:
|
|
# 1. Translate P2 so P1 is at origin: (20,20) - (10,10) = (10,10)
|
|
# 2. Rotate (10,10) by -pi: (-10,-10)
|
|
# 3. Translate by s_port.offset (0,0): (-10,-10)
|
|
assert_allclose(b.ports['B'].offset, [-10, -10], atol=1e-10)
|
|
# P2 rot pi + transform rot -pi = 0
|
|
assert_allclose(b.ports['B'].rotation, 0, atol=1e-10)
|