A T depends on every connection between its copper and its group
Anchoring a T only on the connection whose copper it touched missed the connections that tie that copper to the T's own group. When one of those was ripped, the T stayed routed but cut off: on charge-sense freeroute counted 15 unrouted while KiCad found 27 opens. Routed connections of a net form a forest, so the path back is unique; the T now depends on all of it. KiCad's opens and freeroute's count agree again (18 against 17, the extra one a GND pour island left to the fill).
This commit is contained in:
@@ -854,11 +854,14 @@ class GridRouter:
|
||||
)
|
||||
return None
|
||||
|
||||
def _tree_ends(self, conn: _Connection, group) -> dict[Node, int]:
|
||||
"""Copper its net has already routed on ``group``'s side of ``conn``,
|
||||
each node mapped to the connection that laid it: a route may start or
|
||||
end anywhere on it. Empty for plane nets, which keep their drops to
|
||||
the plane, and when tree routing is off."""
|
||||
def _tree_ends(self, conn: _Connection, group) -> dict[Node, frozenset[int]]:
|
||||
"""Copper its net has already routed on ``group``'s side of ``conn``: a
|
||||
route may start or end anywhere on it. Each node maps to what a route
|
||||
ending there relies on: the connection that laid the copper and every
|
||||
one between it and ``group`` (routed connections of a net form a
|
||||
forest, so that path is unique). Ripping any of them would cut the
|
||||
route off. Empty for plane nets, which keep their drops to the plane,
|
||||
and when tree routing is off."""
|
||||
if not self.tree or conn.net_no in self._plane_nets:
|
||||
return {}
|
||||
adjacent: dict[int, list[tuple[int, _Connection]]] = {}
|
||||
@@ -868,18 +871,21 @@ class GridRouter:
|
||||
a, b = id(c.a_group), id(c.b_group)
|
||||
adjacent.setdefault(a, []).append((b, c))
|
||||
adjacent.setdefault(b, []).append((a, c))
|
||||
owners: dict[Node, int] = {}
|
||||
seen = {id(group)}
|
||||
owners: dict[Node, frozenset[int]] = {}
|
||||
path_to: dict[int, frozenset[int]] = {id(group): frozenset()}
|
||||
stack = [id(group)]
|
||||
while stack:
|
||||
for other, c in adjacent.get(stack.pop(), ()):
|
||||
here = stack.pop()
|
||||
for other, c in adjacent.get(here, ()):
|
||||
if other in path_to:
|
||||
continue
|
||||
path = path_to[here] | {c.index}
|
||||
path_to[other] = path
|
||||
stack.append(other)
|
||||
for node in c.copper:
|
||||
# a walled cell's via moved into the pad: no copper there
|
||||
if node not in self._keepout and node not in self._via_only:
|
||||
owners.setdefault(node, c.index)
|
||||
if other not in seen:
|
||||
seen.add(other)
|
||||
stack.append(other)
|
||||
owners.setdefault(node, path)
|
||||
return owners
|
||||
|
||||
def _with_dependents(self, victims: list[_Connection], conns: list[_Connection]) -> list[_Connection]:
|
||||
@@ -1943,12 +1949,13 @@ def _first(items):
|
||||
|
||||
|
||||
def _anchors(path, starts, goals, a_tree, b_tree) -> frozenset[int]:
|
||||
"""The connections whose copper a found path starts or ends on."""
|
||||
found = set()
|
||||
"""What a found path relies on: the connections between the copper it
|
||||
starts or ends on and its own groups."""
|
||||
found: set[int] = set()
|
||||
if starts.get(path[0]) is None and path[0] in a_tree:
|
||||
found.add(a_tree[path[0]])
|
||||
found |= a_tree[path[0]]
|
||||
if goals.get(path[-1]) is None and path[-1] in b_tree:
|
||||
found.add(b_tree[path[-1]])
|
||||
found |= b_tree[path[-1]]
|
||||
return frozenset(found)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user