[Pather] fix using trees when append=True
This commit is contained in:
parent
6cf9600193
commit
bdc4dfdd06
3 changed files with 136 additions and 2 deletions
|
|
@ -16,7 +16,7 @@ from numpy import pi
|
|||
from numpy.typing import ArrayLike
|
||||
|
||||
from ..pattern import Pattern
|
||||
from ..library import ILibrary, TreeView
|
||||
from ..library import ILibrary, TreeView, SINGLE_USE_PREFIX
|
||||
from ..error import BuildError, PortError
|
||||
from ..ports import PortList, Port
|
||||
from ..abstract import Abstract
|
||||
|
|
@ -1067,9 +1067,24 @@ class Pather(PortList):
|
|||
tool_port_names = ('A', 'B')
|
||||
pat = Pattern()
|
||||
|
||||
def validate_tree(portspec: str, batch: list[RenderStep], tree: ILibrary) -> None:
|
||||
missing = sorted(
|
||||
name
|
||||
for name in tree.dangling_refs(tree.top())
|
||||
if isinstance(name, str) and name.startswith(SINGLE_USE_PREFIX)
|
||||
)
|
||||
if not missing:
|
||||
return
|
||||
|
||||
tool_name = type(batch[0].tool).__name__
|
||||
raise BuildError(
|
||||
f'Tool {tool_name}.render() returned missing single-use refs for {portspec}: {missing}'
|
||||
)
|
||||
|
||||
def render_batch(portspec: str, batch: list[RenderStep], append: bool) -> None:
|
||||
assert batch[0].tool is not None
|
||||
tree = batch[0].tool.render(batch, port_names=tool_port_names)
|
||||
validate_tree(portspec, batch, tree)
|
||||
name = self.library << tree
|
||||
if portspec in pat.ports:
|
||||
del pat.ports[portspec]
|
||||
|
|
|
|||
|
|
@ -451,7 +451,9 @@ class Tool:
|
|||
else:
|
||||
continue
|
||||
|
||||
pat.plug(seg_tree.top_pattern(), {port_names[1]: port_names[0]}, append=True)
|
||||
seg_name = lib << seg_tree
|
||||
pat.plug(lib[seg_name], {port_names[1]: port_names[0]}, append=True)
|
||||
del lib[seg_name]
|
||||
|
||||
return lib
|
||||
|
||||
|
|
|
|||
|
|
@ -684,6 +684,123 @@ def test_pather_uturn_failed_fallback_is_atomic() -> None:
|
|||
assert len(p.paths['A']) == 0
|
||||
|
||||
|
||||
def test_pather_render_auto_renames_single_use_tool_children() -> None:
|
||||
class FullTreeTool(Tool):
|
||||
def planL(self, ccw, length, *, in_ptype=None, out_ptype=None, **kwargs): # noqa: ANN001,ANN202
|
||||
ptype = out_ptype or in_ptype or 'wire'
|
||||
return Port((length, 0), rotation=pi, ptype=ptype), {'length': length}
|
||||
|
||||
def render(self, batch, *, port_names=('A', 'B'), **kwargs) -> Library: # noqa: ANN001,ANN202
|
||||
tree = Library()
|
||||
top = Pattern(ports={
|
||||
port_names[0]: Port((0, 0), 0, ptype='wire'),
|
||||
port_names[1]: Port((1, 0), pi, ptype='wire'),
|
||||
})
|
||||
child = Pattern(annotations={'batch': [len(batch)]})
|
||||
top.ref('_seg')
|
||||
tree['_top'] = top
|
||||
tree['_seg'] = child
|
||||
return tree
|
||||
|
||||
lib = Library()
|
||||
p = Pather(lib, tools=FullTreeTool(), auto_render=False)
|
||||
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
|
||||
|
||||
p.straight('A', 10)
|
||||
p.render()
|
||||
p.straight('A', 10)
|
||||
p.render()
|
||||
|
||||
assert len(lib) == 2
|
||||
assert set(lib.keys()) == set(p.pattern.refs.keys())
|
||||
assert len(set(p.pattern.refs.keys())) == 2
|
||||
assert all(name.startswith('_seg') for name in lib)
|
||||
assert p.pattern.referenced_patterns() <= set(lib.keys())
|
||||
|
||||
|
||||
def test_tool_render_fallback_preserves_segment_subtrees() -> None:
|
||||
class TraceTreeTool(Tool):
|
||||
def traceL(self, ccw, length, *, in_ptype=None, out_ptype=None, port_names=('A', 'B'), **kwargs) -> Library: # noqa: ANN001
|
||||
tree = Library()
|
||||
top = Pattern(ports={
|
||||
port_names[0]: Port((0, 0), 0, ptype='wire'),
|
||||
port_names[1]: Port((length, 0), pi, ptype='wire'),
|
||||
})
|
||||
child = Pattern(annotations={'length': [length]})
|
||||
top.ref('_seg')
|
||||
tree['_top'] = top
|
||||
tree['_seg'] = child
|
||||
return tree
|
||||
|
||||
lib = Library()
|
||||
p = Pather(lib, tools=TraceTreeTool(), auto_render=False)
|
||||
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
|
||||
|
||||
p.straight('A', 10)
|
||||
p.render()
|
||||
|
||||
assert '_seg' in lib
|
||||
assert '_seg' in p.pattern.refs
|
||||
assert p.pattern.referenced_patterns() <= set(lib.keys())
|
||||
|
||||
|
||||
def test_pather_render_rejects_missing_single_use_tool_refs() -> None:
|
||||
class MissingSingleUseTool(Tool):
|
||||
def planL(self, ccw, length, *, in_ptype=None, out_ptype=None, **kwargs): # noqa: ANN001,ANN202
|
||||
ptype = out_ptype or in_ptype or 'wire'
|
||||
return Port((length, 0), rotation=pi, ptype=ptype), {'length': length}
|
||||
|
||||
def render(self, batch, *, port_names=('A', 'B'), **kwargs) -> Library: # noqa: ANN001,ANN202
|
||||
tree = Library()
|
||||
top = Pattern(ports={
|
||||
port_names[0]: Port((0, 0), 0, ptype='wire'),
|
||||
port_names[1]: Port((1, 0), pi, ptype='wire'),
|
||||
})
|
||||
top.ref('_seg')
|
||||
tree['_top'] = top
|
||||
return tree
|
||||
|
||||
lib = Library()
|
||||
lib['_seg'] = Pattern(annotations={'stale': [1]})
|
||||
p = Pather(lib, tools=MissingSingleUseTool(), auto_render=False)
|
||||
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
|
||||
p.straight('A', 10)
|
||||
|
||||
with pytest.raises(BuildError, match='missing single-use refs'):
|
||||
p.render()
|
||||
|
||||
assert list(lib.keys()) == ['_seg']
|
||||
assert not p.pattern.refs
|
||||
|
||||
|
||||
def test_pather_render_allows_missing_non_single_use_tool_refs() -> None:
|
||||
class SharedRefTool(Tool):
|
||||
def planL(self, ccw, length, *, in_ptype=None, out_ptype=None, **kwargs): # noqa: ANN001,ANN202
|
||||
ptype = out_ptype or in_ptype or 'wire'
|
||||
return Port((length, 0), rotation=pi, ptype=ptype), {'length': length}
|
||||
|
||||
def render(self, batch, *, port_names=('A', 'B'), **kwargs) -> Library: # noqa: ANN001,ANN202
|
||||
tree = Library()
|
||||
top = Pattern(ports={
|
||||
port_names[0]: Port((0, 0), 0, ptype='wire'),
|
||||
port_names[1]: Port((1, 0), pi, ptype='wire'),
|
||||
})
|
||||
top.ref('shared')
|
||||
tree['_top'] = top
|
||||
return tree
|
||||
|
||||
lib = Library()
|
||||
lib['shared'] = Pattern(annotations={'shared': [1]})
|
||||
p = Pather(lib, tools=SharedRefTool(), auto_render=False)
|
||||
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
|
||||
|
||||
p.straight('A', 10)
|
||||
p.render()
|
||||
|
||||
assert 'shared' in p.pattern.refs
|
||||
assert p.pattern.referenced_patterns() <= set(lib.keys())
|
||||
|
||||
|
||||
def test_renderpather_rename_to_none_keeps_pending_geometry_without_port() -> None:
|
||||
lib = Library()
|
||||
tool = PathTool(layer='M1', width=1000)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue