Add item hierarchy, BasicBoard, and DSN->board construction

Ports the data model of the board Item hierarchy (Item base, Pin,
ObstacleArea/ConductionArea, Via, Trace) and BasicBoard (layers, nets,
clearance, bounding box, items with query helpers), then build_board:
the load-bearing integration that constructs a BasicBoard from a parsed
DsnBoard.

build_board maps layers -> LayerStructure, resolution -> transform,
default clearance rule -> ClearanceMatrix, nets -> Nets plus a
(component,pin)->net map, padstacks x placement -> Pin items, and
keepouts -> ObstacleArea items. Rectangle pads become exact IntBoxes,
convex polygon pads exact Simplexes, circle pads their bounding box
(documented approximation); every pad is centred on its pin location so
it contains that location by construction. Trace/Via are router-produced
and lightweight here (an imported unrouted board has none).

Validated on a real KiCad export (kicad_routable.dsn): layer/net/pin
counts match the parsed DSN, every pin's pad shape contains its origin,
every pin reports a valid net, and pin locations lie in the board
bounding box. An oracle-gated test cross-checks that every net the
reference FreeRouting JAR routes exists on the constructed board.
This commit is contained in:
2026-07-12 08:29:43 -06:00
parent dd40aaba5d
commit 4220fe0cd6
5 changed files with 623 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
"""``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 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", "_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
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]:
return [it for it in self._items if isinstance(it, ObstacleArea)]
def get_traces(self) -> list[Trace]:
return [it for it in self._items if isinstance(it, Trace)]
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)