Files
rpmroute/src/freeroute/board/board.py
T
rsp2k 8a50bde531 Read the DSN's wiring as fixed copper of its nets
Tracks and vias already on the board were ignored, so other nets could
be routed straight through them (the flight controller's 82 GND
stitching vias and its guard tracks). They are now copper of their net:
other nets keep a pad's distance from them in the grid, the exact check
and the room engine, and they add no connections to route.
2026-09-28 21:06:35 -06:00

102 lines
3.8 KiB
Python

"""``BasicBoard`` — the board container.
Ports the data-model core of ``board/BasicBoard.java``: the layer stack, the
net list, the clearance matrix, the bounding box, and the list of items with
insertion and query helpers. The routing operations live in ``RoutingBoard``
(the router phase); this class is what DSN import produces and what the
board-model invariants check.
"""
from __future__ import annotations
from freeroute.geometry import IntBox
from .clearance import ClearanceMatrix
from .items import ConductionArea, Item, ObstacleArea, Pin, Trace, Via
from .layer import LayerStructure
from .net import Nets
__all__ = ["BasicBoard"]
class BasicBoard:
"""A board: layers, nets, clearances, a bounding box, and items."""
__slots__ = ("layer_structure", "nets", "clearance_matrix", "bounding_box", "outline", "_items")
def __init__(
self,
layer_structure: LayerStructure,
nets: Nets,
clearance_matrix: ClearanceMatrix,
bounding_box: IntBox,
) -> None:
self.layer_structure = layer_structure
self.nets = nets
self.clearance_matrix = clearance_matrix
self.bounding_box = bounding_box
#: the real board outline, when the DSN has a polygon boundary
self.outline = None
self._items: list[Item] = []
# --- layers -------------------------------------------------------------
def get_layer_count(self) -> int:
return len(self.layer_structure)
# --- items --------------------------------------------------------------
def insert(self, item: Item) -> Item:
self._items.append(item)
return item
def get_items(self) -> list[Item]:
return list(self._items)
def get_pins(self) -> list[Pin]:
return [it for it in self._items if isinstance(it, Pin)]
def get_obstacle_areas(self) -> list[ObstacleArea]:
"""Keepouts. Copper zones are not obstacles; see get_conduction_areas."""
return [
it
for it in self._items
if isinstance(it, ObstacleArea) and not isinstance(it, ConductionArea)
]
def get_conduction_areas(self) -> list[ConductionArea]:
return [it for it in self._items if isinstance(it, ConductionArea)]
def get_traces(self) -> list[Trace]:
return [it for it in self._items if isinstance(it, Trace)]
def get_fixed_copper(self) -> list[tuple[int, frozenset[int], IntBox]]:
"""Copper already routed on the board (the DSN's ``wiring``), as
``(net, layers, box)``. A track is its segments, its corners and the
round caps at its ends (as squares); a via is its largest pad."""
from freeroute.geometry.polyline import Polyline, PolylineShape, corner_box
out: list[tuple[int, frozenset[int], IntBox]] = []
for item in self._items:
if not item.fixed or not item.net_nos:
continue
net = item.net_nos[0]
if isinstance(item, Trace) and not item.is_empty():
layers = frozenset({item.layer})
for box in PolylineShape(Polyline(item.corners), item.half_width).tiles():
out.append((net, layers, box))
for end in (item.corners[0], item.corners[-1]):
out.append((net, layers, corner_box(end, item.half_width)))
elif isinstance(item, Via) and item.shape is not None:
out.append((net, frozenset(item.layers), item.shape.bounding_box()))
return out
def get_vias(self) -> list[Via]:
return [it for it in self._items if isinstance(it, Via)]
def get_connectable_items(self, net_no: int) -> list[Item]:
return [it for it in self._items if it.contains_net(net_no)]
def item_count(self) -> int:
return len(self._items)