Ryan Malloy dd40aaba5d Add board data-model foundation: units, layers, transform, nets, clearance
Ports the foundational board classes: Unit (mil/inch/mm/um with
micrometer scaling), Layer/LayerStructure (the layer stack with name and
signal-layer lookups), CoordinateTransform (DSN<->board scaling by the
resolution), Net/Nets (connectivity keyed by (name, subnet) and a board-
unique net number), and ClearanceMatrix (class x class spacing with a
reserved null class and a default class).

The per-layer axis of ClearanceMatrix is simplified to a single value per
class pair (DSN default clearance rules are layer-independent for the
boards we build); the router phase can add the layer dimension.

Unit-tested: unit scaling/parsing, layer lookups, transform round-trip,
net registration/lookup, and clearance default/append/symmetry.
2026-07-12 08:29:32 -06:00

60 lines
1.6 KiB
Python

"""``Layer`` and ``LayerStructure`` — the board's layer stack.
Ports ``board/Layer.java`` and ``board/LayerStructure.java``. Layer index 0 is
the component (top) side; ``is_signal`` marks routable copper layers (as opposed
to power/ground planes).
"""
from __future__ import annotations
from dataclasses import dataclass
__all__ = ["Layer", "LayerStructure"]
@dataclass(frozen=True)
class Layer:
"""A single board layer."""
name: str
is_signal: bool = True
def __str__(self) -> str:
return self.name
class LayerStructure:
"""The ordered layer stack, with name/index lookups."""
__slots__ = ("arr",)
def __init__(self, layers: list[Layer]) -> None:
self.arr: list[Layer] = list(layers)
def __len__(self) -> int:
return len(self.arr)
def get_no(self, layer_or_name) -> int:
"""Index of a layer (by :class:`Layer` or by name), or -1."""
if isinstance(layer_or_name, Layer):
for i, layer in enumerate(self.arr):
if layer is layer_or_name or layer == layer_or_name:
return i
return -1
for i, layer in enumerate(self.arr):
if layer.name == layer_or_name:
return i
return -1
def signal_layer_count(self) -> int:
return sum(1 for layer in self.arr if layer.is_signal)
def get_signal_layer(self, no: int) -> Layer:
found = 0
for layer in self.arr:
if layer.is_signal:
if found == no:
return layer
found += 1
return self.arr[-1]