diff --git a/masque/builder/pather.py b/masque/builder/pather.py index 3478c32..e8804d1 100644 --- a/masque/builder/pather.py +++ b/masque/builder/pather.py @@ -415,8 +415,17 @@ class Pather(PortList): self._apply_dead_fallback(portspec, length, jog, None, in_ptype, plug_into, out_rot=pi) return self - self._traceL(portspec, ccw0, L1, **(kwargs | {'out_ptype': None})) - self._traceL(portspec, not ccw0, L2, **(kwargs | {'plug_into': plug_into})) + try: + 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 if out_port is not None: self._apply_step('S', portspec, out_port, data, tool, plug_into) @@ -436,14 +445,16 @@ class Pather(PortList): try: R = self._get_tool_R(tool, ccw, in_ptype, **kwargs) L1, L2 = length + R, abs(jog) - R - self._traceL(portspec, ccw, L1, **(kwargs | {'out_ptype': None})) - self._traceL(portspec, ccw, L2, **(kwargs | {'plug_into': plug_into})) + out_port0, data0 = tool.planL(ccw, L1, in_ptype=in_ptype, **(kwargs | {'out_ptype': None})) + out_port1, data1 = tool.planL(ccw, 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=0) return self else: + self._apply_step('L', portspec, out_port0, data0, tool) + self._apply_step('L', portspec, out_port1, data1, tool, plug_into) return self if out_port is not None: self._apply_step('U', portspec, out_port, data, tool, plug_into) diff --git a/masque/test/test_pather_api.py b/masque/test/test_pather_api.py index 9ac1b78..c837280 100644 --- a/masque/test/test_pather_api.py +++ b/masque/test/test_pather_api.py @@ -1,7 +1,9 @@ +import pytest import numpy from numpy import pi from masque import Pather, RenderPather, Library, Pattern, Port from masque.builder.tools import PathTool +from masque.error import BuildError def test_pather_trace_basic() -> None: lib = Library() @@ -240,3 +242,31 @@ def test_pather_trace_into() -> None: assert numpy.allclose(p.pattern.ports['G'].offset, (-10000, 2000)) assert p.pattern.ports['G'].rotation is not None 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