[Pather] make two-L path planning atomic (don't error out with only one half drawn)

This commit is contained in:
Jan Petykiewicz 2026-03-31 09:28:48 -07:00
commit e7f847d4c7
2 changed files with 45 additions and 4 deletions

View file

@ -415,8 +415,17 @@ class Pather(PortList):
self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=pi) self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=pi)
return self return self
self._traceL(portspec, ccw0, L1, **(kwargs | {'out_ptype': None})) try:
self._traceL(portspec, not ccw0, L2, **(kwargs | {'plug_into': plug_into})) out_port0, data0 = tool.planL(ccw0, L1, in_ptype=in_ptype, **(kwargs | {'out_ptype': None}))
out_port1, data1 = tool.planL(not ccw0, L2, in_ptype=out_port0.ptype, **kwargs)
except (BuildError, NotImplementedError):
if not self._dead:
raise
self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=pi)
return self
self._apply_step('L', portspec, out_port0, data0, tool)
self._apply_step('L', portspec, out_port1, data1, tool, plug_into)
return self return self
if out_port is not None: if out_port is not None:
self._apply_step('S', portspec, out_port, data, tool, plug_into) self._apply_step('S', portspec, out_port, data, tool, plug_into)
@ -436,14 +445,16 @@ class Pather(PortList):
try: try:
R = self._get_tool_R(tool, ccw, in_ptype, **kwargs) R = self._get_tool_R(tool, ccw, in_ptype, **kwargs)
L1, L2 = length + R, abs(jog) - R L1, L2 = length + R, abs(jog) - R
self._traceL(portspec, ccw, L1, **(kwargs | {'out_ptype': None})) out_port0, data0 = tool.planL(ccw, L1, in_ptype=in_ptype, **(kwargs | {'out_ptype': None}))
self._traceL(portspec, ccw, L2, **(kwargs | {'plug_into': plug_into})) out_port1, data1 = tool.planL(ccw, L2, in_ptype=out_port0.ptype, **kwargs)
except (BuildError, NotImplementedError): except (BuildError, NotImplementedError):
if not self._dead: if not self._dead:
raise raise
self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=0) self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=0)
return self return self
else: else:
self._apply_step('L', portspec, out_port0, data0, tool)
self._apply_step('L', portspec, out_port1, data1, tool, plug_into)
return self return self
if out_port is not None: if out_port is not None:
self._apply_step('U', portspec, out_port, data, tool, plug_into) self._apply_step('U', portspec, out_port, data, tool, plug_into)

View file

@ -1,7 +1,9 @@
import pytest
import numpy import numpy
from numpy import pi from numpy import pi
from masque import Pather, RenderPather, Library, Pattern, Port from masque import Pather, RenderPather, Library, Pattern, Port
from masque.builder.tools import PathTool from masque.builder.tools import PathTool
from masque.error import BuildError
def test_pather_trace_basic() -> None: def test_pather_trace_basic() -> None:
lib = Library() lib = Library()
@ -240,3 +242,31 @@ def test_pather_trace_into() -> None:
assert numpy.allclose(p.pattern.ports['G'].offset, (-10000, 2000)) assert numpy.allclose(p.pattern.ports['G'].offset, (-10000, 2000))
assert p.pattern.ports['G'].rotation is not None assert p.pattern.ports['G'].rotation is not None
assert numpy.isclose(p.pattern.ports['G'].rotation, pi) assert numpy.isclose(p.pattern.ports['G'].rotation, pi)
def test_pather_jog_failed_fallback_is_atomic() -> None:
lib = Library()
tool = PathTool(layer='M1', width=2, ptype='wire')
p = Pather(lib, tools=tool)
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
with pytest.raises(BuildError, match='shorter than required bend'):
p.jog('A', 1.5, length=5)
assert numpy.allclose(p.pattern.ports['A'].offset, (0, 0))
assert p.pattern.ports['A'].rotation == 0
assert len(p.paths['A']) == 0
def test_pather_uturn_failed_fallback_is_atomic() -> None:
lib = Library()
tool = PathTool(layer='M1', width=2, ptype='wire')
p = Pather(lib, tools=tool)
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
with pytest.raises(BuildError, match='shorter than required bend'):
p.uturn('A', 1.5, length=0)
assert numpy.allclose(p.pattern.ports['A'].offset, (0, 0))
assert p.pattern.ports['A'].rotation == 0
assert len(p.paths['A']) == 0