[Pather] fix port rename/deletion tracking
This commit is contained in:
parent
2e0b64bdab
commit
2b29e46b93
3 changed files with 63 additions and 4 deletions
|
|
@ -228,7 +228,17 @@ class Pather(PortList):
|
|||
@logged_op(lambda args: list(args['mapping'].keys()))
|
||||
def rename_ports(self, mapping: dict[str, str | None], overwrite: bool = False) -> Self:
|
||||
self.pattern.rename_ports(mapping, overwrite)
|
||||
renamed: dict[str, list[RenderStep]] = {vv: self.paths.pop(kk) for kk, vv in mapping.items() if kk in self.paths and vv is not None}
|
||||
renamed: dict[str, list[RenderStep]] = {}
|
||||
for kk, vv in mapping.items():
|
||||
if kk not in self.paths:
|
||||
continue
|
||||
steps = self.paths.pop(kk)
|
||||
# Preserve deferred geometry even if the live port is deleted.
|
||||
# `render()` can still materialize the saved steps using their stored start/end ports.
|
||||
# Current semantics intentionally keep deleted ports' queued steps under the old key,
|
||||
# so if a new live port later reuses that name it does not retarget the old geometry;
|
||||
# the old and new routes merely share a render bucket until `render()` consumes them.
|
||||
renamed[kk if vv is None else vv] = steps
|
||||
self.paths.update(renamed)
|
||||
return self
|
||||
|
||||
|
|
@ -789,6 +799,9 @@ class PortPather:
|
|||
#
|
||||
# Delegate to port
|
||||
#
|
||||
# These mutate only the selected live port state. They do not rewrite already planned
|
||||
# RenderSteps, so deferred geometry remains as previously planned and only future routing
|
||||
# starts from the updated port.
|
||||
def set_ptype(self, ptype: str) -> Self:
|
||||
for port in self.ports:
|
||||
self.pather.pattern[port].set_ptype(ptype)
|
||||
|
|
@ -865,8 +878,7 @@ class PortPather:
|
|||
|
||||
def drop(self) -> Self:
|
||||
""" Remove selected ports from the pattern and the PortPather. """
|
||||
for pp in self.ports:
|
||||
del self.pather.pattern.ports[pp]
|
||||
self.pather.rename_ports({pp: None for pp in self.ports})
|
||||
self.ports = []
|
||||
return self
|
||||
|
||||
|
|
@ -880,7 +892,7 @@ class PortPather:
|
|||
if name is None:
|
||||
self.drop()
|
||||
return None
|
||||
del self.pather.pattern.ports[name]
|
||||
self.pather.rename_ports({name: None})
|
||||
self.ports = [pp for pp in self.ports if pp != name]
|
||||
return self
|
||||
|
||||
|
|
|
|||
|
|
@ -300,3 +300,20 @@ def test_pather_uturn_failed_fallback_is_atomic() -> None:
|
|||
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_renderpather_rename_to_none_keeps_pending_geometry_without_port() -> None:
|
||||
lib = Library()
|
||||
tool = PathTool(layer='M1', width=1000)
|
||||
rp = RenderPather(lib, tools=tool)
|
||||
rp.pattern.ports['A'] = Port((0, 0), rotation=0)
|
||||
|
||||
rp.at('A').straight(5000)
|
||||
rp.rename_ports({'A': None})
|
||||
|
||||
assert 'A' not in rp.pattern.ports
|
||||
assert len(rp.paths['A']) == 1
|
||||
|
||||
rp.render()
|
||||
assert rp.pattern.has_shapes()
|
||||
assert 'A' not in rp.pattern.ports
|
||||
|
|
|
|||
|
|
@ -90,6 +90,22 @@ 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:
|
||||
rp, tool, lib = rpather_setup
|
||||
pp = rp.at("start")
|
||||
pp.straight(10)
|
||||
pp.translate((5, 0))
|
||||
pp.straight(10)
|
||||
|
||||
rp.render()
|
||||
|
||||
shapes = rp.pattern.shapes[(1, 0)]
|
||||
assert len(shapes) == 2
|
||||
assert_allclose(cast("Path", shapes[0]).vertices, [[0, 0], [0, -10]], atol=1e-10)
|
||||
assert_allclose(cast("Path", shapes[1]).vertices, [[5, -10], [5, -20]], atol=1e-10)
|
||||
assert_allclose(rp.ports["start"].offset, [5, -20], atol=1e-10)
|
||||
|
||||
|
||||
def test_renderpather_dead_ports() -> None:
|
||||
lib = Library()
|
||||
tool = PathTool(layer=(1, 0), width=1)
|
||||
|
|
@ -132,6 +148,20 @@ 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:
|
||||
rp, tool, lib = rpather_setup
|
||||
rp.at("start").straight(10).drop()
|
||||
|
||||
assert "start" not in rp.ports
|
||||
assert len(rp.paths["start"]) == 1
|
||||
|
||||
rp.render()
|
||||
assert rp.pattern.has_shapes()
|
||||
assert "start" not in rp.ports
|
||||
path_shape = cast("Path", rp.pattern.shapes[(1, 0)][0])
|
||||
assert_allclose(path_shape.vertices, [[0, 0], [0, -10]], atol=1e-10)
|
||||
|
||||
|
||||
def test_pathtool_traceL_bend_geometry_matches_ports() -> None:
|
||||
tool = PathTool(layer=(1, 0), width=2, ptype="wire")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue