Honour KiCad's keepouts: keyhole rings, wire_keepout, per-tile rasterizing

On the TerraHawk rc4 power stage every engine misread the board's rule areas:

- A no-track ring along the board edge arrives as one keyhole polygon, and
  keepout polygons were wrapped in a single convex tile, so the ring became
  the whole board. Split keepout polygons into convex pieces instead.
- The grid engine rasterized an area by its overall bounding box, which for
  that ring is again the whole board; it routed nothing. Rasterize per tile.
- "Keep router off" rule areas arrive as wire_keepout, which the reader did
  not know, so tracks ran through them (172 DRC items_not_allowed). Read it
  at board and image level. For now it blocks vias too, which is stricter
  than KiCad and cannot create a violation.
- place_keepout only restricts part placement; stop treating it as copper.

The oracle now runs the JAR headless; it was opening FreeRouting's GUI on
the desktop for every oracle test.
This commit is contained in:
2026-09-27 06:30:43 -06:00
parent b26fdd96d5
commit 3ad5c983f5
6 changed files with 115 additions and 10 deletions
+9
View File
@@ -178,6 +178,11 @@ def _read_structure(node: SExp, board: DsnBoard) -> None:
area = read_area(it)
if area is not None:
board.keepouts.append(Keepout(area=area, kind="via_keepout"))
elif head == "wire_keepout":
# KiCad writes a "no tracks" rule area this way (vias allowed).
area = read_area(it)
if area is not None:
board.keepouts.append(Keepout(area=area, kind="wire_keepout"))
elif head == "place_keepout":
area = read_area(it)
if area is not None:
@@ -285,6 +290,10 @@ def _read_image(node: SExp) -> Image | None:
area = read_area(it)
if area is not None:
image.via_keepouts.append(area)
elif head == "wire_keepout":
area = read_area(it)
if area is not None:
image.wire_keepouts.append(area)
elif head == "place_keepout":
area = read_area(it)
if area is not None: