[set_dead] improve handling of dead ports
This commit is contained in:
parent
429e687666
commit
84106dc355
5 changed files with 350 additions and 88 deletions
|
|
@ -252,19 +252,24 @@ 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]] = {}
|
||||
for kk, vv in mapping.items():
|
||||
if kk not in self.paths:
|
||||
continue
|
||||
steps = self.paths.pop(kk)
|
||||
winners = self.pattern._rename_ports_impl(
|
||||
mapping,
|
||||
overwrite=overwrite or self._dead,
|
||||
allow_collisions=self._dead,
|
||||
)
|
||||
|
||||
moved_steps = {kk: self.paths.pop(kk) for kk in mapping if kk in self.paths}
|
||||
for kk, steps in moved_steps.items():
|
||||
vv = mapping[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)
|
||||
target = kk if vv is None else vv
|
||||
if self._dead and vv is not None and winners.get(vv) != kk:
|
||||
target = kk
|
||||
self.paths[target].extend(steps)
|
||||
return self
|
||||
|
||||
def set_dead(self) -> Self:
|
||||
|
|
@ -756,6 +761,7 @@ class Pather(PortList):
|
|||
plug_into: str | None = None,
|
||||
*,
|
||||
out_rot: float | None = None,
|
||||
out_ptype: str | None = None,
|
||||
) -> None:
|
||||
if out_rot is None:
|
||||
if ccw is None:
|
||||
|
|
@ -768,7 +774,7 @@ class Pather(PortList):
|
|||
port = self.pattern[portspec]
|
||||
port_rot = port.rotation
|
||||
assert port_rot is not None
|
||||
out_port = Port((length, jog), rotation=out_rot, ptype=in_ptype)
|
||||
out_port = Port((length, jog), rotation=out_rot, ptype=out_ptype or in_ptype)
|
||||
out_port.rotate_around((0, 0), pi + port_rot)
|
||||
out_port.translate(port.offset)
|
||||
self.pattern.ports[portspec] = out_port
|
||||
|
|
@ -786,7 +792,15 @@ class Pather(PortList):
|
|||
except (BuildError, NotImplementedError):
|
||||
if not self._dead:
|
||||
raise
|
||||
self._apply_dead_fallback(portspec, length, 0, ccw, in_ptype, plug_into)
|
||||
self._apply_dead_fallback(
|
||||
portspec,
|
||||
length,
|
||||
0,
|
||||
ccw,
|
||||
in_ptype,
|
||||
plug_into,
|
||||
out_ptype=kwargs.get('out_ptype'),
|
||||
)
|
||||
return self
|
||||
if out_port is not None:
|
||||
self._apply_step('L', portspec, out_port, data, tool, plug_into)
|
||||
|
|
@ -806,7 +820,16 @@ class Pather(PortList):
|
|||
except (BuildError, NotImplementedError):
|
||||
if not self._dead:
|
||||
raise
|
||||
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,
|
||||
out_ptype=kwargs.get('out_ptype'),
|
||||
)
|
||||
return self
|
||||
|
||||
self._apply_validated_double_l(
|
||||
|
|
@ -840,7 +863,16 @@ class Pather(PortList):
|
|||
except (BuildError, NotImplementedError):
|
||||
if not self._dead:
|
||||
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,
|
||||
out_ptype=kwargs.get('out_ptype'),
|
||||
)
|
||||
return self
|
||||
|
||||
self._apply_validated_double_l(
|
||||
|
|
@ -1015,8 +1047,6 @@ class Pather(PortList):
|
|||
thru=thru,
|
||||
**kwargs,
|
||||
):
|
||||
if self._dead:
|
||||
return self
|
||||
ops = self._plan_trace_into(
|
||||
portspec_src,
|
||||
portspec_dst,
|
||||
|
|
@ -1238,7 +1268,7 @@ class PortPather:
|
|||
else:
|
||||
name_map = dict(name)
|
||||
self.pather.rename_ports(name_map)
|
||||
self.ports = [mm for mm in [name_map.get(pp, pp) for pp in self.ports] if mm is not None]
|
||||
self.ports = list(dict.fromkeys(mm for mm in [name_map.get(pp, pp) for pp in self.ports] if mm is not None))
|
||||
return self
|
||||
|
||||
def select(self, ports: str | Iterable[str]) -> Self:
|
||||
|
|
@ -1274,33 +1304,37 @@ class PortPather:
|
|||
if missing_pattern:
|
||||
raise PortError(f'Ports to {action} were not found: {missing_pattern}')
|
||||
|
||||
targets = list(name_map.values())
|
||||
duplicate_targets = {vv for vv in targets if targets.count(vv) > 1}
|
||||
if duplicate_targets:
|
||||
raise PortError(f'{action.capitalize()} targets would collide: {duplicate_targets}')
|
||||
if not self.pather._dead:
|
||||
targets = list(name_map.values())
|
||||
duplicate_targets = {vv for vv in targets if targets.count(vv) > 1}
|
||||
if duplicate_targets:
|
||||
raise PortError(f'{action.capitalize()} targets would collide: {duplicate_targets}')
|
||||
|
||||
overwritten = {
|
||||
dst for src, dst in name_map.items()
|
||||
if dst in self.pather.pattern.ports and dst != src
|
||||
}
|
||||
if overwritten:
|
||||
raise PortError(f'{action.capitalize()} would overwrite existing ports: {overwritten}')
|
||||
overwritten = {
|
||||
dst for src, dst in name_map.items()
|
||||
if dst in self.pather.pattern.ports and dst != src
|
||||
}
|
||||
if overwritten:
|
||||
raise PortError(f'{action.capitalize()} would overwrite existing ports: {overwritten}')
|
||||
|
||||
return name_map
|
||||
|
||||
def mark(self, name: str | Mapping[str, str]) -> Self:
|
||||
""" Bookmark current port(s). """
|
||||
name_map = self._normalize_copy_map(name, 'mark')
|
||||
source_ports = {src: self.pather.pattern[src].copy() for src in name_map}
|
||||
for src, dst in name_map.items():
|
||||
self.pather.pattern.ports[dst] = self.pather.pattern[src].copy()
|
||||
self.pather.pattern.ports[dst] = source_ports[src].copy()
|
||||
return self
|
||||
|
||||
def fork(self, name: str | Mapping[str, str]) -> Self:
|
||||
""" Split and follow new name. """
|
||||
name_map = self._normalize_copy_map(name, 'fork')
|
||||
source_ports = {src: self.pather.pattern[src].copy() for src in name_map}
|
||||
for src, dst in name_map.items():
|
||||
self.pather.pattern.ports[dst] = self.pather.pattern[src].copy()
|
||||
self.pather.pattern.ports[dst] = source_ports[src].copy()
|
||||
self.ports = [(dst if pp == src else pp) for pp in self.ports]
|
||||
self.ports = list(dict.fromkeys(self.ports))
|
||||
return self
|
||||
|
||||
def drop(self) -> Self:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue