Add shared orthogonal shove primitives module
Factor the exact router's shove geometry into route/shove.py: the conflicting-segment finder and displacement ladder are re-exported from exact_router (kept byte-unchanged so its shove count and geometry stay identical), and a room-oriented trace placement helper is added that returns the inserted tile owner ids for a journaled rollback.
This commit is contained in:
parent
faf2df42bc
commit
e67a11c833
124
src/freeroute/route/shove.py
Normal file
124
src/freeroute/route/shove.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
"""Shared orthogonal shove primitives for the exact and room routers.
|
||||||
|
|
||||||
|
The exact-geometry router (:mod:`freeroute.route.exact_router`) grew the first
|
||||||
|
shove implementation; the continuous room router reuses its geometry so both
|
||||||
|
tracks agree on how a blocking trace's conflicting segment is found, how the
|
||||||
|
perpendicular displacement ladder is generated, and how a reshaped trace is
|
||||||
|
verified before it is committed.
|
||||||
|
|
||||||
|
To keep the exact track byte-for-byte identical (its tests assert an exact shove
|
||||||
|
count and geometry), the two low-level geometry helpers still live in
|
||||||
|
``exact_router`` and are re-exported here rather than moved. This module adds the
|
||||||
|
room-specific placement helper, which returns the inserted tile owner ids: the
|
||||||
|
room engine mutates already-committed nets, so it needs those ids for a real
|
||||||
|
journaled rollback (the exact track's rollback is a no-op because it only ever
|
||||||
|
verifies a shoved trace in isolation).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Iterator
|
||||||
|
|
||||||
|
from freeroute.board.search_tree import ShapeSearchTree, TreeShape
|
||||||
|
from freeroute.geometry import IntBox, IntPoint, Polyline, PolylineShape
|
||||||
|
|
||||||
|
from .exact_router import _conflicting_segment as conflicting_segment
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"conflicting_segment",
|
||||||
|
"trace_tiles",
|
||||||
|
"shove_displacements",
|
||||||
|
"place_trace_owners",
|
||||||
|
"force_place_owners",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def trace_tiles(corners: list[IntPoint], half_width: int) -> list[IntBox]:
|
||||||
|
"""The copper tiles of the trace centreline ``corners`` at ``half_width``."""
|
||||||
|
return PolylineShape(Polyline(corners), half_width).tiles()
|
||||||
|
|
||||||
|
|
||||||
|
def shove_displacements(
|
||||||
|
corners: list[IntPoint],
|
||||||
|
seg_index: int,
|
||||||
|
horizontal: bool,
|
||||||
|
d_corners: list[IntPoint],
|
||||||
|
half_width: int,
|
||||||
|
clearance: int,
|
||||||
|
) -> list[tuple[int, int]]:
|
||||||
|
"""The perpendicular displacement ladder for shoving segment ``seg_index``.
|
||||||
|
|
||||||
|
Mirrors the exact router's ladder: displace the conflicting segment away
|
||||||
|
from the crossing net at ``d_corners`` (its straight copper) by just enough
|
||||||
|
to clear it (``2*half_width + clearance`` centre-to-centre), then step out in
|
||||||
|
``half_width + clearance`` increments. The "away" side is tried first (past
|
||||||
|
the far edge of the crossing net), then the near side, so the search is
|
||||||
|
deterministic and integer-only.
|
||||||
|
"""
|
||||||
|
d0, d1 = d_corners[0], d_corners[1]
|
||||||
|
margin = 2 * half_width + clearance
|
||||||
|
fine = half_width + clearance
|
||||||
|
ref = corners[seg_index]
|
||||||
|
out: list[tuple[int, int]] = []
|
||||||
|
if horizontal: # displace in y to clear the crossing net's y-span
|
||||||
|
lo, hi = min(d0.y, d1.y), max(d0.y, d1.y)
|
||||||
|
for extra in range(6):
|
||||||
|
out.append((0, (hi + margin) - ref.y + extra * fine))
|
||||||
|
out.append((0, (lo - margin) - ref.y - extra * fine))
|
||||||
|
else: # displace in x
|
||||||
|
lo, hi = min(d0.x, d1.x), max(d0.x, d1.x)
|
||||||
|
for extra in range(6):
|
||||||
|
out.append(((hi + margin) - ref.x + extra * fine, 0))
|
||||||
|
out.append(((lo - margin) - ref.x - extra * fine, 0))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def place_trace_owners(
|
||||||
|
net_no: int,
|
||||||
|
layer: int,
|
||||||
|
corners: list[IntPoint],
|
||||||
|
tree: ShapeSearchTree,
|
||||||
|
owner: Iterator[int],
|
||||||
|
half_width: int,
|
||||||
|
clearance: int,
|
||||||
|
outline: IntBox,
|
||||||
|
) -> list[int] | None:
|
||||||
|
"""Verify a trace's copper and insert it, returning the inserted owner ids.
|
||||||
|
|
||||||
|
The trace is accepted only if every tile stays inside ``outline`` and clears
|
||||||
|
every other net exactly (via ``tree.clearance_conflict``). On rejection
|
||||||
|
nothing is inserted and ``None`` is returned.
|
||||||
|
"""
|
||||||
|
boxes = PolylineShape(Polyline(corners), half_width).tiles()
|
||||||
|
for box in boxes:
|
||||||
|
if not outline.contains_box(box):
|
||||||
|
return None
|
||||||
|
if tree.clearance_conflict(box, net_no, layer, clearance):
|
||||||
|
return None
|
||||||
|
owners: list[int] = []
|
||||||
|
for box in boxes:
|
||||||
|
oid = next(owner)
|
||||||
|
tree.insert(TreeShape(oid, net_no, frozenset({layer}), box, routed=True))
|
||||||
|
owners.append(oid)
|
||||||
|
return owners
|
||||||
|
|
||||||
|
|
||||||
|
def force_place_owners(
|
||||||
|
net_no: int,
|
||||||
|
layer: int,
|
||||||
|
corners: list[IntPoint],
|
||||||
|
tree: ShapeSearchTree,
|
||||||
|
owner: Iterator[int],
|
||||||
|
half_width: int,
|
||||||
|
) -> list[int]:
|
||||||
|
"""Insert a trace's copper without any check, returning the owner ids.
|
||||||
|
|
||||||
|
Used only to restore an already-verified original trace during rollback: its
|
||||||
|
tiles were clean before the attempt, so no re-verification is needed.
|
||||||
|
"""
|
||||||
|
owners: list[int] = []
|
||||||
|
for box in PolylineShape(Polyline(corners), half_width).tiles():
|
||||||
|
oid = next(owner)
|
||||||
|
tree.insert(TreeShape(oid, net_no, frozenset({layer}), box, routed=True))
|
||||||
|
owners.append(oid)
|
||||||
|
return owners
|
||||||
Loading…
x
Reference in New Issue
Block a user